diff --git a/unused/testdata/src/e/e.go b/unused/testdata/src/e/e.go new file mode 100644 index 0000000..13b258e --- /dev/null +++ b/unused/testdata/src/e/e.go @@ -0,0 +1,14 @@ +package shadow + +type Notifier interface { + Notify() error +} + +func NotifyAll[n any](items []n) error { + for _, item := range items { + if notifier, ok := any(item).(Notifier); ok { + _ = notifier.Notify() + } + } + return nil +} \ No newline at end of file diff --git a/unused/unused.go b/unused/unused.go index 743e294..a6ffb05 100644 --- a/unused/unused.go +++ b/unused/unused.go @@ -4,6 +4,7 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "os" "slices" "strings" @@ -35,8 +36,9 @@ func newAnalyzer() *analysis.Analyzer { } type ifaceEntry struct { - ts *ast.TypeSpec - decl *ast.GenDecl + ifaceName string + ts *ast.TypeSpec + decl *ast.GenDecl } type runner struct { @@ -62,7 +64,7 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) // Collect all interface type declarations - ifaces := make(map[string]ifaceEntry) + ifaces := make(map[*types.TypeName]ifaceEntry) nodeFilter := []ast.Node{ (*ast.GenDecl)(nil), @@ -105,14 +107,25 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { continue } - ifaces[ts.Name.Name] = ifaceEntry{ts: ts, decl: decl} + obj := pass.TypesInfo.Defs[ts.Name] + + typeName, ok := obj.(*types.TypeName) + if !ok { + continue + } + + ifaces[typeName] = ifaceEntry{ + ifaceName: ts.Name.Name, + ts: ts, + decl: decl, + } } }) if r.debug { var ifaceNames []string - for name := range ifaces { - ifaceNames = append(ifaceNames, name) + for tn := range ifaces { + ifaceNames = append(ifaceNames, tn.Name()) } fmt.Fprintln(os.Stderr, "Declared interfaces:", ifaceNames) @@ -129,23 +142,28 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { return } - entry, ok := ifaces[ident.Name] + obj := pass.TypesInfo.Uses[ident] + + typeName, ok := obj.(*types.TypeName) if !ok { return } - if entry.ts.Pos() == ident.Pos() { + entry, ok := ifaces[typeName] + if !ok { return } - delete(ifaces, ident.Name) + r.debugln(" used:", entry.ifaceName) + + delete(ifaces, typeName) }) if r.debug { fmt.Fprintf(os.Stderr, "Package %s %s\n", pass.Pkg.Path(), pass.Pkg.Name()) } - for name, entry := range ifaces { + for typeName, entry := range ifaces { ts := entry.ts decl := entry.decl @@ -166,7 +184,7 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { end = ts.End() } - msg := fmt.Sprintf("interface '%s' is declared but not used within the package", name) + msg := fmt.Sprintf("interface '%s' is declared but not used within the package", typeName.Name()) pass.Report(analysis.Diagnostic{ Pos: ts.Pos(), Message: msg, diff --git a/unused/unused_test.go b/unused/unused_test.go index ad55847..e59b76a 100644 --- a/unused/unused_test.go +++ b/unused/unused_test.go @@ -15,6 +15,7 @@ func Test(t *testing.T) { analysistest.RunWithSuggestedFixes(t, testdata, unused.Analyzer, "agroupsingle") analysistest.Run(t, testdata, unused.Analyzer, "b") analysistest.RunWithSuggestedFixes(t, testdata, unused.Analyzer, "d") + analysistest.Run(t, testdata, unused.Analyzer, "e") } func TestExclusion(t *testing.T) {