14 "golang.org/x/tools/go/ast/inspector"
17 var netFiles []*ast.File
20 files, err := parseNetFiles()
27 func parseNetFiles() ([]*ast.File, error) {
28 pkg, err := build.Default.Import("net", "", 0)
32 fset := token.NewFileSet()
34 for _, filename := range pkg.GoFiles {
35 filename = filepath.Join(pkg.Dir, filename)
36 f, err := parser.ParseFile(fset, filename, nil, 0)
40 files = append(files, f)
45 // TestAllNodes compares Inspector against ast.Inspect.
46 func TestInspectAllNodes(t *testing.T) {
47 inspect := inspector.New(netFiles)
50 inspect.Nodes(nil, func(n ast.Node, push bool) bool {
52 nodesA = append(nodesA, n)
57 for _, f := range netFiles {
58 ast.Inspect(f, func(n ast.Node) bool {
60 nodesB = append(nodesB, n)
65 compare(t, nodesA, nodesB)
68 // TestPruning compares Inspector against ast.Inspect,
69 // pruning descent within ast.CallExpr nodes.
70 func TestInspectPruning(t *testing.T) {
71 inspect := inspector.New(netFiles)
74 inspect.Nodes(nil, func(n ast.Node, push bool) bool {
76 nodesA = append(nodesA, n)
77 _, isCall := n.(*ast.CallExpr)
78 return !isCall // don't descend into function calls
83 for _, f := range netFiles {
84 ast.Inspect(f, func(n ast.Node) bool {
86 nodesB = append(nodesB, n)
87 _, isCall := n.(*ast.CallExpr)
88 return !isCall // don't descend into function calls
93 compare(t, nodesA, nodesB)
96 func compare(t *testing.T, nodesA, nodesB []ast.Node) {
97 if len(nodesA) != len(nodesB) {
98 t.Errorf("inconsistent node lists: %d vs %d", len(nodesA), len(nodesB))
100 for i := range nodesA {
101 if a, b := nodesA[i], nodesB[i]; a != b {
102 t.Errorf("node %d is inconsistent: %T, %T", i, a, b)
108 func TestTypeFiltering(t *testing.T) {
109 const src = `package a
115 fset := token.NewFileSet()
116 f, _ := parser.ParseFile(fset, "a.go", src, 0)
117 inspect := inspector.New([]*ast.File{f})
120 fn := func(n ast.Node, push bool) bool {
122 got = append(got, typeOf(n))
128 inspect.Nodes(nil, fn)
129 if want := strings.Fields("File Ident FuncDecl Ident FuncType FieldList BlockStmt ExprStmt CallExpr Ident BasicLit ExprStmt CallExpr Ident BasicLit"); !reflect.DeepEqual(got, want) {
130 t.Errorf("inspect: got %s, want %s", got, want)
134 nodeTypes := []ast.Node{
135 (*ast.BasicLit)(nil),
136 (*ast.CallExpr)(nil),
139 inspect.Nodes(nodeTypes, fn)
140 if want := strings.Fields("CallExpr BasicLit CallExpr BasicLit"); !reflect.DeepEqual(got, want) {
141 t.Errorf("inspect: got %s, want %s", got, want)
144 // inspect with stack
146 inspect.WithStack(nodeTypes, func(n ast.Node, push bool, stack []ast.Node) bool {
149 for _, n := range stack {
150 line = append(line, typeOf(n))
152 got = append(got, strings.Join(line, " "))
157 "File FuncDecl BlockStmt ExprStmt CallExpr",
158 "File FuncDecl BlockStmt ExprStmt CallExpr BasicLit",
159 "File FuncDecl BlockStmt ExprStmt CallExpr",
160 "File FuncDecl BlockStmt ExprStmt CallExpr BasicLit",
162 if !reflect.DeepEqual(got, want) {
163 t.Errorf("inspect: got %s, want %s", got, want)
167 func typeOf(n ast.Node) string {
168 return strings.TrimPrefix(reflect.TypeOf(n).String(), "*ast.")
171 // The numbers show a marginal improvement (ASTInspect/Inspect) of 3.5x,
172 // but a break-even point (NewInspector/(ASTInspect-Inspect)) of about 5
175 // BenchmarkNewInspector 4.5 ms
176 // BenchmarkNewInspect 0.33ms
177 // BenchmarkASTInspect 1.2 ms
179 func BenchmarkNewInspector(b *testing.B) {
180 // Measure one-time construction overhead.
181 for i := 0; i < b.N; i++ {
182 inspector.New(netFiles)
186 func BenchmarkInspect(b *testing.B) {
188 inspect := inspector.New(netFiles)
191 // Measure marginal cost of traversal.
192 var ndecls, nlits int
193 for i := 0; i < b.N; i++ {
194 inspect.Preorder(nil, func(n ast.Node) {
205 func BenchmarkASTInspect(b *testing.B) {
206 var ndecls, nlits int
207 for i := 0; i < b.N; i++ {
208 for _, f := range netFiles {
209 ast.Inspect(f, func(n ast.Node) bool {