// findIdentifier looks for an identifier at byte-offset searchpos // inside the parsed source represented by node. // If it is part of a selector expression, it returns // that expression rather than the identifier itself. // // As a special case, if it finds an import // spec, it returns ImportSpec. // func findIdentifier(f *ast.File, searchpos int) ast.Node { ec := make(chan ast.Node) found := func(startPos, endPos token.Pos) bool { start := types.FileSet.Position(startPos).Offset end := start + int(endPos-startPos) return start <= searchpos && searchpos <= end } go func() { var visit func(ast.Node) bool visit = func(n ast.Node) bool { var startPos token.Pos switch n := n.(type) { default: return true case *ast.Ident: startPos = n.NamePos case *ast.SelectorExpr: startPos = n.Sel.NamePos case *ast.ImportSpec: startPos = n.Pos() case *ast.StructType: // If we find an anonymous bare field in a // struct type, its definition points to itself, // but we actually want to go elsewhere, // so assume (dubiously) that the expression // works globally and return a new node for it. for _, field := range n.Fields.List { if field.Names != nil { continue } t := field.Type if pt, ok := field.Type.(*ast.StarExpr); ok { t = pt.X } if id, ok := t.(*ast.Ident); ok { if found(id.NamePos, id.End()) { ec <- parseExpr(f.Scope, id.Name) runtime.Goexit() } } } return true } if found(startPos, n.End()) { ec <- n runtime.Goexit() } return true } ast.Walk(FVisitor(visit), f) ec <- nil }() ev := <-ec if ev == nil { fail("no identifier found") } return ev }
// constainsNode returns true if x is found somewhere // inside node. func containsNode(node, x ast.Node) (found bool) { ast.Walk(funcVisitor(func(n ast.Node) bool { if !found { found = n == x } return !found }), node) return }
func (v identVisitor) Visit(n ast.Node) ast.Visitor { switch n := n.(type) { case *ast.Ident: if strings.HasPrefix(n.Name, prefix) { v <- n } return nil case *ast.SelectorExpr: ast.Walk(v, n.X) if strings.HasPrefix(n.Sel.Name, prefix) { v <- n } return nil } return v }
func TestOneFile(t *testing.T) { code, offsetMap := translateSymbols(testCode) //fmt.Printf("------------------- {%s}\n", code) f, err := parser.ParseFile(FileSet, "xx.go", code, 0, ast.NewScope(parser.Universe)) if err != nil { t.Fatalf("parse failed: %v", err) } v := make(identVisitor) go func() { ast.Walk(v, f) close(v) }() for e := range v { testExpr(t, FileSet, e, offsetMap) } }
// IterateSyms calls visitf for each identifier in the given file. If // visitf returns false, the iteration stops. If visitf changes // info.Ident.Name, the file is added to ctxt.ChangedFiles. func (ctxt *Context) IterateSyms(f *ast.File, visitf func(info *Info) bool) { var visit astVisitor ok := true local := false // TODO set to true inside function body visit = func(n ast.Node) bool { if !ok { return false } switch n := n.(type) { case *ast.ImportSpec: // If the file imports a package to ".", abort // because we don't support that (yet). if n.Name != nil && n.Name.Name == "." { ctxt.logf(n.Pos(), "import to . not supported") ok = false return false } return true case *ast.FuncDecl: // add object for init functions if n.Recv == nil && n.Name.Name == "init" { n.Name.Obj = ast.NewObj(ast.Fun, "init") } if n.Recv != nil { ast.Walk(visit, n.Recv) } var e ast.Expr = n.Name if n.Recv != nil { // It's a method, so we need to synthesise a // selector expression so that visitExpr doesn't // just see a blank name. if len(n.Recv.List) != 1 { ctxt.logf(n.Pos(), "expected one receiver only!") return true } e = &ast.SelectorExpr{ X: n.Recv.List[0].Type, Sel: n.Name, } } ok = ctxt.visitExpr(f, e, false, visitf) local = true ast.Walk(visit, n.Type) if n.Body != nil { ast.Walk(visit, n.Body) } local = false return false case *ast.Ident: ok = ctxt.visitExpr(f, n, local, visitf) return false case *ast.KeyValueExpr: // don't try to resolve the key part of a key-value // because it might be a map key which doesn't // need resolving, and we can't tell without being // complicated with types. ast.Walk(visit, n.Value) return false case *ast.SelectorExpr: ast.Walk(visit, n.X) ok = ctxt.visitExpr(f, n, local, visitf) return false case *ast.File: for _, d := range n.Decls { ast.Walk(visit, d) } return false } return true } ast.Walk(visit, f) }
func checkExprs(t *testing.T, pkg *ast.File, importer Importer) { var visit astVisitor stopped := false visit = func(n ast.Node) bool { if stopped { return false } mustResolve := false var e ast.Expr switch n := n.(type) { case *ast.ImportSpec: // If the file imports a package to ".", abort // because we don't support that (yet). if n.Name != nil && n.Name.Name == "." { stopped = true return false } return true case *ast.FuncDecl: // add object for init functions if n.Recv == nil && n.Name.Name == "init" { n.Name.Obj = ast.NewObj(ast.Fun, "init") } return true case *ast.Ident: if n.Name == "_" { return false } e = n mustResolve = true case *ast.KeyValueExpr: // don't try to resolve the key part of a key-value // because it might be a map key which doesn't // need resolving, and we can't tell without being // complicated with types. ast.Walk(visit, n.Value) return false case *ast.SelectorExpr: ast.Walk(visit, n.X) e = n mustResolve = true case *ast.File: for _, d := range n.Decls { ast.Walk(visit, d) } return false case ast.Expr: e = n default: return true } defer func() { if err := recover(); err != nil { t.Fatalf("panic (%v) on %T", err, e) //t.Fatalf("panic (%v) on %v at %v\n", err, e, FileSet.Position(e.Pos())) } }() obj, _ := ExprType(e, importer) if obj == nil && mustResolve { t.Errorf("no object for %v(%p, %T) at %v\n", e, e, e, FileSet.Position(e.Pos())) } return false } ast.Walk(visit, pkg) }