+++ /dev/null
-package inspector_test
-
-import (
- "go/ast"
- "go/build"
- "go/parser"
- "go/token"
- "log"
- "path/filepath"
- "reflect"
- "strings"
- "testing"
-
- "golang.org/x/tools/go/ast/inspector"
-)
-
-var netFiles []*ast.File
-
-func init() {
- files, err := parseNetFiles()
- if err != nil {
- log.Fatal(err)
- }
- netFiles = files
-}
-
-func parseNetFiles() ([]*ast.File, error) {
- pkg, err := build.Default.Import("net", "", 0)
- if err != nil {
- return nil, err
- }
- fset := token.NewFileSet()
- var files []*ast.File
- for _, filename := range pkg.GoFiles {
- filename = filepath.Join(pkg.Dir, filename)
- f, err := parser.ParseFile(fset, filename, nil, 0)
- if err != nil {
- return nil, err
- }
- files = append(files, f)
- }
- return files, nil
-}
-
-// TestAllNodes compares Inspector against ast.Inspect.
-func TestInspectAllNodes(t *testing.T) {
- inspect := inspector.New(netFiles)
-
- var nodesA []ast.Node
- inspect.Nodes(nil, func(n ast.Node, push bool) bool {
- if push {
- nodesA = append(nodesA, n)
- }
- return true
- })
- var nodesB []ast.Node
- for _, f := range netFiles {
- ast.Inspect(f, func(n ast.Node) bool {
- if n != nil {
- nodesB = append(nodesB, n)
- }
- return true
- })
- }
- compare(t, nodesA, nodesB)
-}
-
-// TestPruning compares Inspector against ast.Inspect,
-// pruning descent within ast.CallExpr nodes.
-func TestInspectPruning(t *testing.T) {
- inspect := inspector.New(netFiles)
-
- var nodesA []ast.Node
- inspect.Nodes(nil, func(n ast.Node, push bool) bool {
- if push {
- nodesA = append(nodesA, n)
- _, isCall := n.(*ast.CallExpr)
- return !isCall // don't descend into function calls
- }
- return false
- })
- var nodesB []ast.Node
- for _, f := range netFiles {
- ast.Inspect(f, func(n ast.Node) bool {
- if n != nil {
- nodesB = append(nodesB, n)
- _, isCall := n.(*ast.CallExpr)
- return !isCall // don't descend into function calls
- }
- return false
- })
- }
- compare(t, nodesA, nodesB)
-}
-
-func compare(t *testing.T, nodesA, nodesB []ast.Node) {
- if len(nodesA) != len(nodesB) {
- t.Errorf("inconsistent node lists: %d vs %d", len(nodesA), len(nodesB))
- } else {
- for i := range nodesA {
- if a, b := nodesA[i], nodesB[i]; a != b {
- t.Errorf("node %d is inconsistent: %T, %T", i, a, b)
- }
- }
- }
-}
-
-func TestTypeFiltering(t *testing.T) {
- const src = `package a
-func f() {
- print("hi")
- panic("oops")
-}
-`
- fset := token.NewFileSet()
- f, _ := parser.ParseFile(fset, "a.go", src, 0)
- inspect := inspector.New([]*ast.File{f})
-
- var got []string
- fn := func(n ast.Node, push bool) bool {
- if push {
- got = append(got, typeOf(n))
- }
- return true
- }
-
- // no type filtering
- inspect.Nodes(nil, fn)
- if want := strings.Fields("File Ident FuncDecl Ident FuncType FieldList BlockStmt ExprStmt CallExpr Ident BasicLit ExprStmt CallExpr Ident BasicLit"); !reflect.DeepEqual(got, want) {
- t.Errorf("inspect: got %s, want %s", got, want)
- }
-
- // type filtering
- nodeTypes := []ast.Node{
- (*ast.BasicLit)(nil),
- (*ast.CallExpr)(nil),
- }
- got = nil
- inspect.Nodes(nodeTypes, fn)
- if want := strings.Fields("CallExpr BasicLit CallExpr BasicLit"); !reflect.DeepEqual(got, want) {
- t.Errorf("inspect: got %s, want %s", got, want)
- }
-
- // inspect with stack
- got = nil
- inspect.WithStack(nodeTypes, func(n ast.Node, push bool, stack []ast.Node) bool {
- if push {
- var line []string
- for _, n := range stack {
- line = append(line, typeOf(n))
- }
- got = append(got, strings.Join(line, " "))
- }
- return true
- })
- want := []string{
- "File FuncDecl BlockStmt ExprStmt CallExpr",
- "File FuncDecl BlockStmt ExprStmt CallExpr BasicLit",
- "File FuncDecl BlockStmt ExprStmt CallExpr",
- "File FuncDecl BlockStmt ExprStmt CallExpr BasicLit",
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("inspect: got %s, want %s", got, want)
- }
-}
-
-func typeOf(n ast.Node) string {
- return strings.TrimPrefix(reflect.TypeOf(n).String(), "*ast.")
-}
-
-// The numbers show a marginal improvement (ASTInspect/Inspect) of 3.5x,
-// but a break-even point (NewInspector/(ASTInspect-Inspect)) of about 5
-// traversals.
-//
-// BenchmarkNewInspector 4.5 ms
-// BenchmarkNewInspect 0.33ms
-// BenchmarkASTInspect 1.2 ms
-
-func BenchmarkNewInspector(b *testing.B) {
- // Measure one-time construction overhead.
- for i := 0; i < b.N; i++ {
- inspector.New(netFiles)
- }
-}
-
-func BenchmarkInspect(b *testing.B) {
- b.StopTimer()
- inspect := inspector.New(netFiles)
- b.StartTimer()
-
- // Measure marginal cost of traversal.
- var ndecls, nlits int
- for i := 0; i < b.N; i++ {
- inspect.Preorder(nil, func(n ast.Node) {
- switch n.(type) {
- case *ast.FuncDecl:
- ndecls++
- case *ast.FuncLit:
- nlits++
- }
- })
- }
-}
-
-func BenchmarkASTInspect(b *testing.B) {
- var ndecls, nlits int
- for i := 0; i < b.N; i++ {
- for _, f := range netFiles {
- ast.Inspect(f, func(n ast.Node) bool {
- switch n.(type) {
- case *ast.FuncDecl:
- ndecls++
- case *ast.FuncLit:
- nlits++
- }
- return true
- })
- }
- }
-}