1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 "golang.org/x/tools/go/ast/inspector"
21 var netFiles []*ast.File
24 files, err := parseNetFiles()
31 func parseNetFiles() ([]*ast.File, error) {
32 pkg, err := build.Default.Import("net", "", 0)
36 fset := token.NewFileSet()
38 for _, filename := range pkg.GoFiles {
39 filename = filepath.Join(pkg.Dir, filename)
40 f, err := parser.ParseFile(fset, filename, nil, 0)
44 files = append(files, f)
49 // TestAllNodes compares Inspector against ast.Inspect.
50 func TestInspectAllNodes(t *testing.T) {
51 inspect := inspector.New(netFiles)
54 inspect.Nodes(nil, func(n ast.Node, push bool) bool {
56 nodesA = append(nodesA, n)
61 for _, f := range netFiles {
62 ast.Inspect(f, func(n ast.Node) bool {
64 nodesB = append(nodesB, n)
69 compare(t, nodesA, nodesB)
72 // TestPruning compares Inspector against ast.Inspect,
73 // pruning descent within ast.CallExpr nodes.
74 func TestInspectPruning(t *testing.T) {
75 inspect := inspector.New(netFiles)
78 inspect.Nodes(nil, func(n ast.Node, push bool) bool {
80 nodesA = append(nodesA, n)
81 _, isCall := n.(*ast.CallExpr)
82 return !isCall // don't descend into function calls
87 for _, f := range netFiles {
88 ast.Inspect(f, func(n ast.Node) bool {
90 nodesB = append(nodesB, n)
91 _, isCall := n.(*ast.CallExpr)
92 return !isCall // don't descend into function calls
97 compare(t, nodesA, nodesB)
100 func compare(t *testing.T, nodesA, nodesB []ast.Node) {
101 if len(nodesA) != len(nodesB) {
102 t.Errorf("inconsistent node lists: %d vs %d", len(nodesA), len(nodesB))
104 for i := range nodesA {
105 if a, b := nodesA[i], nodesB[i]; a != b {
106 t.Errorf("node %d is inconsistent: %T, %T", i, a, b)
112 func TestTypeFiltering(t *testing.T) {
113 const src = `package a
119 fset := token.NewFileSet()
120 f, _ := parser.ParseFile(fset, "a.go", src, 0)
121 inspect := inspector.New([]*ast.File{f})
124 fn := func(n ast.Node, push bool) bool {
126 got = append(got, typeOf(n))
132 inspect.Nodes(nil, fn)
133 if want := strings.Fields("File Ident FuncDecl Ident FuncType FieldList BlockStmt ExprStmt CallExpr Ident BasicLit ExprStmt CallExpr Ident BasicLit"); !reflect.DeepEqual(got, want) {
134 t.Errorf("inspect: got %s, want %s", got, want)
138 nodeTypes := []ast.Node{
139 (*ast.BasicLit)(nil),
140 (*ast.CallExpr)(nil),
143 inspect.Nodes(nodeTypes, fn)
144 if want := strings.Fields("CallExpr BasicLit CallExpr BasicLit"); !reflect.DeepEqual(got, want) {
145 t.Errorf("inspect: got %s, want %s", got, want)
148 // inspect with stack
150 inspect.WithStack(nodeTypes, func(n ast.Node, push bool, stack []ast.Node) bool {
153 for _, n := range stack {
154 line = append(line, typeOf(n))
156 got = append(got, strings.Join(line, " "))
161 "File FuncDecl BlockStmt ExprStmt CallExpr",
162 "File FuncDecl BlockStmt ExprStmt CallExpr BasicLit",
163 "File FuncDecl BlockStmt ExprStmt CallExpr",
164 "File FuncDecl BlockStmt ExprStmt CallExpr BasicLit",
166 if !reflect.DeepEqual(got, want) {
167 t.Errorf("inspect: got %s, want %s", got, want)
171 func typeOf(n ast.Node) string {
172 return strings.TrimPrefix(reflect.TypeOf(n).String(), "*ast.")
175 // The numbers show a marginal improvement (ASTInspect/Inspect) of 3.5x,
176 // but a break-even point (NewInspector/(ASTInspect-Inspect)) of about 5
179 // BenchmarkNewInspector 4.5 ms
180 // BenchmarkNewInspect 0.33ms
181 // BenchmarkASTInspect 1.2 ms
183 func BenchmarkNewInspector(b *testing.B) {
184 // Measure one-time construction overhead.
185 for i := 0; i < b.N; i++ {
186 inspector.New(netFiles)
190 func BenchmarkInspect(b *testing.B) {
192 inspect := inspector.New(netFiles)
195 // Measure marginal cost of traversal.
196 var ndecls, nlits int
197 for i := 0; i < b.N; i++ {
198 inspect.Preorder(nil, func(n ast.Node) {
209 func BenchmarkASTInspect(b *testing.B) {
210 var ndecls, nlits int
211 for i := 0; i < b.N; i++ {
212 for _, f := range netFiles {
213 ast.Inspect(f, func(n ast.Node) bool {