// PathEnclosingInterval returns the PackageInfo and ast.Node that // contain source interval [start, end), and all the node's ancestors // up to the AST root. It searches all ast.Files of all packages in prog. // exact is defined as for astutil.PathEnclosingInterval. // // The zero value is returned if not found. // func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) { for _, info := range prog.AllPackages { for _, f := range info.Files { if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) { continue } if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil { return info, path, exact } } } return nil, nil, false }
func TestPathEnclosingInterval_Paths(t *testing.T) { // For these tests, we check only the path of the enclosing // node, but not its complete text because it's often quite // large when !exact. tests := []struct { substr string // first occurrence of this string indicates interval path string // the pathToString(),exact of the expected path }{ {"// add", "[BlockStmt FuncDecl File],false"}, {"(x + y", "[ParenExpr AssignStmt BlockStmt FuncDecl File],false"}, {"x +", "[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"}, {"z := (x", "[AssignStmt BlockStmt FuncDecl File],false"}, {"func f", "[FuncDecl File],false"}, {"func f()", "[FuncDecl File],false"}, {" f()", "[FuncDecl File],false"}, {"() {}", "[FuncDecl File],false"}, {"// Hello", "[File],false"}, {" f", "[Ident FuncDecl File],true"}, {"func ", "[FuncDecl File],true"}, {"mai", "[Ident File],true"}, {"f() // NB", "[CallExpr ExprStmt BlockStmt FuncDecl File],true"}, } for _, test := range tests { f, start, end := findInterval(t, new(token.FileSet), input, test.substr) if f == nil { continue } path, exact := astutil.PathEnclosingInterval(f, start, end) if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path { t.Errorf("PathEnclosingInterval(%q): got %q, want %q", test.substr, got, test.path) continue } } }
func TestPathEnclosingInterval_Exact(t *testing.T) { // For the exact tests, we check that a substring is mapped to // the canonical string for the node it denotes. tests := []struct { substr string // first occurrence of this string indicates interval node string // complete text of expected containing node }{ {"package", input[11 : len(input)-1]}, {"\npack", input[11 : len(input)-1]}, {"main", "main"}, {"import", "import \"fmt\""}, {"\"fmt\"", "\"fmt\""}, {"\nfunc f() {}\n", "func f() {}"}, {"x ", "x"}, {" y", "y"}, {"z", "z"}, {" + ", "x + y"}, {" :=", "z := (x + y)"}, {"x + y", "x + y"}, {"(x + y)", "(x + y)"}, {" (x + y) ", "(x + y)"}, {" (x + y) // add", "(x + y)"}, {"func", "func f() {}"}, {"func f() {}", "func f() {}"}, {"\nfun", "func f() {}"}, {" f", "f"}, } for _, test := range tests { f, start, end := findInterval(t, new(token.FileSet), input, test.substr) if f == nil { continue } path, exact := astutil.PathEnclosingInterval(f, start, end) if !exact { t.Errorf("PathEnclosingInterval(%q) not exact", test.substr) continue } if len(path) == 0 { if test.node != "" { t.Errorf("PathEnclosingInterval(%q).path: got [], want %q", test.substr, test.node) } continue } if got := input[path[0].Pos():path[0].End()]; got != test.node { t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)", test.substr, got, test.node, pathToString(path)) continue } } }
func TestEnclosingFunction(t *testing.T) { tests := []struct { input string // the input file substr string // first occurrence of this string denotes interval fn string // name of expected containing function }{ // We use distinctive numbers as syntactic landmarks. // Ordinary function: {`package main func f() { println(1003) }`, "100", "main.f"}, // Methods: {`package main type T int func (t T) f() { println(200) }`, "200", "(main.T).f"}, // Function literal: {`package main func f() { println(func() { print(300) }) }`, "300", "main.f$1"}, // Doubly nested {`package main func f() { println(func() { print(func() { print(350) })})}`, "350", "main.f$1$1"}, // Implicit init for package-level var initializer. {"package main; var a = 400", "400", "main.init"}, // No code for constants: {"package main; const a = 500", "500", "(none)"}, // Explicit init() {"package main; func init() { println(600) }", "600", "main.init#1"}, // Multiple explicit init functions: {`package main func init() { println("foo") } func init() { println(800) }`, "800", "main.init#2"}, // init() containing FuncLit. {`package main func init() { println(func(){print(900)}) }`, "900", "main.init#1$1"}, } for _, test := range tests { conf := loader.Config{Fset: token.NewFileSet()} f, start, end := findInterval(t, conf.Fset, test.input, test.substr) if f == nil { continue } path, exact := astutil.PathEnclosingInterval(f, start, end) if !exact { t.Errorf("EnclosingFunction(%q) not exact", test.substr) continue } conf.CreateFromFiles("main", f) iprog, err := conf.Load() if err != nil { t.Error(err) continue } prog := ssa.Create(iprog, 0) pkg := prog.Package(iprog.Created[0].Pkg) pkg.Build() name := "(none)" fn := ssa.EnclosingFunction(pkg, path) if fn != nil { name = fn.String() } if name != test.fn { t.Errorf("EnclosingFunction(%q in %q) got %s, want %s", test.substr, test.input, name, test.fn) continue } // While we're here: test HasEnclosingFunction. if has := ssa.HasEnclosingFunction(pkg, path); has != (fn != nil) { t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v", test.substr, test.input, has, fn != nil) continue } } }