1 // Copyright 2019 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.
5 // Taken from golang.org/x/tools/refactor/rename.
19 "golang.org/x/tools/go/ast/astutil"
20 "golang.org/x/tools/refactor/satisfy"
23 // errorf reports an error (e.g. conflict) and prevents file modification.
24 func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) {
26 r.errors += fmt.Sprintf(format, args...)
29 // check performs safety checks of the renaming of the 'from' object to r.to.
30 func (r *renamer) check(from types.Object) {
31 if r.objsToUpdate[from] {
34 r.objsToUpdate[from] = true
36 // NB: order of conditions is important.
37 if from_, ok := from.(*types.PkgName); ok {
38 r.checkInFileBlock(from_)
39 } else if from_, ok := from.(*types.Label); ok {
41 } else if isPackageLevel(from) {
42 r.checkInPackageBlock(from)
43 } else if v, ok := from.(*types.Var); ok && v.IsField() {
45 } else if f, ok := from.(*types.Func); ok && recv(f) != nil {
47 } else if isLocal(from) {
48 r.checkInLocalScope(from)
50 r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
51 objectKind(from), from)
55 // checkInFileBlock performs safety checks for renames of objects in the file block,
56 // i.e. imported package names.
57 func (r *renamer) checkInFileBlock(from *types.PkgName) {
58 // Check import name is not "init".
60 r.errorf(from.Pos(), "%q is not a valid imported package name", r.to)
63 // Check for conflicts between file and package block.
64 if prev := from.Pkg().Scope().Lookup(r.to); prev != nil {
65 r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
66 objectKind(from), from.Name(), r.to)
67 r.errorf(prev.Pos(), "\twith this package member %s",
69 return // since checkInPackageBlock would report redundant errors
72 // Check for conflicts in lexical scope.
73 r.checkInLexicalScope(from, r.packages[from.Pkg()])
76 // checkInPackageBlock performs safety checks for renames of
77 // func/var/const/type objects in the package block.
78 func (r *renamer) checkInPackageBlock(from types.Object) {
79 // Check that there are no references to the name from another
80 // package if the renaming would make it unexported.
81 if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
82 for typ, pkg := range r.packages {
83 if typ == from.Pkg() {
86 if id := someUse(pkg.GetTypesInfo(), from); id != nil &&
87 !r.checkExport(id, typ, from) {
93 pkg := r.packages[from.Pkg()]
98 // Check that in the package block, "init" is a function, and never referenced.
100 kind := objectKind(from)
102 // Reject if intra-package references to it exist.
103 for id, obj := range pkg.GetTypesInfo().Uses {
106 "renaming this func %q to %q would make it a package initializer",
108 r.errorf(id.Pos(), "\tbut references to it exist")
113 r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
118 // Check for conflicts between package block and all file blocks.
119 for _, f := range pkg.GetSyntax() {
120 fileScope := pkg.GetTypesInfo().Scopes[f]
121 b, prev := fileScope.LookupParent(r.to, token.NoPos)
123 r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to)
124 var prevPos token.Pos
128 r.errorf(prevPos, "\twith this %s", objectKind(prev))
129 return // since checkInPackageBlock would report redundant errors
133 // Check for conflicts in lexical scope.
135 for _, pkg := range r.packages {
136 r.checkInLexicalScope(from, pkg)
139 r.checkInLexicalScope(from, pkg)
143 func (r *renamer) checkInLocalScope(from types.Object) {
144 pkg := r.packages[from.Pkg()]
145 r.checkInLexicalScope(from, pkg)
148 // checkInLexicalScope performs safety checks that a renaming does not
149 // change the lexical reference structure of the specified package.
151 // For objects in lexical scope, there are three kinds of conflicts:
152 // same-, sub-, and super-block conflicts. We will illustrate all three
153 // using this example:
163 // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
164 // with the new name already exists, defined in the same lexical block
165 // as the old object.
167 // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
168 // a reference to x from within (what would become) a hole in its scope.
169 // The definition of y in an (inner) sub-block would cast a shadow in
170 // the scope of the renamed variable.
172 // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the
173 // converse situation: there is an existing definition of the new name
174 // (x) in an (enclosing) super-block, and the renaming would create a
175 // hole in its scope, within which there exist references to it. The
176 // new name casts a shadow in scope of the existing definition of x in
179 // Removing the old name (and all references to it) is always safe, and
180 // requires no checks.
182 func (r *renamer) checkInLexicalScope(from types.Object, pkg Package) {
183 b := from.Parent() // the block defining the 'from' object
185 toBlock, to := b.LookupParent(r.to, from.Parent().End())
187 // same-block conflict
188 r.errorf(from.Pos(), "renaming this %s %q to %q",
189 objectKind(from), from.Name(), r.to)
190 r.errorf(to.Pos(), "\tconflicts with %s in same block",
193 } else if toBlock != nil {
194 // Check for super-block conflict.
195 // The name r.to is defined in a superblock.
196 // Is that name referenced from within this block?
197 forEachLexicalRef(pkg, to, func(id *ast.Ident, block *types.Scope) bool {
198 _, obj := lexicalLookup(block, from.Name(), id.Pos())
200 // super-block conflict
201 r.errorf(from.Pos(), "renaming this %s %q to %q",
202 objectKind(from), from.Name(), r.to)
203 r.errorf(id.Pos(), "\twould shadow this reference")
204 r.errorf(to.Pos(), "\tto the %s declared here",
212 // Check for sub-block conflict.
213 // Is there an intervening definition of r.to between
214 // the block defining 'from' and some reference to it?
215 forEachLexicalRef(pkg, from, func(id *ast.Ident, block *types.Scope) bool {
216 // Find the block that defines the found reference.
217 // It may be an ancestor.
218 fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos())
220 // See what r.to would resolve to in the same scope.
221 toBlock, to := lexicalLookup(block, r.to, id.Pos())
223 // sub-block conflict
224 if deeper(toBlock, fromBlock) {
225 r.errorf(from.Pos(), "renaming this %s %q to %q",
226 objectKind(from), from.Name(), r.to)
227 r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
228 r.errorf(to.Pos(), "\tby this intervening %s definition",
236 // Renaming a type that is used as an embedded field
237 // requires renaming the field too. e.g.
238 // type T int // if we rename this to U..
240 // print(s.T) // ...this must change too
241 if _, ok := from.(*types.TypeName); ok {
242 for id, obj := range pkg.GetTypesInfo().Uses {
244 if field := pkg.GetTypesInfo().Defs[id]; field != nil {
252 // lexicalLookup is like (*types.Scope).LookupParent but respects the
253 // environment visible at pos. It assumes the relative position
254 // information is correct with each file.
255 func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) {
256 for b := block; b != nil; b = b.Parent() {
257 obj := b.Lookup(name)
258 // The scope of a package-level object is the entire package,
259 // so ignore pos in that case.
260 // No analogous clause is needed for file-level objects
261 // since no reference can appear before an import decl.
262 if obj == nil || obj.Pkg() == nil {
265 if b == obj.Pkg().Scope() || obj.Pos() < pos {
272 // deeper reports whether block x is lexically deeper than y.
273 func deeper(x, y *types.Scope) bool {
274 if x == y || x == nil {
279 return deeper(x.Parent(), y.Parent())
283 // forEachLexicalRef calls fn(id, block) for each identifier id in package
284 // pkg that is a reference to obj in lexical scope. block is the
285 // lexical block enclosing the reference. If fn returns false the
286 // iteration is terminated and findLexicalRefs returns false.
287 func forEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
291 var visit func(n ast.Node) bool
292 visit = func(n ast.Node) bool {
294 stack = stack[:len(stack)-1] // pop
298 return false // bail out
301 stack = append(stack, n) // push
302 switch n := n.(type) {
304 if pkg.GetTypesInfo().Uses[n] == obj {
305 block := enclosingBlock(pkg.GetTypesInfo(), stack)
310 return visit(nil) // pop stack
312 case *ast.SelectorExpr:
314 ast.Inspect(n.X, visit)
315 return visit(nil) // pop stack, don't descend
317 case *ast.CompositeLit:
318 // Handle recursion ourselves for struct literals
319 // so we don't visit field identifiers.
320 tv, ok := pkg.GetTypesInfo().Types[n]
322 return visit(nil) // pop stack, don't descend
324 if _, ok := Deref(tv.Type).Underlying().(*types.Struct); ok {
326 ast.Inspect(n.Type, visit)
328 for _, elt := range n.Elts {
329 if kv, ok := elt.(*ast.KeyValueExpr); ok {
330 ast.Inspect(kv.Value, visit)
332 ast.Inspect(elt, visit)
335 return visit(nil) // pop stack, don't descend
341 for _, f := range pkg.GetSyntax() {
342 ast.Inspect(f, visit)
353 // enclosingBlock returns the innermost block enclosing the specified
354 // AST node, specified in the form of a path from the root of the file,
356 func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope {
357 for i := range stack {
358 n := stack[len(stack)-1-i]
359 // For some reason, go/types always associates a
360 // function's scope with its FuncType.
361 // TODO(adonovan): feature or a bug?
362 switch f := n.(type) {
368 if b := info.Scopes[n]; b != nil {
372 panic("no Scope for *ast.File")
375 func (r *renamer) checkLabel(label *types.Label) {
376 // Check there are no identical labels in the function's label block.
377 // (Label blocks don't nest, so this is easy.)
378 if prev := label.Parent().Lookup(r.to); prev != nil {
379 r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name())
380 r.errorf(prev.Pos(), "\twould conflict with this one")
384 // checkStructField checks that the field renaming will not cause
385 // conflicts at its declaration, or ambiguity or changes to any selection.
386 func (r *renamer) checkStructField(from *types.Var) {
387 // Check that the struct declaration is free of field conflicts,
388 // and field/method conflicts.
390 // go/types offers no easy way to get from a field (or interface
391 // method) to its declaring struct (or interface), so we must
393 fromPkg, ok := r.packages[from.Pkg()]
397 pkg, path, _ := pathEnclosingInterval(r.fset, fromPkg, from.Pos(), from.Pos())
398 if pkg == nil || path == nil {
401 // path matches this pattern:
402 // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]
404 // Ascend to FieldList.
407 if _, ok := path[i].(*ast.FieldList); ok {
413 tStruct := path[i].(*ast.StructType)
415 // Ascend past parens (unlikely).
417 _, ok := path[i].(*ast.ParenExpr)
423 if spec, ok := path[i].(*ast.TypeSpec); ok {
424 // This struct is also a named type.
425 // We must check for direct (non-promoted) field/field
426 // and method/field conflicts.
427 named := pkg.GetTypesInfo().Defs[spec.Name].Type()
428 prev, indices, _ := types.LookupFieldOrMethod(named, true, pkg.GetTypes(), r.to)
429 if len(indices) == 1 {
430 r.errorf(from.Pos(), "renaming this field %q to %q",
432 r.errorf(prev.Pos(), "\twould conflict with this %s",
434 return // skip checkSelections to avoid redundant errors
437 // This struct is not a named type.
438 // We need only check for direct (non-promoted) field/field conflicts.
439 T := pkg.GetTypesInfo().Types[tStruct].Type.Underlying().(*types.Struct)
440 for i := 0; i < T.NumFields(); i++ {
441 if prev := T.Field(i); prev.Name() == r.to {
442 r.errorf(from.Pos(), "renaming this field %q to %q",
444 r.errorf(prev.Pos(), "\twould conflict with this field")
445 return // skip checkSelections to avoid redundant errors
450 // Renaming an anonymous field requires renaming the type too. e.g.
451 // print(s.T) // if we rename T to U,
452 // type T int // this and
453 // var s struct {T} // this must change too.
454 if from.Anonymous() {
455 if named, ok := from.Type().(*types.Named); ok {
457 } else if named, ok := Deref(from.Type()).(*types.Named); ok {
462 // Check integrity of existing (field and method) selections.
463 r.checkSelections(from)
466 // checkSelection checks that all uses and selections that resolve to
467 // the specified object would continue to do so after the renaming.
468 func (r *renamer) checkSelections(from types.Object) {
469 for typ, pkg := range r.packages {
470 if id := someUse(pkg.GetTypesInfo(), from); id != nil {
471 if !r.checkExport(id, typ, from) {
476 for syntax, sel := range pkg.GetTypesInfo().Selections {
477 // There may be extant selections of only the old
478 // name or only the new name, so we must check both.
479 // (If neither, the renaming is sound.)
481 // In both cases, we wish to compare the lengths
482 // of the implicit field path (Selection.Index)
483 // to see if the renaming would change it.
485 // If a selection that resolves to 'from', when renamed,
486 // would yield a path of the same or shorter length,
487 // this indicates ambiguity or a changed referent,
488 // analogous to same- or sub-block lexical conflict.
490 // If a selection using the name 'to' would
491 // yield a path of the same or shorter length,
492 // this indicates ambiguity or shadowing,
493 // analogous to same- or super-block lexical conflict.
495 // TODO(adonovan): fix: derive from Types[syntax.X].Mode
496 // TODO(adonovan): test with pointer, value, addressable value.
497 isAddressable := true
499 if sel.Obj() == from {
500 if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil {
501 // Renaming this existing selection of
502 // 'from' may block access to an existing
503 // type member named 'to'.
504 delta := len(indices) - len(sel.Index())
506 continue // no ambiguity
508 r.selectionConflict(from, delta, syntax, obj)
511 } else if sel.Obj().Name() == r.to {
512 if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from {
513 // Renaming 'from' may cause this existing
514 // selection of the name 'to' to change
516 delta := len(indices) - len(sel.Index())
518 continue // no ambiguity
520 r.selectionConflict(from, -delta, syntax, sel.Obj())
528 func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) {
529 r.errorf(from.Pos(), "renaming this %s %q to %q",
530 objectKind(from), from.Name(), r.to)
534 // analogous to sub-block conflict
535 r.errorf(syntax.Sel.Pos(),
536 "\twould change the referent of this selection")
537 r.errorf(obj.Pos(), "\tof this %s", objectKind(obj))
539 // analogous to same-block conflict
540 r.errorf(syntax.Sel.Pos(),
541 "\twould make this reference ambiguous")
542 r.errorf(obj.Pos(), "\twith this %s", objectKind(obj))
544 // analogous to super-block conflict
545 r.errorf(syntax.Sel.Pos(),
546 "\twould shadow this selection")
547 r.errorf(obj.Pos(), "\tof the %s declared here",
552 // checkMethod performs safety checks for renaming a method.
553 // There are three hazards:
554 // - declaration conflicts
555 // - selection ambiguity/changes
556 // - entailed renamings of assignable concrete/interface types.
557 // We reject renamings initiated at concrete methods if it would
558 // change the assignability relation. For renamings of abstract
559 // methods, we rename all methods transitively coupled to it via
561 func (r *renamer) checkMethod(from *types.Func) {
563 if from.Pkg() == nil {
564 r.errorf(from.Pos(), "you cannot rename built-in method %s", from)
568 // ASSIGNABILITY: We reject renamings of concrete methods that
569 // would break a 'satisfy' constraint; but renamings of abstract
570 // methods are allowed to proceed, and we rename affected
571 // concrete and abstract methods as necessary. It is the
572 // initial method that determines the policy.
574 // Check for conflict at point of declaration.
575 // Check to ensure preservation of assignability requirements.
576 R := recv(from).Type()
581 prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to)
583 r.errorf(from.Pos(), "renaming this interface method %q to %q",
585 r.errorf(prev.Pos(), "\twould conflict with this method")
589 // Check all interfaces that embed this one for
590 // declaration conflicts too.
591 for _, pkg := range r.packages {
592 // Start with named interface types (better errors)
593 for _, obj := range pkg.GetTypesInfo().Defs {
594 if obj, ok := obj.(*types.TypeName); ok && IsInterface(obj.Type()) {
595 f, _, _ := types.LookupFieldOrMethod(
596 obj.Type(), false, from.Pkg(), from.Name())
600 t, _, _ := types.LookupFieldOrMethod(
601 obj.Type(), false, from.Pkg(), r.to)
605 r.errorf(from.Pos(), "renaming this interface method %q to %q",
607 r.errorf(t.Pos(), "\twould conflict with this method")
608 r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name())
612 // Now look at all literal interface types (includes named ones again).
613 for e, tv := range pkg.GetTypesInfo().Types {
614 if e, ok := e.(*ast.InterfaceType); ok {
616 _ = tv.Type.(*types.Interface)
617 // TODO(adonovan): implement same check as above.
624 // Find the set of concrete or abstract methods directly
625 // coupled to abstract method 'from' by some
626 // satisfy.Constraint, and rename them too.
627 for key := range r.satisfy() {
628 // key = (lhs, rhs) where lhs is always an interface.
630 lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
634 rmethods := r.msets.MethodSet(key.RHS)
635 rsel := rmethods.Lookup(from.Pkg(), from.Name())
640 // If both sides have a method of this name,
641 // and one of them is m, the other must be coupled.
642 var coupled *types.Func
645 coupled = rsel.Obj().(*types.Func)
647 coupled = lsel.Obj().(*types.Func)
652 // We must treat concrete-to-interface
653 // constraints like an implicit selection C.f of
654 // each interface method I.f, and check that the
655 // renaming leaves the selection unchanged and
658 // Fun fact: the implicit selection of C.f
659 // type I interface{f()}
662 // var _ I = C{} // here
663 // yields abstract method I.f. This can make error
664 // messages less than obvious.
666 if !IsInterface(key.RHS) {
667 // The logic below was derived from checkSelections.
669 rtosel := rmethods.Lookup(from.Pkg(), r.to)
671 rto := rtosel.Obj().(*types.Func)
672 delta := len(rsel.Index()) - len(rtosel.Index())
674 continue // no ambiguity
677 // TODO(adonovan): record the constraint's position.
678 keyPos := token.NoPos
680 r.errorf(from.Pos(), "renaming this method %q to %q",
683 // analogous to same-block conflict
684 r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous",
685 r.to, key.RHS, key.LHS)
686 r.errorf(rto.Pos(), "\twith (%s).%s",
687 recv(rto).Type(), r.to)
689 // analogous to super-block conflict
690 r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s",
691 r.to, key.RHS, key.LHS)
692 r.errorf(coupled.Pos(), "\tfrom (%s).%s",
693 recv(coupled).Type(), r.to)
694 r.errorf(rto.Pos(), "\tto (%s).%s",
695 recv(rto).Type(), r.to)
697 return // one error is enough
701 if !r.changeMethods {
702 // This should be unreachable.
703 r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from)
704 r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled)
705 r.errorf(from.Pos(), "\tPlease file a bug report")
709 // Rename the coupled method to preserve assignability.
716 prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to)
717 if prev != nil && len(indices) == 1 {
718 r.errorf(from.Pos(), "renaming this method %q to %q",
720 r.errorf(prev.Pos(), "\twould conflict with this %s",
727 // Find the set of abstract methods coupled to concrete
728 // method 'from' by some satisfy.Constraint, and rename
731 // Coupling may be indirect, e.g. I.f <-> C.f via type D.
733 // type I interface {f()}
739 for key := range r.satisfy() {
740 // key = (lhs, rhs) where lhs is always an interface.
741 if IsInterface(key.RHS) {
744 rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
745 if rsel == nil || rsel.Obj() != from {
746 continue // rhs does not have the method
748 lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
752 imeth := lsel.Obj().(*types.Func)
754 // imeth is the abstract method (e.g. I.f)
755 // and key.RHS is the concrete coupling type (e.g. D).
756 if !r.changeMethods {
757 r.errorf(from.Pos(), "renaming this method %q to %q",
762 I := recv(imeth).Type()
763 if named, ok := I.(*types.Named); ok {
764 pos = named.Obj().Pos()
765 iface = "interface " + named.Obj().Name()
770 r.errorf(pos, "\twould make %s no longer assignable to %s",
772 r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)",
774 return // one error is enough
777 // Rename the coupled interface method to preserve assignability.
782 // Check integrity of existing (field and method) selections.
783 // We skip this if there were errors above, to avoid redundant errors.
784 r.checkSelections(from)
787 func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool {
788 // Reject cross-package references if r.to is unexported.
789 // (Such references may be qualified identifiers or field/method
791 if !ast.IsExported(r.to) && pkg != from.Pkg() {
793 "renaming %q to %q would make it unexported",
795 r.errorf(id.Pos(), "\tbreaking references from packages such as %q",
802 // satisfy returns the set of interface satisfaction constraints.
803 func (r *renamer) satisfy() map[satisfy.Constraint]bool {
804 if r.satisfyConstraints == nil {
805 // Compute on demand: it's expensive.
807 for _, pkg := range r.packages {
808 // From satisfy.Finder documentation:
810 // The package must be free of type errors, and
811 // info.{Defs,Uses,Selections,Types} must have been populated by the
814 // Only proceed if all packages have no errors.
815 if errs := pkg.GetErrors(); len(errs) > 0 {
816 r.errorf(token.NoPos, // we don't have a position for this error.
817 "renaming %q to %q not possible because %q has errors",
818 r.from, r.to, pkg.PkgPath())
821 f.Find(pkg.GetTypesInfo(), pkg.GetSyntax())
823 r.satisfyConstraints = f.Result
825 return r.satisfyConstraints
828 // -- helpers ----------------------------------------------------------
830 // recv returns the method's receiver.
831 func recv(meth *types.Func) *types.Var {
832 return meth.Type().(*types.Signature).Recv()
835 // someUse returns an arbitrary use of obj within info.
836 func someUse(info *types.Info, obj types.Object) *ast.Ident {
837 for id, o := range info.Uses {
845 // pathEnclosingInterval returns the Package and ast.Node that
846 // contain source interval [start, end), and all the node's ancestors
847 // up to the AST root. It searches all ast.Files of all packages.
848 // exact is defined as for astutil.PathEnclosingInterval.
850 // The zero value is returned if not found.
852 func pathEnclosingInterval(fset *token.FileSet, pkg Package, start, end token.Pos) (resPkg Package, path []ast.Node, exact bool) {
853 pkgs := []Package{pkg}
854 for _, f := range pkg.GetSyntax() {
855 for _, imp := range f.Imports {
859 importPath, err := strconv.Unquote(imp.Path.Value)
863 importPkg, err := pkg.GetImport(importPath)
865 return nil, nil, false
867 pkgs = append(pkgs, importPkg)
870 for _, p := range pkgs {
871 for _, f := range p.GetSyntax() {
872 if f.Pos() == token.NoPos {
873 // This can happen if the parser saw
874 // too many errors and bailed out.
875 // (Use parser.AllErrors to prevent that.)
878 if !tokenFileContainsPos(fset.File(f.Pos()), start) {
881 if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
882 return pkg, path, exact
886 return nil, nil, false
889 // TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
890 func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
893 return base <= p && p < base+f.Size()
896 func objectKind(obj types.Object) string {
900 switch obj := obj.(type) {
902 return "imported package name"
903 case *types.TypeName:
910 if obj.Type().(*types.Signature).Recv() != nil {
914 // label, func, var, const
915 return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
918 // NB: for renamings, blank is not considered valid.
919 func isValidIdentifier(id string) bool {
920 if id == "" || id == "_" {
923 for i, r := range id {
924 if !isLetter(r) && (i == 0 || !isDigit(r)) {
928 return token.Lookup(id) == token.IDENT
931 // isLocal reports whether obj is local to some function.
932 // Precondition: not a struct field or interface method.
933 func isLocal(obj types.Object) bool {
934 // [... 5=stmt 4=func 3=file 2=pkg 1=universe]
936 for scope := obj.Parent(); scope != nil; scope = scope.Parent() {
942 func isPackageLevel(obj types.Object) bool {
946 return obj.Pkg().Scope().Lookup(obj.Name()) == obj
949 // -- Plundered from go/scanner: ---------------------------------------
951 func isLetter(ch rune) bool {
952 return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
955 func isDigit(ch rune) bool {
956 return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)