// 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 } } } } }
func ext۰sync۰Pool۰Get(fr *frame, args []value) value { Pool := fr.i.prog.ImportedPackage("sync").Type("Pool").Object() _, newIndex, _ := types.LookupFieldOrMethod(Pool.Type(), false, Pool.Pkg(), "New") if New := (*args[0].(*value)).(structure)[newIndex[0]]; New != nil { return call(fr.i, fr, 0, New, nil) } return nil }
// checkMethod performs safety checks for renaming a method. // There are three hazards: // - declaration conflicts // - selection ambiguity/changes // - entailed renamings of assignable concrete/interface types (for now, just reject) func (r *renamer) checkMethod(from *types.Func) { // e.g. error.Error if from.Pkg() == nil { r.errorf(from.Pos(), "you cannot rename built-in method %s", from) return } // As always, having to support concrete methods with pointer // and non-pointer receivers, and named vs unnamed types with // methods, makes tooling fun. // ASSIGNABILITY // // For now, if any method renaming breaks a required // assignability to another type, we reject it. // // TODO(adonovan): probably we should compute the entailed // renamings so that an interface method renaming causes // concrete methods to change too. But which ones? // // There is no correct answer, only heuristics, because Go's // "duck typing" doesn't distinguish intentional from contingent // assignability. There are two obvious approaches: // // (1) Update the minimum set of types to preserve the // assignability of types all syntactic assignments // (incl. implicit ones in calls, returns, sends, etc). // The satisfy.Finder enumerates these. // This is likely to be an underapproximation. // // (2) Update all types that are assignable to/from the changed // type. This requires computing the "implements" relation // for all pairs of types (as godoc and oracle do). // This is likely to be an overapproximation. // // If a concrete type is renamed, we probably do not want to // rename corresponding interfaces; interface renamings should // probably be initiated at the interface. (But what if a // concrete type implements multiple interfaces with the same // method? Then the user is stuck.) // // We need some experience before we decide how to implement this. // Check for conflict at point of declaration. // Check to ensure preservation of assignability requirements. recv := from.Type().(*types.Signature).Recv().Type() if isInterface(recv) { // Abstract method // declaration prev, _, _ := types.LookupFieldOrMethod(recv, false, from.Pkg(), r.to) if prev != nil { r.errorf(from.Pos(), "renaming this interface method %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this method") return } // Check all interfaces that embed this one for // declaration conflicts too. for _, info := range r.packages { // Start with named interface types (better errors) for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) { f, _, _ := types.LookupFieldOrMethod( obj.Type(), false, from.Pkg(), from.Name()) if f == nil { continue } t, _, _ := types.LookupFieldOrMethod( obj.Type(), false, from.Pkg(), r.to) if t == nil { continue } r.errorf(from.Pos(), "renaming this interface method %q to %q", from.Name(), r.to) r.errorf(t.Pos(), "\twould conflict with this method") r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name()) } } // Now look at all literal interface types (includes named ones again). for e, tv := range info.Types { if e, ok := e.(*ast.InterfaceType); ok { _ = e _ = tv.Type.(*types.Interface) // TODO(adonovan): implement same check as above. } } } // assignability for T := range r.findAssignments(recv) { if obj, _, _ := types.LookupFieldOrMethod(T, false, from.Pkg(), from.Name()); obj == nil { continue } r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) var pos token.Pos var other string if named, ok := T.(*types.Named); ok { pos = named.Obj().Pos() other = named.Obj().Name() } else { pos = from.Pos() other = T.String() } r.errorf(pos, "\twould make %s no longer assignable to it", other) return } } else { // Concrete method // declaration prev, indices, _ := types.LookupFieldOrMethod(recv, true, from.Pkg(), r.to) if prev != nil && len(indices) == 1 { r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this %s", objectKind(prev)) return } // assignability (of both T and *T) recvBase := deref(recv) for _, R := range []types.Type{recvBase, types.NewPointer(recvBase)} { for I := range r.findAssignments(R) { if obj, _, _ := types.LookupFieldOrMethod(I, true, from.Pkg(), from.Name()); obj == nil { continue } r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) var pos token.Pos var iface string if named, ok := I.(*types.Named); ok { pos = named.Obj().Pos() iface = "interface " + named.Obj().Name() } else { pos = from.Pos() iface = I.String() } r.errorf(pos, "\twould make it no longer assignable to %s", iface) return // one is enough } } } // Check integrity of existing (field and method) selections. // We skip this if there were errors above, to avoid redundant errors. r.checkSelections(from) }
// checkStructField checks that the field renaming will not cause // conflicts at its declaration, or ambiguity or changes to any selection. func (r *renamer) checkStructField(from *types.Var) { // Check that the struct declaration is free of field conflicts, // and field/method conflicts. // go/types offers no easy way to get from a field (or interface // method) to its declaring struct (or interface), so we must // ascend the AST. info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) // path is [Ident Field FieldList StructType ... File]. Can't fail. // Ascend past parens (unlikely). i := 4 for { _, ok := path[i].(*ast.ParenExpr) if !ok { break } i++ } if spec, ok := path[i].(*ast.TypeSpec); ok { // This struct is also a named type. // We must check for direct (non-promoted) field/field // and method/field conflicts. named := info.Defs[spec.Name].Type() prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to) if len(indices) == 1 { r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this %s", objectKind(prev)) return // skip checkSelections to avoid redundant errors } } else { // This struct is not a named type. // We need only check for direct (non-promoted) field/field conflicts. T := info.Types[path[3].(*ast.StructType)].Type.Underlying().(*types.Struct) for i := 0; i < T.NumFields(); i++ { if prev := T.Field(i); prev.Name() == r.to { r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this field") return // skip checkSelections to avoid redundant errors } } } // Renaming an anonymous field requires renaming the type too. e.g. // print(s.T) // if we rename T to U, // type T int // this and // var s struct {T} // this must change too. if from.Anonymous() { if named, ok := from.Type().(*types.Named); ok { r.check(named.Obj()) } else if named, ok := deref(from.Type()).(*types.Named); ok { r.check(named.Obj()) } } // Check integrity of existing (field and method) selections. r.checkSelections(from) }
// hasMethod reports whether the type contains a method with the given name. // It is part of the workaround for Formatters and should be deleted when // that workaround is no longer necessary. // TODO: This could be better once issue 6259 is fixed. func (f *File) hasMethod(typ types.Type, name string) bool { obj, _, _ := types.LookupFieldOrMethod(typ, f.pkg.typesPkg, name) _, ok := obj.(*types.Func) return ok }
// On success, findObjects returns the list of objects named // spec.fromName matching the spec. On success, the result has exactly // one element unless spec.searchFor!="", in which case it has at least one // element. // func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) { if spec.pkgMember == "" { if spec.searchFor == "" { panic(spec) } objects := searchDefs(&info.Info, spec.searchFor) if objects == nil { return nil, fmt.Errorf("no object %q declared in package %q", spec.searchFor, info.Pkg.Path()) } return objects, nil } pkgMember := info.Pkg.Scope().Lookup(spec.pkgMember) if pkgMember == nil { return nil, fmt.Errorf("package %q has no member %q", info.Pkg.Path(), spec.pkgMember) } var searchFunc *types.Func if spec.typeMember == "" { // package member if spec.searchFor == "" { return []types.Object{pkgMember}, nil } // Search within pkgMember, which must be a function. searchFunc, _ = pkgMember.(*types.Func) if searchFunc == nil { return nil, fmt.Errorf("cannot search for %q within %s %q", spec.searchFor, objectKind(pkgMember), pkgMember) } } else { // field/method of type // e.g. (encoding/json.Decoder).Decode // or ::x within it. tName, _ := pkgMember.(*types.TypeName) if tName == nil { return nil, fmt.Errorf("%s.%s is a %s, not a type", info.Pkg.Path(), pkgMember.Name(), objectKind(pkgMember)) } // search within named type. obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember) if obj == nil { return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s", spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name()) } if spec.searchFor == "" { return []types.Object{obj}, nil } searchFunc, _ = obj.(*types.Func) if searchFunc == nil { return nil, fmt.Errorf("cannot search for local name %q within %s (%s.%s).%s; need a function", spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(), obj.Name()) } if isInterface(tName.Type()) { return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s", spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name()) } } // -- search within function or method -- decl := funcDecl(info, searchFunc) if decl == nil { return nil, fmt.Errorf("cannot find syntax for %s", searchFunc) // can't happen? } var objects []types.Object for _, obj := range searchDefs(&info.Info, spec.searchFor) { // We use positions, not scopes, to determine whether // the obj is within searchFunc. This is clumsy, but the // alternative, using the types.Scope tree, doesn't // account for non-lexical objects like fields and // interface methods. if decl.Pos() <= obj.Pos() && obj.Pos() < decl.End() && obj != searchFunc { objects = append(objects, obj) } } if objects == nil { return nil, fmt.Errorf("no local definition of %q within %s", spec.searchFor, searchFunc) } return objects, nil }
// hasMethod reports whether the type contains a method with the given name. // It is part of the workaround for Formatters and should be deleted when // that workaround is no longer necessary. // TODO: This could be better once issue 6259 is fixed. func (f *File) hasMethod(typ types.Type, name string) bool { // assume we have an addressable variable of type typ obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name) _, ok := obj.(*types.Func) return ok }