mirror of
https://github.com/golang/go
synced 2024-11-15 06:30:32 -07:00
[release-branch.go1] go/ast: ast.Print must not crash with unexported fields
««« backport d134e30c4d29 go/ast: ast.Print must not crash with unexported fields Don't print unexported struct fields; their values are not accessible via reflection. Fixes #3898. Also: - added support for arrays - print empty maps, arrays, slices, structs on one line for a denser output - added respective test cases R=r CC=golang-dev https://golang.org/cl/6454089 »»»
This commit is contained in:
parent
6425aa28f2
commit
dda61d68c8
@ -34,7 +34,8 @@ func NotNilFilter(_ string, v reflect.Value) bool {
|
|||||||
//
|
//
|
||||||
// A non-nil FieldFilter f may be provided to control the output:
|
// A non-nil FieldFilter f may be provided to control the output:
|
||||||
// struct fields for which f(fieldname, fieldvalue) is true are
|
// struct fields for which f(fieldname, fieldvalue) is true are
|
||||||
// are printed; all others are filtered from the output.
|
// are printed; all others are filtered from the output. Unexported
|
||||||
|
// struct fields are never printed.
|
||||||
//
|
//
|
||||||
func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
|
func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
|
||||||
// setup printer
|
// setup printer
|
||||||
@ -145,15 +146,18 @@ func (p *printer) print(x reflect.Value) {
|
|||||||
p.print(x.Elem())
|
p.print(x.Elem())
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
p.printf("%s (len = %d) {\n", x.Type(), x.Len())
|
p.printf("%s (len = %d) {", x.Type(), x.Len())
|
||||||
p.indent++
|
if x.Len() > 0 {
|
||||||
for _, key := range x.MapKeys() {
|
p.indent++
|
||||||
p.print(key)
|
|
||||||
p.printf(": ")
|
|
||||||
p.print(x.MapIndex(key))
|
|
||||||
p.printf("\n")
|
p.printf("\n")
|
||||||
|
for _, key := range x.MapKeys() {
|
||||||
|
p.print(key)
|
||||||
|
p.printf(": ")
|
||||||
|
p.print(x.MapIndex(key))
|
||||||
|
p.printf("\n")
|
||||||
|
}
|
||||||
|
p.indent--
|
||||||
}
|
}
|
||||||
p.indent--
|
|
||||||
p.printf("}")
|
p.printf("}")
|
||||||
|
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
@ -169,32 +173,57 @@ func (p *printer) print(x reflect.Value) {
|
|||||||
p.print(x.Elem())
|
p.print(x.Elem())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case reflect.Array:
|
||||||
|
p.printf("%s {", x.Type())
|
||||||
|
if x.Len() > 0 {
|
||||||
|
p.indent++
|
||||||
|
p.printf("\n")
|
||||||
|
for i, n := 0, x.Len(); i < n; i++ {
|
||||||
|
p.printf("%d: ", i)
|
||||||
|
p.print(x.Index(i))
|
||||||
|
p.printf("\n")
|
||||||
|
}
|
||||||
|
p.indent--
|
||||||
|
}
|
||||||
|
p.printf("}")
|
||||||
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if s, ok := x.Interface().([]byte); ok {
|
if s, ok := x.Interface().([]byte); ok {
|
||||||
p.printf("%#q", s)
|
p.printf("%#q", s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.printf("%s (len = %d) {\n", x.Type(), x.Len())
|
p.printf("%s (len = %d) {", x.Type(), x.Len())
|
||||||
p.indent++
|
if x.Len() > 0 {
|
||||||
for i, n := 0, x.Len(); i < n; i++ {
|
p.indent++
|
||||||
p.printf("%d: ", i)
|
|
||||||
p.print(x.Index(i))
|
|
||||||
p.printf("\n")
|
p.printf("\n")
|
||||||
|
for i, n := 0, x.Len(); i < n; i++ {
|
||||||
|
p.printf("%d: ", i)
|
||||||
|
p.print(x.Index(i))
|
||||||
|
p.printf("\n")
|
||||||
|
}
|
||||||
|
p.indent--
|
||||||
}
|
}
|
||||||
p.indent--
|
|
||||||
p.printf("}")
|
p.printf("}")
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
p.printf("%s {\n", x.Type())
|
|
||||||
p.indent++
|
|
||||||
t := x.Type()
|
t := x.Type()
|
||||||
|
p.printf("%s {", t)
|
||||||
|
p.indent++
|
||||||
|
first := true
|
||||||
for i, n := 0, t.NumField(); i < n; i++ {
|
for i, n := 0, t.NumField(); i < n; i++ {
|
||||||
name := t.Field(i).Name
|
// exclude non-exported fields because their
|
||||||
value := x.Field(i)
|
// values cannot be accessed via reflection
|
||||||
if p.filter == nil || p.filter(name, value) {
|
if name := t.Field(i).Name; IsExported(name) {
|
||||||
p.printf("%s: ", name)
|
value := x.Field(i)
|
||||||
p.print(value)
|
if p.filter == nil || p.filter(name, value) {
|
||||||
p.printf("\n")
|
if first {
|
||||||
|
p.printf("\n")
|
||||||
|
first = false
|
||||||
|
}
|
||||||
|
p.printf("%s: ", name)
|
||||||
|
p.print(value)
|
||||||
|
p.printf("\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.indent--
|
p.indent--
|
||||||
|
@ -23,6 +23,7 @@ var tests = []struct {
|
|||||||
{"foobar", "0 \"foobar\""},
|
{"foobar", "0 \"foobar\""},
|
||||||
|
|
||||||
// maps
|
// maps
|
||||||
|
{map[Expr]string{}, `0 map[ast.Expr]string (len = 0) {}`},
|
||||||
{map[string]int{"a": 1},
|
{map[string]int{"a": 1},
|
||||||
`0 map[string]int (len = 1) {
|
`0 map[string]int (len = 1) {
|
||||||
1 . "a": 1
|
1 . "a": 1
|
||||||
@ -31,7 +32,21 @@ var tests = []struct {
|
|||||||
// pointers
|
// pointers
|
||||||
{new(int), "0 *0"},
|
{new(int), "0 *0"},
|
||||||
|
|
||||||
|
// arrays
|
||||||
|
{[0]int{}, `0 [0]int {}`},
|
||||||
|
{[3]int{1, 2, 3},
|
||||||
|
`0 [3]int {
|
||||||
|
1 . 0: 1
|
||||||
|
2 . 1: 2
|
||||||
|
3 . 2: 3
|
||||||
|
4 }`},
|
||||||
|
{[...]int{42},
|
||||||
|
`0 [1]int {
|
||||||
|
1 . 0: 42
|
||||||
|
2 }`},
|
||||||
|
|
||||||
// slices
|
// slices
|
||||||
|
{[]int{}, `0 []int (len = 0) {}`},
|
||||||
{[]int{1, 2, 3},
|
{[]int{1, 2, 3},
|
||||||
`0 []int (len = 3) {
|
`0 []int (len = 3) {
|
||||||
1 . 0: 1
|
1 . 0: 1
|
||||||
@ -40,6 +55,12 @@ var tests = []struct {
|
|||||||
4 }`},
|
4 }`},
|
||||||
|
|
||||||
// structs
|
// structs
|
||||||
|
{struct{}{}, `0 struct {} {}`},
|
||||||
|
{struct{ x int }{007}, `0 struct { x int } {}`},
|
||||||
|
{struct{ X, y int }{42, 991},
|
||||||
|
`0 struct { X int; y int } {
|
||||||
|
1 . X: 42
|
||||||
|
2 }`},
|
||||||
{struct{ X, Y int }{42, 991},
|
{struct{ X, Y int }{42, 991},
|
||||||
`0 struct { X int; Y int } {
|
`0 struct { X int; Y int } {
|
||||||
1 . X: 42
|
1 . X: 42
|
||||||
|
Loading…
Reference in New Issue
Block a user