diff --git a/unused/implements.go b/unused/implements.go new file mode 100644 index 000000000..78a545639 --- /dev/null +++ b/unused/implements.go @@ -0,0 +1,79 @@ +package unused + +import "go/types" + +// lookupMethod returns the index of and method with matching package and name, or (-1, nil). +func lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) { + if name != "_" { + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + if sameId(m, pkg, name) { + return i, m + } + } + } + return -1, nil +} + +func sameId(obj types.Object, pkg *types.Package, name string) bool { + // spec: + // "Two identifiers are different if they are spelled differently, + // or if they appear in different packages and are not exported. + // Otherwise, they are the same." + if name != obj.Name() { + return false + } + // obj.Name == name + if obj.Exported() { + return true + } + // not exported, so packages must be the same (pkg == nil for + // fields in Universe scope; this can only happen for types + // introduced via Eval) + if pkg == nil || obj.Pkg() == nil { + return pkg == obj.Pkg() + } + // pkg != nil && obj.pkg != nil + return pkg.Path() == obj.Pkg().Path() +} + +func (c *Checker) implements(V types.Type, T *types.Interface) bool { + // fast path for common case + if T.Empty() { + return true + } + + if ityp, _ := V.Underlying().(*types.Interface); ityp != nil { + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + _, obj := lookupMethod(ityp, m.Pkg(), m.Name()) + switch { + case obj == nil: + return false + case !types.Identical(obj.Type(), m.Type()): + return false + } + } + return true + } + + // A concrete type implements T if it implements all methods of T. + ms := c.msCache.MethodSet(V) + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + sel := ms.Lookup(m.Pkg(), m.Name()) + if sel == nil { + return false + } + + f, _ := sel.Obj().(*types.Func) + if f == nil { + return false + } + + if !types.Identical(f.Type(), m.Type()) { + return false + } + } + return true +} diff --git a/unused/unused.go b/unused/unused.go index c16f3fe3a..b1dbd6f54 100644 --- a/unused/unused.go +++ b/unused/unused.go @@ -265,6 +265,7 @@ func (c *Checker) Check(prog *lint.Program) []Unused { unused = append(unused, Unused{Obj: obj, Position: pos}) } + return unused } @@ -557,13 +558,13 @@ func (c *Checker) processTypes(pkg *lint.Pkg) { switch obj.Underlying().(type) { case *types.Interface: // pointers to interfaces have no methods, only checking non-pointer - if !types.Implements(obj, iface) { + if !c.implements(obj, iface) { continue namedLoop } default: // pointer receivers include the method set of non-pointer receivers, // only checking pointer - if !types.Implements(objPtr, iface) { + if !c.implements(objPtr, iface) { continue namedLoop } }