case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return !v.IsNil()
}
- return false
+ return true
}
output io.Writer
fset *token.FileSet
filter FieldFilter
- ptrmap map[interface{}]int // *reflect.PtrValue -> line number
+ ptrmap map[interface{}]int // *T -> line number
written int // number of bytes written to output
indent int // current indentation level
last byte // the last byte processed by Write
// Implementation note: Print is written for AST nodes but could be
// used to print arbitrary data structures; such a version should
// probably be in a different package.
+//
+// Note: This code detects (some) cycles created via pointers but
+// not cycles that are created via slices or maps containing the
+// same slice or map. Code for general data structures probably
+// should catch those as well.
func (p *printer) print(x reflect.Value) {
if !NotNilFilter("", x) {
return
}
- switch v := x; v.Kind() {
+ switch x.Kind() {
case reflect.Interface:
- p.print(v.Elem())
+ p.print(x.Elem())
case reflect.Map:
- p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
+ p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
p.indent++
- for _, key := range v.MapKeys() {
+ for _, key := range x.MapKeys() {
p.print(key)
p.printf(": ")
- p.print(v.MapIndex(key))
+ p.print(x.MapIndex(key))
p.printf("\n")
}
p.indent--
// type-checked ASTs may contain cycles - use ptrmap
// to keep track of objects that have been printed
// already and print the respective line number instead
- ptr := v.Interface()
+ ptr := x.Interface()
if line, exists := p.ptrmap[ptr]; exists {
p.printf("(obj @ %d)", line)
} else {
p.ptrmap[ptr] = p.line
- p.print(v.Elem())
+ p.print(x.Elem())
}
case reflect.Slice:
- if s, ok := v.Interface().([]byte); ok {
+ if s, ok := x.Interface().([]byte); ok {
p.printf("%#q", s)
return
}
- p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
+ p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
p.indent++
- for i, n := 0, v.Len(); i < n; i++ {
+ for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
- p.print(v.Index(i))
+ p.print(x.Index(i))
p.printf("\n")
}
p.indent--
case reflect.Struct:
p.printf("%s {\n", x.Type().String())
p.indent++
- t := v.Type()
+ t := x.Type()
for i, n := 0, t.NumField(); i < n; i++ {
name := t.Field(i).Name
- value := v.Field(i)
+ value := x.Field(i)
if p.filter == nil || p.filter(name, value) {
p.printf("%s: ", name)
p.print(value)
p.printf("}")
default:
- value := x.Interface()
- // position values can be printed nicely if we have a file set
- if pos, ok := value.(token.Pos); ok && p.fset != nil {
- value = p.fset.Position(pos)
+ v := x.Interface()
+ switch v := v.(type) {
+ case string:
+ // print strings in quotes
+ p.printf("%q", v)
+ return
+ case token.Pos:
+ // position values can be printed nicely if we have a file set
+ if p.fset != nil {
+ p.printf("%s", p.fset.Position(v))
+ return
+ }
}
- p.printf("%v", value)
+ // default
+ p.printf("%v", v)
}
}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ast
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+
+var tests = []struct {
+ x interface{} // x is printed as s
+ s string
+}{
+ // basic types
+ {nil, "0 nil"},
+ {true, "0 true"},
+ {42, "0 42"},
+ {3.14, "0 3.14"},
+ {1 + 2.718i, "0 (1+2.718i)"},
+ {"foobar", "0 \"foobar\""},
+
+ // maps
+ {map[string]int{"a": 1, "b": 2},
+ `0 map[string] int (len = 2) {
+ 1 . "a": 1
+ 2 . "b": 2
+ 3 }`},
+
+ // pointers
+ {new(int), "0 *0"},
+
+ // slices
+ {[]int{1, 2, 3},
+ `0 []int (len = 3) {
+ 1 . 0: 1
+ 2 . 1: 2
+ 3 . 2: 3
+ 4 }`},
+
+ // structs
+ {struct{ x, y int }{42, 991},
+ `0 struct { x int; y int } {
+ 1 . x: 42
+ 2 . y: 991
+ 3 }`},
+}
+
+
+// Split s into lines, trim whitespace from all lines, and return
+// the concatenated non-empty lines.
+func trim(s string) string {
+ lines := strings.Split(s, "\n", -1)
+ i := 0
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line != "" {
+ lines[i] = line
+ i++
+ }
+ }
+ return strings.Join(lines[0:i], "\n")
+}
+
+
+func TestPrint(t *testing.T) {
+ var buf bytes.Buffer
+ for _, test := range tests {
+ buf.Reset()
+ if _, err := Fprint(&buf, nil, test.x, nil); err != nil {
+ t.Errorf("Fprint failed: %s", err)
+ }
+ if s, ts := trim(buf.String()), trim(test.s); s != ts {
+ t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts)
+ }
+ }
+}