perf(lint): collect interface method sets once

Dedup interface pointers so Complete().Methods() isn't walked twice per
analysis pass.

Assisted-By: docker-agent
Signed-off-by: David Gageot <david.gageot@docker.com>
This commit is contained in:
David Gageot committed 2026-10-17 13:10:16 +02:00
1 parent 07144db8c3
commit e65f2f9825
2 files changed
+31 -7

No files matched your search

+12 -7
View File
@@ -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)
}
}
}
+19
View File
@@ -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)
}
}