// 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 } } } } }
// Ident looks up a definition by its import path and identifier names. // // buildCtx can be used to control GOPATH, GOOS, GOARCH, build tags, // and such. If buildCtx is nil, go/build.Default is used. func Ident(lookup string, buildCtx *build.Context) (*token.Position, error) { if buildCtx == nil { buildCtx = &build.Default } importPath, lookup := lookup, "" if idx := strings.IndexByte(importPath, '#'); idx >= 0 { importPath, lookup = importPath[:idx], importPath[idx+1:] } idents := strings.Split(lookup, ".") fset := token.NewFileSet() imp := newImporter(buildCtx, fset) pkg, err := imp.Import(importPath) if err != nil { return nil, fmt.Errorf("cannot import path: %q: %v", importPath, err) } obj := pkg.Scope().Lookup(idents[0]) if obj == nil { return nil, fmt.Errorf("identifier not found: %q", idents[0]) } for _, name := range idents[1:] { obj, _, _ = types.LookupFieldOrMethod(obj.Type(), true, pkg, name) if obj == nil { return nil, fmt.Errorf("field or method not found: %q", name) } } p := fset.Position(obj.Pos()) return &p, nil }
func checkExample(fn *ast.FuncDecl, pkg *Package, report reporter) { fnName := fn.Name.Name if params := fn.Type.Params; len(params.List) != 0 { report("%s should be niladic", fnName) } if results := fn.Type.Results; results != nil && len(results.List) != 0 { report("%s should return nothing", fnName) } if filesRun && !includesNonTest { // The coherence checks between a test and the package it tests // will report false positives if no non-test files have // been provided. return } if fnName == "Example" { // Nothing more to do. return } var ( exName = strings.TrimPrefix(fnName, "Example") elems = strings.SplitN(exName, "_", 3) ident = elems[0] obj = lookup(ident, extendedScope(pkg)) ) if ident != "" && obj == nil { // Check ExampleFoo and ExampleBadFoo. report("%s refers to unknown identifier: %s", fnName, ident) // Abort since obj is absent and no subsequent checks can be performed. return } if len(elems) < 2 { // Nothing more to do. return } if ident == "" { // Check Example_suffix and Example_BadSuffix. if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) { report("%s has malformed example suffix: %s", fnName, residual) } return } mmbr := elems[1] if !isExampleSuffix(mmbr) { // Check ExampleFoo_Method and ExampleFoo_BadMethod. if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil { report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr) } } if len(elems) == 3 && !isExampleSuffix(elems[2]) { // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix. report("%s has malformed example suffix: %s", fnName, elems[2]) } }
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 }
func (tr *Transformer) matchSelectorExpr(x, y *ast.SelectorExpr) bool { if xobj, ok := tr.wildcardObj(x.X); ok { field := x.Sel.Name yt := tr.info.TypeOf(y.X) o, _, _ := types.LookupFieldOrMethod(yt, true, tr.currentPkg, field) if o != nil { tr.env[xobj.Name()] = y.X // record binding return true } } return tr.matchExpr(x.X, y.X) }
// accessibleFields returns the set of accessible // field selections on a value of type recv. func accessibleFields(recv types.Type, from *types.Package) []describeField { wantField := func(f *types.Var) bool { if !isAccessibleFrom(f, from) { return false } // Check that the field is not shadowed. obj, _, _ := types.LookupFieldOrMethod(recv, true, f.Pkg(), f.Name()) return obj == f } var fields []describeField var visit func(t types.Type, stack []*types.Named) visit = func(t types.Type, stack []*types.Named) { tStruct, ok := deref(t).Underlying().(*types.Struct) if !ok { return } fieldloop: for i := 0; i < tStruct.NumFields(); i++ { f := tStruct.Field(i) // Handle recursion through anonymous fields. if f.Anonymous() { tf := f.Type() if ptr, ok := tf.(*types.Pointer); ok { tf = ptr.Elem() } if named, ok := tf.(*types.Named); ok { // (be defensive) // If we've already visited this named type // on this path, break the cycle. for _, x := range stack { if x == named { continue fieldloop } } visit(f.Type(), append(stack, named)) } } // Save accessible fields. if wantField(f) { fields = append(fields, describeField{ implicits: append([]*types.Named(nil), stack...), field: f, }) } } } visit(recv, nil) return fields }
func TestIssue13898(t *testing.T) { skipSpecialPlatforms(t) // This package only handles gc export data. if runtime.Compiler != "gc" { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) return } // import go/internal/gcimporter which imports go/types partially imports := make(map[string]*types.Package) _, err := Import(imports, "go/internal/gcimporter", ".") if err != nil { t.Fatal(err) } // look for go/types package var goTypesPkg *types.Package for path, pkg := range imports { if path == "go/types" { goTypesPkg = pkg break } } if goTypesPkg == nil { t.Fatal("go/types not found") } // look for go/types.Object type obj := goTypesPkg.Scope().Lookup("Object") if obj == nil { t.Fatal("go/types.Object not found") } typ, ok := obj.Type().(*types.Named) if !ok { t.Fatalf("go/types.Object type is %v; wanted named type", typ) } // lookup go/types.Object.Pkg method m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") if m == nil { t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) } // the method must belong to go/types if m.Pkg().Path() != "go/types" { t.Fatalf("found %v; want go/types", m.Pkg()) } }
func (p *Package) buildTypes() map[string]*Type { if p.Types != nil { return p.Types } p.Types = map[string]*Type{} for _, docT := range p.DocPkg.Types { name := docT.Name if typesT, ok := p.TypesPkg.Scope().Lookup(name).(*types.TypeName); ok { funcs := make(map[string]*Func, len(docT.Funcs)) for _, f := range docT.Funcs { funcs[f.Name] = &Func{ Package: p, Doc: f, Types: p.TypesPkg.Scope().Lookup(f.Name).(*types.Func), } } methods := make(map[string]*Func, len(docT.Methods)) for _, m := range docT.Methods { // Must be found and be *types.Func obj, _, _ := types.LookupFieldOrMethod(typesT.Type(), true, p.TypesPkg, m.Name) methods[m.Name] = &Func{ Package: p, Doc: m, Types: obj.(*types.Func), } } p.Types[name] = &Type{ Package: p, Doc: docT, Types: typesT, Funcs: funcs, Methods: methods, } } } return p.Types }
func implements(t types.Type, interfac *types.Interface, pkg *types.Package) bool { if interfac == nil || t == nil || interfac.Empty() { return false } if types.Implements(t, interfac) { return true } //For some reason, interfaces that comes //already built in (not from sources) are //not working with types.Implements method for i := 0; i < interfac.NumMethods(); i++ { m := interfac.Method(i) obj, _, _ := types.LookupFieldOrMethod(t, true, pkg, m.Name()) if obj == nil { util.Debug("method %s not found in type %v", m.Name(), t) return false } } return true }
// 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 == "" { // If it is an embedded field, return the type of the field. if v, ok := obj.(*types.Var); ok && v.Anonymous() { switch t := v.Type().(type) { case *types.Pointer: return []types.Object{t.Elem().(*types.Named).Obj()}, nil case *types.Named: return []types.Object{t.Obj()}, nil } } 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 }
//buildQueryStrings build names for matching //if onlyExport it's true only query Exported identifier else only Unexported func buildQueryStrings(queries chan *queryIdent, pkg *types.Package, info *types.Info, onlyExport bool) { defer close(queries) var typesDefs []types.Object typesByField := make(map[types.Object]types.Object) interfaceByMethod := make(map[types.Object]types.Object) //get definition of types first for ident, obj := range info.Defs { if obj == nil { continue } if ident.IsExported() != onlyExport { //querying unexport, so we need save typesDefs for finding fields func etc.. if !onlyExport { typesDefs = append(typesDefs, obj) } continue } switch obj.(type) { case *types.TypeName: var typeOfObj string switch t := obj.Type().Underlying().(type) { case *types.Struct: for i := 0; i < t.NumFields(); i++ { field := t.Field(i) typesByField[field] = obj } typeOfObj = "@struct" case *types.Interface: for i := 0; i < t.NumMethods(); i++ { method := t.Method(i) interfaceByMethod[method] = obj } for i := 0; i < t.NumExplicitMethods(); i++ { method := t.ExplicitMethod(i) interfaceByMethod[method] = obj } typeOfObj = "@interface" default: typeOfObj = "@type" } query := pkg.Name() + "." + nameExported(obj.Name()) + typeOfObj queries <- &queryIdent{query, ident} typesDefs = append(typesDefs, obj) } } //get rest of definitions for ident, obj := range info.Defs { if obj == nil { continue } if ident.IsExported() != onlyExport { continue } if sobj, ok := typesByField[obj]; ok { queries <- &queryIdent{joinQuery(pkg, sobj, obj, "@attr"), ident} } else if sobj, ok := interfaceByMethod[obj]; ok { queries <- &queryIdent{joinQuery(pkg, sobj, obj, "@method"), ident} } else { switch t := obj.(type) { case *types.Const: queries <- &queryIdent{joinQuery(pkg, nil, obj, "@const"), ident} case *types.Var: if !t.IsField() { queries <- &queryIdent{joinQuery(pkg, nil, obj, "@var"), ident} } case *types.Func: foundLikeMethod := false for _, tobj := range typesDefs { tfield, _, _ := types.LookupFieldOrMethod(tobj.Type(), true, pkg, obj.Name()) if tfield == nil { continue } if tfield.Pos() == obj.Pos() { queries <- &queryIdent{joinQuery(pkg, tobj, tfield, "@method"), ident} foundLikeMethod = true } } if !foundLikeMethod { queries <- &queryIdent{joinQuery(pkg, nil, obj, "@func"), ident} } default: } } } }
// 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 }
func main() { flag.Parse() exitStatus := 0 importPaths := gotool.ImportPaths(flag.Args()) if len(importPaths) == 0 { importPaths = []string{"."} } ctx := build.Default for _, pkgPath := range importPaths { visitor := &visitor{ m: make(map[types.Type]map[string]int), skip: make(map[types.Type]struct{}), } loadcfg := loader.Config{ Build: &ctx, } rest, err := loadcfg.FromArgs([]string{pkgPath}, *loadTestFiles) if err != nil { fmt.Fprintf(os.Stderr, "could not parse arguments: %s", err) continue } if len(rest) > 0 { fmt.Fprintf(os.Stderr, "unhandled extra arguments: %v", rest) continue } program, err := loadcfg.Load() if err != nil { fmt.Fprintf(os.Stderr, "could not type check: %s", err) continue } pkg := program.InitialPackages()[0] visitor.prog = program visitor.pkg = pkg for _, f := range pkg.Files { ast.Walk(visitor, f) } for t := range visitor.m { if _, skip := visitor.skip[t]; skip { continue } for fieldName, v := range visitor.m[t] { if !*reportExported && ast.IsExported(fieldName) { continue } if v == 0 { field, _, _ := types.LookupFieldOrMethod(t, false, pkg.Pkg, fieldName) if fieldName == "XMLName" { if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg().Path() == "encoding/xml" { continue } } pos := program.Fset.Position(field.Pos()) fmt.Printf("%s: %s:%d:%d: %s.%s\n", pkgPath, pos.Filename, pos.Line, pos.Column, types.TypeString(t, nil), fieldName, ) exitStatus = 1 } } } } os.Exit(exitStatus) }
// checkMethod performs safety checks for renaming a method. // There are three hazards: // - declaration conflicts // - selection ambiguity/changes // - entailed renamings of assignable concrete/interface types. // We reject renamings initiated at concrete methods if it would // change the assignability relation. For renamings of abstract // methods, we rename all methods transitively coupled to it via // assignability. 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 } // ASSIGNABILITY: We reject renamings of concrete methods that // would break a 'satisfy' constraint; but renamings of abstract // methods are allowed to proceed, and we rename affected // concrete and abstract methods as necessary. It is the // initial method that determines the policy. // Check for conflict at point of declaration. // Check to ensure preservation of assignability requirements. R := recv(from).Type() if isInterface(R) { // Abstract method // declaration prev, _, _ := types.LookupFieldOrMethod(R, 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 // // Find the set of concrete or abstract methods directly // coupled to abstract method 'from' by some // satisfy.Constraint, and rename them too. for key := range r.satisfy() { // key = (lhs, rhs) where lhs is always an interface. lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) if lsel == nil { continue } rmethods := r.msets.MethodSet(key.RHS) rsel := rmethods.Lookup(from.Pkg(), from.Name()) if rsel == nil { continue } // If both sides have a method of this name, // and one of them is m, the other must be coupled. var coupled *types.Func switch from { case lsel.Obj(): coupled = rsel.Obj().(*types.Func) case rsel.Obj(): coupled = lsel.Obj().(*types.Func) default: continue } // We must treat concrete-to-interface // constraints like an implicit selection C.f of // each interface method I.f, and check that the // renaming leaves the selection unchanged and // unambiguous. // // Fun fact: the implicit selection of C.f // type I interface{f()} // type C struct{I} // func (C) g() // var _ I = C{} // here // yields abstract method I.f. This can make error // messages less than obvious. // if !isInterface(key.RHS) { // The logic below was derived from checkSelections. rtosel := rmethods.Lookup(from.Pkg(), r.to) if rtosel != nil { rto := rtosel.Obj().(*types.Func) delta := len(rsel.Index()) - len(rtosel.Index()) if delta < 0 { continue // no ambiguity } // TODO(adonovan): record the constraint's position. keyPos := token.NoPos r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) if delta == 0 { // analogous to same-block conflict r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous", r.to, key.RHS, key.LHS) r.errorf(rto.Pos(), "\twith (%s).%s", recv(rto).Type(), r.to) } else { // analogous to super-block conflict r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s", r.to, key.RHS, key.LHS) r.errorf(coupled.Pos(), "\tfrom (%s).%s", recv(coupled).Type(), r.to) r.errorf(rto.Pos(), "\tto (%s).%s", recv(rto).Type(), r.to) } return // one error is enough } } if !r.changeMethods { // This should be unreachable. r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from) r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled) r.errorf(from.Pos(), "\tPlease file a bug report") return } // Rename the coupled method to preserve assignability. r.check(coupled) } } else { // Concrete method // declaration prev, indices, _ := types.LookupFieldOrMethod(R, 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 // // Find the set of abstract methods coupled to concrete // method 'from' by some satisfy.Constraint, and rename // them too. // // Coupling may be indirect, e.g. I.f <-> C.f via type D. // // type I interface {f()} // type C int // type (C) f() // type D struct{C} // var _ I = D{} // for key := range r.satisfy() { // key = (lhs, rhs) where lhs is always an interface. if isInterface(key.RHS) { continue } rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name()) if rsel == nil || rsel.Obj() != from { continue // rhs does not have the method } lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) if lsel == nil { continue } imeth := lsel.Obj().(*types.Func) // imeth is the abstract method (e.g. I.f) // and key.RHS is the concrete coupling type (e.g. D). if !r.changeMethods { r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) var pos token.Pos var iface string I := recv(imeth).Type() 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 %s no longer assignable to %s", key.RHS, iface) r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", I, from.Name()) return // one error is enough } // Rename the coupled interface method to preserve assignability. r.check(imeth) } } // 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 matches this pattern: // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File] // Ascend to FieldList. var i int for { if _, ok := path[i].(*ast.FieldList); ok { break } i++ } i++ tStruct := path[i].(*ast.StructType) i++ // Ascend past parens (unlikely). 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[tStruct].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) }
// checkExample walks the documentation example functions checking for common // mistakes of misnamed functions, failure to map functions to existing // identifiers, etc. func checkExample(f *File, node ast.Node) { if !strings.HasSuffix(f.name, "_test.go") { return } var ( pkg = f.pkg pkgName = pkg.typesPkg.Name() scopes = []*types.Scope{pkg.typesPkg.Scope()} lookup = func(name string) types.Object { for _, scope := range scopes { if o := scope.Lookup(name); o != nil { return o } } return nil } ) if strings.HasSuffix(pkgName, "_test") { // Treat 'package foo_test' as an alias for 'package foo'. var ( basePkg = strings.TrimSuffix(pkgName, "_test") pkg = f.pkg ) for _, p := range pkg.typesPkg.Imports() { if p.Name() == basePkg { scopes = append(scopes, p.Scope()) break } } } fn, ok := node.(*ast.FuncDecl) if !ok { // Ignore non-functions. return } var ( fnName = fn.Name.Name report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) } ) if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") { // Ignore methods and types not named "Example". return } if params := fn.Type.Params; len(params.List) != 0 { report("%s should be niladic", fnName) } if results := fn.Type.Results; results != nil && len(results.List) != 0 { report("%s should return nothing", fnName) } if fnName == "Example" { // Nothing more to do. return } if filesRun && !includesNonTest { // The coherence checks between a test and the package it tests // will report false positives if no non-test files have // been provided. return } var ( exName = strings.TrimPrefix(fnName, "Example") elems = strings.SplitN(exName, "_", 3) ident = elems[0] obj = lookup(ident) ) if ident != "" && obj == nil { // Check ExampleFoo and ExampleBadFoo. report("%s refers to unknown identifier: %s", fnName, ident) // Abort since obj is absent and no subsequent checks can be performed. return } if elemCnt := strings.Count(exName, "_"); elemCnt == 0 { // Nothing more to do. return } mmbr := elems[1] if ident == "" { // Check Example_suffix and Example_BadSuffix. if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) { report("%s has malformed example suffix: %s", fnName, residual) } return } if !isExampleSuffix(mmbr) { // Check ExampleFoo_Method and ExampleFoo_BadMethod. if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil { report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr) } } if len(elems) == 3 && !isExampleSuffix(elems[2]) { // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix. report("%s has malformed example suffix: %s", fnName, elems[2]) } return }