func tokenOf(o types.Object) string { switch o := o.(type) { case *types.Func: return "func" case *types.Var: return "var" case *types.TypeName: return "type" case *types.Const: return "const" case *types.PkgName: return "package" case *types.Builtin: return "builtin" // e.g. when describing package "unsafe" case *types.Nil: return "nil" case *types.Label: return "label" case *types.Alias: if o.Orig() == nil { return "alias" } return tokenOf(o.Orig()) } panic(o) }
func typesObjectString(obj types.Object) string { var prefix string switch obj.(type) { case *types.Builtin: prefix = "builtin" case *types.Func: prefix = "func" case *types.Const: prefix = "const" case *types.PkgName: prefix = "package" case *types.Var: prefix = "var" case *types.Label: prefix = "label" case *types.Nil: return "nil" case *types.TypeName: prefix = "type" default: panic(fmt.Sprintf("unexpected type: %T", obj)) } return prefix + " " + obj.Name() }
func updateGetDefinitionsContext(ctx *getDefinitionsContext, def *Definition, ident *ast.Ident, obj types.Object) { switch obj.(type) { case *types.Var: //Processing vars later to be sure that all info about structs already filled ctx.vars = append(ctx.vars, newObjectWithIdent(obj, ident)) case *types.Func: //Processing funcs later to be sure that all info about interfaces already filled ctx.funcs = append(ctx.funcs, newObjectWithIdent(obj, ident)) case *types.TypeName: //If the underlying type is struct, then filling //positions of struct's fields (key) and struct name(value) //to map. Then we can extract struct name for fields when //will be analyze them. t := obj.(*types.TypeName) underlyingType := t.Type().Underlying() switch underlyingType.(type) { case *types.Struct: s := underlyingType.(*types.Struct) for i := 0; i < s.NumFields(); i++ { field := s.Field(i) ctx.structs[posToStr(ctx.fset, field.Pos())] = obj.Name() } } } //Check for interfaces underlyingType := obj.Type().Underlying() switch underlyingType.(type) { case *types.Interface: d := new(defWithInterface) d.def = def d.interfac = underlyingType.(*types.Interface) ctx.interfaces = append(ctx.interfaces, d) } }
// checkSelection checks that all uses and selections that resolve to // the specified object would continue to do so after the renaming. func (r *renamer) checkSelections(from types.Object) { for pkg, info := range r.packages { if id := someUse(info, from); id != nil { if !r.checkExport(id, pkg, from) { return } } for syntax, sel := range info.Selections { // There may be extant selections of only the old // name or only the new name, so we must check both. // (If neither, the renaming is sound.) // // In both cases, we wish to compare the lengths // of the implicit field path (Selection.Index) // to see if the renaming would change it. // // If a selection that resolves to 'from', when renamed, // would yield a path of the same or shorter length, // this indicates ambiguity or a changed referent, // analogous to same- or sub-block lexical conflict. // // If a selection using the name 'to' would // yield a path of the same or shorter length, // this indicates ambiguity or shadowing, // analogous to same- or super-block lexical conflict. // TODO(adonovan): fix: derive from Types[syntax.X].Mode // TODO(adonovan): test with pointer, value, addressable value. isAddressable := true if sel.Obj() == from { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil { // Renaming this existing selection of // 'from' may block access to an existing // type member named 'to'. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } r.selectionConflict(from, delta, syntax, obj) return } } else if sel.Obj().Name() == r.to { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { // Renaming 'from' may cause this existing // selection of the name 'to' to change // its meaning. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } r.selectionConflict(from, -delta, syntax, sel.Obj()) return } } } } }
// lookup returns the address of the named variable identified by obj // that is local to function f or one of its enclosing functions. // If escaping, the reference comes from a potentially escaping pointer // expression and the referent must be heap-allocated. // func (f *Function) lookup(obj types.Object, escaping bool) Value { if v, ok := f.objects[obj]; ok { if alloc, ok := v.(*Alloc); ok && escaping { alloc.Heap = true } return v // function-local var (address) } // Definition must be in an enclosing function; // plumb it through intervening closures. if f.parent == nil { panic("no ssa.Value for " + obj.String()) } outer := f.parent.lookup(obj, true) // escaping v := &FreeVar{ name: obj.Name(), typ: outer.Type(), pos: outer.Pos(), outer: outer, parent: f, } f.objects[obj] = v f.FreeVars = append(f.FreeVars, v) return v }
func isStringer(obj types.Object) bool { switch obj := obj.(type) { case *types.Func: if obj.Name() != "String" { return false } sig, ok := obj.Type().(*types.Signature) if !ok { return false } if sig.Recv() == nil { return false } if sig.Params().Len() != 0 { return false } res := sig.Results() if res.Len() != 1 { return false } ret := res.At(0).Type() if ret != types.Universe.Lookup("string").Type() { return false } return true default: return false } return false }
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) } }
// lookup returns the address of the named variable identified by obj // that is local to function f or one of its enclosing functions. // If escaping, the reference comes from a potentially escaping pointer // expression and the referent must be heap-allocated. // func (f *Function) lookup(obj types.Object, escaping bool) Value { if v, ok := f.objects[obj]; ok { if escaping { // Walk up the chain of Captures. x := v for { if c, ok := x.(*Capture); ok { x = c.Outer } else { break } } // By construction, all captures are ultimately Allocs in the // naive SSA form. Parameters are pre-spilled to the stack. x.(*Alloc).Heap = true } return v // function-local var (address) } // Definition must be in an enclosing function; // plumb it through intervening closures. if f.Enclosing == nil { panic("no Value for type.Object " + obj.GetName()) } v := &Capture{f.Enclosing.lookup(obj, true)} // escaping f.objects[obj] = v f.FreeVars = append(f.FreeVars, v) return v }
// equalObj reports how x and y differ. They are assumed to belong to // different universes so cannot be compared directly. func equalObj(x, y types.Object) error { if reflect.TypeOf(x) != reflect.TypeOf(y) { return fmt.Errorf("%T vs %T", x, y) } xt := x.Type() yt := y.Type() switch x.(type) { case *types.Var, *types.Func: // ok case *types.Const: xval := x.(*types.Const).Val() yval := y.(*types.Const).Val() // Use string comparison for floating-point values since rounding is permitted. if constant.Compare(xval, token.NEQ, yval) && !(xval.Kind() == constant.Float && xval.String() == yval.String()) { return fmt.Errorf("unequal constants %s vs %s", xval, yval) } case *types.TypeName: xt = xt.Underlying() yt = yt.Underlying() default: return fmt.Errorf("unexpected %T", x) } return equalType(xt, yt) }
func (p *exporter) obj(obj types.Object) { switch obj := obj.(type) { case *types.Const: p.tag(constTag) p.pos(obj) p.qualifiedName(obj) p.typ(obj.Type()) p.value(obj.Val()) case *types.TypeName: p.tag(typeTag) p.typ(obj.Type()) case *types.Var: p.tag(varTag) p.pos(obj) p.qualifiedName(obj) p.typ(obj.Type()) case *types.Func: p.tag(funcTag) p.pos(obj) p.qualifiedName(obj) sig := obj.Type().(*types.Signature) p.paramList(sig.Params(), sig.Variadic()) p.paramList(sig.Results(), false) default: log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj) } }
//getFullName is returning unique name of obj. func getFullName(obj types.Object, ctx *getDefinitionsContext, isType bool) string { if obj == nil { return "" } if isType { return obj.Type().String() } result := "" switch obj.(type) { case *types.Func: f := obj.(*types.Func) r := strings.NewReplacer("(", "", "*", "", ")", "") result = r.Replace(f.FullName()) default: if obj.Pkg() != nil { result += obj.Pkg().Path() result += "." } if packageName, ok := ctx.structs[posToStr(ctx.fset, obj.Pos())]; ok { result += packageName result += "." } result += obj.Name() } return result }
func (p *exporter) qualifiedName(obj types.Object) { if obj == nil { p.string("") return } p.string(obj.Name()) p.pkg(obj.Pkg(), false) }
func (p *exporter) fileLine(obj types.Object) (file string, line int) { if p.fset != nil { pos := p.fset.Position(obj.Pos()) file = pos.Filename line = pos.Line } return }
// 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 }
func (v *visitor) decl(obj types.Object) { key := getKey(obj) if _, ok := v.uses[key]; !ok { v.uses[key] = 0 } if _, ok := v.positions[key]; !ok { v.positions[key] = v.prog.Fset.Position(obj.Pos()) } }
// VName returns a VName for obj relative to that of its package. func (pi *PackageInfo) VName(obj types.Object) *spb.VName { sig := pi.Signature(obj) base := pi.VNames[obj.Pkg()] if base == nil { return govname.ForBuiltin(sig) } vname := proto.Clone(base).(*spb.VName) vname.Signature = sig return vname }
func joinQuery(pkg *types.Package, parent types.Object, obj types.Object, suffix string) string { var args []string args = append(args, pkg.Name()) if parent != nil { args = append(args, parent.Name()) } args = append(args, nameExported(obj.Name())) return strings.Join(args, ".") + suffix }
// same reports whether x and y are identical, or both are PkgNames // that import the same Package. // func sameObj(x, y types.Object) bool { if x == y { return true } if x, ok := x.(*types.PkgName); ok { if y, ok := y.(*types.PkgName); ok { return x.Imported() == y.Imported() } } return false }
func (p *importer) declare(obj types.Object) { pkg := obj.Pkg() if alt := pkg.Scope().Insert(obj); alt != nil { // This could only trigger if we import a (non-type) object a second time. // This should never happen because 1) we only import a package once; and // b) we ignore compiler-specific export data which may contain functions // whose inlined function bodies refer to other functions that were already // imported. // (See also the comment in cmd/compile/internal/gc/bimport.go importer.obj, // switch case importing functions). panic(fmt.Sprintf("%s already declared", alt.Name())) } }
func newObject(pkg *build.Package, obj types.Object, parent types.Object) *identifier { if !obj.Exported() { panic("Only exported objects") } if v, ok := obj.(*types.Var); ok && v.IsField() && parent == nil { panic("Expected a non nil parent") } return &identifier{ buildPkg: pkg, parent: parent, this: obj, usedBy: make([]token.Position, 0), } }
func (b *candidateCollector) asCandidate(obj types.Object) Candidate { objClass := classifyObject(obj) var typ types.Type switch objClass { case "const", "func", "var": typ = obj.Type() case "type": typ = obj.Type().Underlying() } var typStr string switch t := typ.(type) { case *types.Interface: typStr = "interface" case *types.Struct: typStr = "struct" default: if _, isBuiltin := obj.(*types.Builtin); isBuiltin { typStr = builtinTypes[obj.Name()] } else if t != nil { typStr = types.TypeString(t, b.qualify) } } return Candidate{ Class: objClass, Name: obj.Name(), Type: typStr, } }
func (r *renamer) checkInLocalScope(from types.Object) { info := r.packages[from.Pkg()] // Is this object an implicit local var for a type switch? // Each case has its own var, whose position is the decl of y, // but Ident in that decl does not appear in the Uses map. // // switch y := x.(type) { // Defs[Ident(y)] is undefined // case int: print(y) // Implicits[CaseClause(int)] = Var(y_int) // case string: print(y) // Implicits[CaseClause(string)] = Var(y_string) // } // var isCaseVar bool for syntax, obj := range info.Implicits { if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() { isCaseVar = true r.check(obj) } } r.checkInLexicalScope(from, info) // Finally, if this was a type switch, change the variable y. if isCaseVar { _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...] } }
func createDef(obj types.Object, ident *ast.Ident, ctx *getDefinitionsContext, isType bool) *Definition { fullName := getFullName(obj, ctx, isType) if def, ok := ctx.defs[fullName]; ok { return def } def := new(Definition) def.Name = fullName def.Pkg = obj.Pkg() def.IsExported = obj.Exported() def.TypeOf = reflect.TypeOf(obj) def.SimpleName = obj.Name() def.Usages = make([]*Usage, 0) def.InterfacesDefs = make([]*Definition, 0) if ident != nil { position := ctx.fset.Position(ident.Pos()) def.File = position.Filename def.Line = position.Line def.Offset = position.Offset def.Col = position.Column } if !types.IsInterface(obj.Type()) { fillInterfaces(def, obj, ctx) } ctx.defs[def.Name] = def logDefinition(def, obj, ident, ctx) return def }
func addInterface(obj types.Object, ident *ast.Ident, ctx *getDefinitionsContext) { interfac := obj.Type().Underlying().(*types.Interface) def := createDef(obj, ident, ctx, true) updateGetDefinitionsContext(ctx, def, ident, obj) util.Debug("adding interface [%s] [%v] [%v] [%v]", def.Name, def.Pkg, obj.Type().Underlying(), obj.Type()) //Adding all methods of interface for i := 0; i < interfac.NumMethods(); i++ { f := interfac.Method(i) def := createDef(f, nil, ctx, false) util.Debug("\tadding method [%v] [%s]", f, def.Name) updateGetDefinitionsContext(ctx, def, ident, f) } }
func getKey(obj types.Object) object { if obj == nil { return object{} } pkg := obj.Pkg() pkgPath := "" if pkg != nil { pkgPath = pkg.Path() } return object{ pkgPath: pkgPath, name: obj.Name(), } }
func (p *exporter) obj(obj types.Object) { switch obj := obj.(type) { case *types.Const: p.tag(constTag) p.pos(obj) p.qualifiedName(obj) p.typ(obj.Type()) p.value(obj.Val()) case *types.TypeName: p.tag(typeTag) p.typ(obj.Type()) case *types.Var: p.tag(varTag) p.pos(obj) p.qualifiedName(obj) p.typ(obj.Type()) case *types.Func: p.tag(funcTag) p.pos(obj) p.qualifiedName(obj) sig := obj.Type().(*types.Signature) p.paramList(sig.Params(), sig.Variadic()) p.paramList(sig.Results(), false) // Alias-related code. Keep for now. // case *types_Alias: // // make sure the original is exported before the alias // // (if the alias declaration was invalid, orig will be nil) // orig := original(obj) // if orig != nil && !p.reexported[orig] { // p.obj(orig) // p.reexported[orig] = true // } // p.tag(aliasTag) // p.pos(obj) // p.string(obj.Name()) // p.qualifiedName(orig) default: log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj) } }
func (p *importer) declare(obj types.Object) { pkg := obj.Pkg() if alt := pkg.Scope().Insert(obj); alt != nil { // This can only trigger if we import a (non-type) object a second time. // Excluding aliases, this cannot happen because 1) we only import a package // once; and b) we ignore compiler-specific export data which may contain // functions whose inlined function bodies refer to other functions that // were already imported. // However, aliases require reexporting the original object, so we need // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, // method importer.obj, switch case importing functions). // Note that the original itself cannot be an alias. if !sameObj(obj, alt) { errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) } } }
func printTargetObj(obj types.Object) { if obj != nil && obj.Pos() != token.NoPos { fmt.Println(posPrinter{obj}) return } if *godef != "" { cmd := exec.Command(*godef, os.Args[1:]...) cmd.Stdin = bytes.NewReader(fileBody) b, err := cmd.Output() if err != nil { fail() } os.Stdout.Write(b) return } fail() }
func isParam(ctx Ctx, fn *types.Func, obj types.Object) bool { params := getParameters(ctx, fn) for _, p := range params { if p.v.Id() == obj.Id() { return true } } return false }
func objectKind(obj types.Object) string { switch obj := obj.(type) { case *types.PkgName: return "imported package name" case *types.TypeName: return "type" case *types.Var: if obj.IsField() { return "field" } case *types.Func: if obj.Type().(*types.Signature).Recv() != nil { return "method" } } // label, func, var, const return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) }