func (b *candidateCollector) appendObject(obj types.Object) { // TODO(mdempsky): Change this to true. const proposeBuiltins = false if obj.Pkg() != b.localpkg { if obj.Parent() == types.Universe { if !proposeBuiltins { return } } else if !obj.Exported() { return } } // TODO(mdempsky): Reconsider this functionality. if b.filter != nil && !b.filter(obj) { return } if b.filter != nil || strings.HasPrefix(obj.Name(), b.partial) { b.exact = append(b.exact, obj) } else if strings.HasPrefix(strings.ToLower(obj.Name()), strings.ToLower(b.partial)) { b.badcase = append(b.badcase, obj) } }
// isLocal reports whether obj is local to some function. // Precondition: not a struct field or interface method. func isLocal(obj types.Object) bool { // [... 5=stmt 4=func 3=file 2=pkg 1=universe] var depth int for scope := obj.Parent(); scope != nil; scope = scope.Parent() { depth++ } return depth >= 4 }
// classify classifies objects by how far // we have to look to find references to them. func classify(obj types.Object) (global, pkglevel bool) { if obj.Exported() { if obj.Parent() == nil { // selectable object (field or method) return true, false } if obj.Parent() == obj.Pkg().Scope() { // lexical object (package-level var/const/func/type) return true, true } } // object with unexported named or defined in local scope return false, false }
// newSignature constructs and returns a tag and base signature for obj. The // tag represents the "kind" of signature, to disambiguate built-in types from // user-defined names, fields from methods, and so on. The base is a unique // name for obj within its package, modulo the tag. func (pi *PackageInfo) newSignature(obj types.Object) (tag, base string) { if obj.Name() == "" { return tagVar, "_" } topLevelTag := tagVar switch t := obj.(type) { case *types.Builtin: return isBuiltin + tagFunc, t.Name() case *types.Nil: return isBuiltin + tagConst, "nil" case *types.PkgName: return "", ":pkg:" // the vname corpus and path carry the package name case *types.Const: topLevelTag = tagConst if t.Pkg() == nil { return isBuiltin + tagConst, t.Name() } case *types.Var: if t.IsField() { if owner, ok := pi.owner[t]; ok { _, base := pi.newSignature(owner) return tagField, base + "." + t.Name() } return tagField, fmt.Sprintf("[%p].%s", t, t.Name()) } else if owner, ok := pi.owner[t]; ok { _, base := pi.newSignature(owner) return tagParam, base + ":" + t.Name() } case *types.Func: topLevelTag = tagFunc if recv := t.Type().(*types.Signature).Recv(); recv != nil { // method if owner, ok := pi.owner[t]; ok { _, base := pi.newSignature(owner) return tagMethod, base + "." + t.Name() } return tagMethod, fmt.Sprintf("(%s).%s", recv.Type(), t.Name()) } case *types.TypeName: topLevelTag = tagType if t.Pkg() == nil { return isBuiltin + tagType, t.Name() } case *types.Label: return tagLabel, fmt.Sprintf("[%p].%s", t, t.Name()) default: log.Panicf("Unexpected object kind: %T", obj) } // At this point, we have eliminated built-in objects; everything else must // be defined in a package. if obj.Pkg() == nil { log.Panic("Object without a package: ", obj) } // Objects at package scope (i.e., parent scope is package scope). if obj.Parent() == obj.Pkg().Scope() { return topLevelTag, obj.Name() } // Objects in interior (local) scopes, i.e., everything else. return topLevelTag, fmt.Sprintf("[%p].%s", obj, obj.Name()) }
func isPkgLevel(o types.Object) bool { return o.Parent() != nil && o.Parent().Parent() == types.Universe }
// checkInLexicalScope performs safety checks that a renaming does not // change the lexical reference structure of the specified package. // // For objects in lexical scope, there are three kinds of conflicts: // same-, sub-, and super-block conflicts. We will illustrate all three // using this example: // // var x int // var z int // // func f(y int) { // print(x) // print(y) // } // // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object // with the new name already exists, defined in the same lexical block // as the old object. // // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists // a reference to x from within (what would become) a hole in its scope. // The definition of y in an (inner) sub-block would cast a shadow in // the scope of the renamed variable. // // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the // converse situation: there is an existing definition of the new name // (x) in an (enclosing) super-block, and the renaming would create a // hole in its scope, within which there exist references to it. The // new name casts a shadow in scope of the existing definition of x in // the super-block. // // Removing the old name (and all references to it) is always safe, and // requires no checks. // func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) { b := from.Parent() // the block defining the 'from' object if b != nil { toBlock, to := b.LookupParent(r.to, from.Parent().End()) if toBlock == b { // same-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(to.Pos(), "\tconflicts with %s in same block", objectKind(to)) return } else if toBlock != nil { // Check for super-block conflict. // The name r.to is defined in a superblock. // Is that name referenced from within this block? forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool { _, obj := lexicalLookup(block, from.Name(), id.Pos()) if obj == from { // super-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\twould shadow this reference") r.errorf(to.Pos(), "\tto the %s declared here", objectKind(to)) return false // stop } return true }) } } // Check for sub-block conflict. // Is there an intervening definition of r.to between // the block defining 'from' and some reference to it? forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool { // Find the block that defines the found reference. // It may be an ancestor. fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos()) // See what r.to would resolve to in the same scope. toBlock, to := lexicalLookup(block, r.to, id.Pos()) if to != nil { // sub-block conflict if deeper(toBlock, fromBlock) { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\twould cause this reference to become shadowed") r.errorf(to.Pos(), "\tby this intervening %s definition", objectKind(to)) return false // stop } } return true }) // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range info.Uses { if obj == from { if field := info.Defs[id]; field != nil { r.check(field) } } } } }
// getDoc returns the doc string associated with types.Object // parent is the name of the containing scope ("" for global scope) func (p *Package) getDoc(parent string, o types.Object) string { n := o.Name() switch o.(type) { case *types.Const: for _, c := range p.doc.Consts { for _, cn := range c.Names { if n == cn { return c.Doc } } } case *types.Var: for _, v := range p.doc.Vars { for _, vn := range v.Names { if n == vn { return v.Doc } } } case *types.Func: doc := func() string { if o.Parent() == nil || (o.Parent() != nil && parent != "") { for _, typ := range p.doc.Types { if typ.Name != parent { continue } if o.Parent() == nil { for _, m := range typ.Methods { if m.Name == n { return m.Doc } } } else { for _, m := range typ.Funcs { if m.Name == n { return m.Doc } } } } } else { for _, f := range p.doc.Funcs { if n == f.Name { return f.Doc } } } return "" }() sig := o.Type().(*types.Signature) parseFn := func(tup *types.Tuple) []string { params := []string{} if tup == nil { return params } for i := 0; i < tup.Len(); i++ { paramVar := tup.At(i) paramType := p.syms.symtype(paramVar.Type()).pysig if paramVar.Name() != "" { paramType = fmt.Sprintf("%s %s", paramType, paramVar.Name()) } params = append(params, paramType) } return params } params := parseFn(sig.Params()) results := parseFn(sig.Results()) paramString := strings.Join(params, ", ") resultString := strings.Join(results, ", ") //FIXME(sbinet): add receiver for methods? docSig := fmt.Sprintf("%s(%s) %s", o.Name(), paramString, resultString) if doc != "" { doc = fmt.Sprintf("%s\n\n%s", docSig, doc) } else { doc = docSig } return doc case *types.TypeName: for _, t := range p.doc.Types { if n == t.Name { return t.Doc } } default: // TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", o, o)) } return "" }