diff --git a/lint/channel_direction.go b/lint/channel_direction.go index faa1b6cc4..ad8f95472 100644 --- a/lint/channel_direction.go +++ b/lint/channel_direction.go @@ -75,6 +75,16 @@ func analyzeChannelDirections(p *prog.Pass, pkgs []*packages.Package) { objects: make(map[types.Object]*channelSlot), functions: make(map[string]*channelFunction), direct: make(map[ast.Expr]bool), interfaces: make(map[string][]*types.Signature), } + seenInterfaces := make(map[*types.Interface]bool) + collectInterface := func(iface *types.Interface) { + if seenInterfaces[iface] { + return + } + seenInterfaces[iface] = true + for method := range iface.Complete().Methods() { + a.interfaces[method.Name()] = append(a.interfaces[method.Name()], method.Type().(*types.Signature)) + } + } visited := make(map[*types.Package]bool) var interfaces func(*types.Package) interfaces = func(pkg *types.Package) { @@ -85,10 +95,7 @@ func analyzeChannelDirections(p *prog.Pass, pkgs []*packages.Package) { for _, name := range pkg.Scope().Names() { if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { if iface, ok := obj.Type().Underlying().(*types.Interface); ok { - for method := range iface.Complete().Methods() { - sig := method.Type().(*types.Signature) - a.interfaces[method.Name()] = append(a.interfaces[method.Name()], sig) - } + collectInterface(iface) } } } @@ -109,9 +116,7 @@ func analyzeChannelDirections(p *prog.Pass, pkgs []*packages.Package) { if expr, ok := n.(ast.Expr); ok { if typ := a.info.TypeOf(expr); typ != nil { if iface, ok := typ.Underlying().(*types.Interface); ok { - for method := range iface.Complete().Methods() { - a.interfaces[method.Name()] = append(a.interfaces[method.Name()], method.Type().(*types.Signature)) - } + collectInterface(iface) } } } diff --git a/lint/channel_direction_test.go b/lint/channel_direction_test.go index 5b2304be3..e1bb3cc56 100644 --- a/lint/channel_direction_test.go +++ b/lint/channel_direction_test.go @@ -199,3 +199,22 @@ func TestChannelDirectionInvalidTests(t *testing.T) { "sample_test.go": "package p\nfunc broken() { close() }\nfunc bodyless(c chan int)", })) } + +func TestChannelDirectionRepeatedInterfaceUses(t *testing.T) { + t.Parallel() + offenses := coptest.RunProgram(t, ChannelDirection, coptest.ProgramFiles{ + "sample.go": `package p +type Sender interface { Send(chan int) } +type Receiver interface { Send(<-chan int) } +type Worker struct{} +func (Worker) Send(c chan int) { c <- 1 } +func use(a, b Sender) { _ = a; _ = b; _ = []Sender{a,b} } +func anonymous(a, b interface{ Send(chan int) }) { _ = a; _ = b } +func narrow(c chan int) { c <- 1 } +`, + }) + assert.Len(t, offenses, 1) + if len(offenses) == 1 { + assert.Equal(t, 8, offenses[0].Pos.Line) + } +}