// deeper reports whether block x is lexically deeper than y. func deeper(x, y *types.Scope) bool { if x == y || x == nil { return false } else if y == nil { return true } else { return deeper(x.Parent(), y.Parent()) } }
func (g *Grapher) assignPaths(s *types.Scope, prefix []string, pkgscope bool) { g.scopePaths[s] = prefix for _, name := range s.Names() { e := s.Lookup(name) if _, seen := g.paths[e]; seen { continue } path := append(append([]string{}, prefix...), name) g.paths[e] = path g.exported[e] = ast.IsExported(name) && pkgscope g.pkgscope[e] = pkgscope if tn, ok := e.(*types.TypeName); ok { // methods named := tn.Type().(*types.Named) g.assignMethodPaths(named, path, pkgscope) // struct fields typ := derefType(tn.Type().Underlying()) if styp, ok := typ.(*types.Struct); ok { g.assignStructFieldPaths(styp, path, pkgscope) } } else if v, ok := e.(*types.Var); ok { // struct fields if type is anonymous struct if styp, ok := derefType(v.Type()).(*types.Struct); ok { g.assignStructFieldPaths(styp, path, pkgscope) } } } seenChildPrefixes := map[string]struct{}{} for i := 0; i < s.NumChildren(); i++ { c := s.Child(i) childPrefix := prefix pkgscope := pkgscope if path := g.scopePath(prefix, c); path != nil { childPrefix = append([]string{}, path...) pkgscope = false } if len(childPrefix) >= 1 { // Ensure all child prefixes are unique. This is an issue when you // have, for example: // func init() { x:=0;_=x};func init() { x:=0;_=x} // This is valid Go code but if we don't uniquify the two `x`s, they // will have the same paths. cp := strings.Join(childPrefix, "/") if _, seen := seenChildPrefixes[cp]; seen { childPrefix[len(childPrefix)-1] += fmt.Sprintf("$%d", i) } seenChildPrefixes[cp] = struct{}{} } g.assignPaths(c, childPrefix, pkgscope) } }
func (g *Grapher) path(obj types.Object) (path []string) { if path, present := g.paths[obj]; present { return path } var scope *types.Scope pkgInfo, astPath, _ := g.program.PathEnclosingInterval(obj.Pos(), obj.Pos()) if astPath != nil { for _, node := range astPath { if s, hasScope := pkgInfo.Scopes[node]; hasScope { scope = s } } } if scope == nil { scope = obj.Parent() } if scope == nil { // TODO(sqs): make this actually handle cases like the one described in // https://github.com/sourcegraph/sourcegraph.com/issues/218 log.Printf("Warning: no scope for object %s at pos %s", obj.String(), g.program.Fset.Position(obj.Pos())) return nil } prefix, hasPath := g.scopePaths[scope] if !hasPath { panic("no scope path for scope " + scope.String()) } path = append([]string{}, prefix...) p := g.program.Fset.Position(obj.Pos()) path = append(path, obj.Name()+uniqID(p)) return path panic("no scope node for object " + obj.String()) }
func (g *Grapher) scopeLabel(s *types.Scope) (path []string) { node, present := g.scopeNodes[s] if !present { // TODO(sqs): diagnose why this happens. See https://github.com/sourcegraph/sourcegraph.com/issues/163. // log.Printf("no node found for scope (giving a dummy label); scope is: %s", s.String()) return []string{fmt.Sprintf("ERROR%d", rand.Intn(10000))} } switch n := node.(type) { case *ast.File: return []string{} case *ast.Package: return []string{} case *ast.FuncType: // get func name _, astPath, _ := g.program.PathEnclosingInterval(n.Pos(), n.End()) if false { log.Printf("----------") for i, n := range astPath { log.Printf("%d. %T %+v", i, n, n) } } if f, ok := astPath[0].(*ast.FuncDecl); ok { var path []string if f.Recv != nil { path = []string{derefNode(f.Recv.List[0].Type).(*ast.Ident).Name} } var uniqName string if f.Name.Name == "init" { // init function can appear multiple times in each file and // package, so need to uniquify it uniqName = f.Name.Name + uniqID(g.program.Fset.Position(f.Name.Pos())) } else { uniqName = f.Name.Name } path = append(path, uniqName) return path } } // get this scope's index in parent // TODO(sqs): is it necessary to uniquify this here now that we're handling // init above? p := s.Parent() var prefix []string if fs, ok := g.scopeNodes[p].(*ast.File); ok { // avoid file scope collisions by using file index as well filename := g.program.Fset.Position(fs.Name.Pos()).Filename prefix = []string{fmt.Sprintf("$%s", strippedFilename(filename))} } for i := 0; i < p.NumChildren(); i++ { if p.Child(i) == s { filename := g.program.Fset.Position(node.Pos()).Filename return append(prefix, fmt.Sprintf("$%s%d", strippedFilename(filename), i)) } } panic("unreachable") }