Exemple #1
0
// checkSelection checks that all uses and selections that resolve to
// the specified object would continue to do so after the renaming.
func (r *Unexporter) checkSelections(objsToUpdate map[types.Object]string, from types.Object, to string) {
	for pkg, info := range r.packages {
		if id := someUse(info, from); id != nil {
			if !r.checkExport(id, pkg, from, to) {
				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(), 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(objsToUpdate, from, to, delta, syntax, obj)
					return
				}

			} else if sel.Obj().Name() == 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(objsToUpdate, from, to, -delta, syntax, sel.Obj())
					return
				}
			}
		}
	}
}
Exemple #2
0
func (u *Unexporter) isInterfaceMethod(e Export) map[string][]*types.Package {
	if _, ok := e.obj.(*types.Func); !ok {
		return nil
	}
	f := e.obj.(*types.Func)
	r := f.Type().(*types.Signature).Recv()
	if r == nil {
		return nil
	}

	if u.f == nil {
		calculateConstraints(u)
	}

	interfaces := make(map[string][]*types.Package)
	for constraint := range u.f {
		if constraint.RHS == r.Type() && types.IsInterface(constraint.LHS) {
			sure, _, _ := types.LookupFieldOrMethod(constraint.LHS, true, f.Pkg(), f.Name())
			if sure != nil {
				interfaces[constraint.LHS.String()] = append(interfaces[constraint.LHS.String()], f.Pkg())
			}
		}
	}

	return interfaces
}
Exemple #3
0
func (g *Grapher) buildStructFields(pkgInfo *loader.PackageInfo) {
	for _, obj := range pkgInfo.Defs {
		if tn, ok := obj.(*types.TypeName); ok {
			typ := tn.Type().Underlying()
			if st, ok := typ.(*types.Struct); ok {
				for i := 0; i < st.NumFields(); i++ {
					sf := st.Field(i)
					g.structFields[sf] = &structField{sf, tn.Type()}
				}
			}
		}
	}

	for selExpr, sel := range pkgInfo.Selections {
		switch sel.Kind() {
		case types.FieldVal:
			rt := derefType(sel.Recv())
			var pkg *types.Package
			switch rt := rt.(type) {
			case *types.Named:
				pkg = rt.Obj().Pkg()
			case *types.Struct:
				pkg = sel.Obj().Pkg()
			default:
				panic("unhandled field recv type " + rt.String())
			}
			sfobj, _, _ := types.LookupFieldOrMethod(derefType(sel.Recv()), false, pkg, selExpr.Sel.Name)

			// Record that this field is in this struct so we can construct the
			// right def path to the field.
			sf, _ := sfobj.(*types.Var)
			g.structFields[sf] = &structField{sf, rt}
		}
	}
}
Exemple #4
0
func main() {
	flag.Parse()
	exitStatus := 0
	importPaths := gotool.ImportPaths(flag.Args())
	if len(importPaths) == 0 {
		importPaths = []string{"."}
	}
	for _, pkgPath := range importPaths {
		visitor := &visitor{
			info: types.Info{
				Types:      make(map[ast.Expr]types.TypeAndValue),
				Defs:       make(map[*ast.Ident]types.Object),
				Selections: make(map[*ast.SelectorExpr]*types.Selection),
			},

			m:    make(map[types.Type]map[string]int),
			skip: make(map[types.Type]struct{}),
		}
		fset, astFiles := check.ASTFilesForPackage(pkgPath, *loadTestFiles)
		imp := importer.New()
		// Preliminary cgo support.
		imp.Config = importer.Config{UseGcFallback: true}
		config := types.Config{Import: imp.Import}
		var err error
		visitor.pkg, err = config.Check(pkgPath, fset, astFiles, &visitor.info)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %v\n", pkgPath, err)
			continue
		}
		for _, f := range astFiles {
			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, visitor.pkg, fieldName)
					if fieldName == "XMLName" {
						if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg().Path() == "encoding/xml" {
							continue
						}
					}
					pos := 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)
}
Exemple #5
0
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)
}
Exemple #7
0
// checkSelection checks that all uses and selections that resolve to
// the specified object would continue to do so after the renaming.
func (e *Export) checkSelections(from types.Object, to string) {
	info := e.u.pkgInfo
	if id := someUse(info, from); id != nil {
		e.Conflicting = true
		return
	}

	for _, sel := range info.Selections {
		if sel.Obj() == from {
			if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), true, from.Pkg(), 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
				}
				e.Conflicting = true
				return
			}

		} else if sel.Obj().Name() == to {
			if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), true, 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
				}
				e.Conflicting = true
				return
			}
		}
	}
}
Exemple #8
0
// checkStructField checks that the field renaming will not cause
// conflicts at its declaration, or ambiguity or changes to any selection.
func (e *Export) checkStructField(from *types.Var, to string) {
	// Check that the struct declaration is free of field conflicts,
	// and field/method conflicts.
	t := getEnclosingStruct(from)
	if t != t.Underlying() {
		// This struct is also a named type.
		// We must check for direct (non-promoted) field/field
		// and method/field conflicts.
		_, indices, _ := types.LookupFieldOrMethod(t, true, e.u.pkgInfo.Pkg, to)
		if len(indices) == 1 {
			e.Conflicting = true
			return
		}
	} else {
		// This struct is not a named type.
		// We need only check for direct (non-promoted) field/field conflicts.
		T := t.Underlying().(*types.Struct)
		for i := 0; i < T.NumFields(); i++ {
			if prev := T.Field(i); prev.Name() == to {
				e.Conflicting = true
				return
			}
		}
	}

	// 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 {
			e.check(named.Obj(), to)
		} else if named, ok := deref(from.Type()).(*types.Named); ok {
			e.check(named.Obj(), to)
		}
	}

	// Check integrity of existing (field and method) selections.
	e.checkSelections(from, to)
}
Exemple #9
0
// 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 *Unexporter) checkMethod(objsToUpdate map[types.Object]string, from *types.Func, to string) {
	// e.g. error.Error
	if from.Pkg() == nil {
		r.warn(from, 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(), to)
		if prev != nil {
			r.warn(from,
				r.errorf(from.Pos(), "renaming this interface method %q to %q",
					from.Name(), 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(), to)
					if t == nil {
						continue
					}
					r.warn(from,
						r.errorf(from.Pos(), "renaming this interface method %q to %q",
							from.Name(), 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(), 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

					rename := r.errorf(from.Pos(), "renaming this method %q to %q",
						from.Name(), to)
					if delta == 0 {
						// analogous to same-block conflict
						r.warn(from, rename,
							r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous",
								to, key.RHS, key.LHS),
							r.errorf(rto.Pos(), "\twith (%s).%s",
								recv(rto).Type(), to))
					} else {
						// analogous to super-block conflict
						r.warn(from, rename,
							r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s",
								to, key.RHS, key.LHS),
							r.errorf(coupled.Pos(), "\tfrom (%s).%s",
								recv(coupled).Type(), to),
							r.errorf(rto.Pos(), "\tto (%s).%s",
								recv(rto).Type(), to))
					}
					return // one error is enough
				}
			}

			if !r.changeMethods {
				// This should be unreachable.
				r.warn(from,
					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(objsToUpdate, coupled, to)
		}
	} else {
		// Concrete method

		// declaration
		prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), to)
		if prev != nil && len(indices) == 1 {
			r.warn(from,
				r.errorf(from.Pos(), "renaming this method %q to %q",
					from.Name(), 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 {
				rename := r.errorf(from.Pos(), "renaming this method %q to %q",
					from.Name(), 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.warn(from, rename,
					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(objsToUpdate, imeth, to)
		}
	}

	// Check integrity of existing (field and method) selections.
	// We skip this if there were errors above, to avoid redundant errors.
	r.checkSelections(objsToUpdate, from, to)
}
Exemple #10
0
// checkStructField checks that the field renaming will not cause
// conflicts at its declaration, or ambiguity or changes to any selection.
func (r *Unexporter) checkStructField(objsToUpdate map[types.Object]string, from *types.Var, to string) {
	// 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, to)
		if len(indices) == 1 {
			r.warn(from,
				r.errorf(from.Pos(), "renaming this field %q to %q",
					from.Name(), 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() == to {
				r.warn(from,
					r.errorf(from.Pos(), "renaming this field %q to %q",
						from.Name(), 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(objsToUpdate, named.Obj(), to)
		} else if named, ok := deref(from.Type()).(*types.Named); ok {
			r.check(objsToUpdate, named.Obj(), to)
		}
	}

	// Check integrity of existing (field and method) selections.
	r.checkSelections(objsToUpdate, from, to)
}
Exemple #11
0
// 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
}
Exemple #12
0
func (e *Export) checkMethod(from *types.Func, to string) {
	// e.g. error.Error
	if from.Pkg() == nil {
		e.Conflicting = true
		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(), to)
		if prev != nil {
			e.Conflicting = true
			return
		}

		// Check all interfaces that embed this one for
		// declaration conflicts too.
		for _, info := range e.u.prog.AllPackages {
			// 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(), to)
					if t == nil {
						continue
					}
					e.Conflicting = true
					return
				}
			}
		}

		// 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 e.u.satisfy() {
			// key = (lhs, rhs) where lhs is always an interface.

			lsel := e.u.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
			if lsel == nil {
				continue
			}
			rmethods := e.u.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(), to)
				if rtosel != nil {
					delta := len(rsel.Index()) - len(rtosel.Index())
					if delta < 0 {
						continue // no ambiguity
					}
					e.Conflicting = true
					return
				}
			}

			// Rename the coupled method to preserve assignability.
			e.check(coupled, to)
		}
	}

	// Concrete method

	// declaration
	prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), to)
	if prev != nil && len(indices) == 1 {
		e.Conflicting = true
		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 e.u.satisfy() {
		// key = (lhs, rhs) where lhs is always an interface.
		if isInterface(key.RHS) {
			continue
		}
		rsel := e.u.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
		if rsel == nil || rsel.Obj() != from {
			continue // rhs does not have the method
		}
		lsel := e.u.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
		if lsel == nil {
			continue
		}
		imeth := lsel.Obj().(*types.Func)
		e.check(imeth, to)
	}

	// Check integrity of existing (field and method) selections.
	// We skip this if there were errors above, to avoid redundant errors.
	e.checkSelections(from, to)
}
Exemple #13
0
// 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
}
Exemple #14
0
// 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 !f.IsTest() {
		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
}
Exemple #15
0
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)
}
Exemple #16
0
// 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)
}