dominikh / go-tools

Staticcheck - The advanced Go linter
https://staticcheck.dev
MIT License
6.21k stars 377 forks source link

U1000: A false positive with generics #1602

Closed mbobrovskyi closed 2 months ago

mbobrovskyi commented 2 months ago

What happened:

I'm trying to check the code below:

package main

func main() {
    m := NewManager[*testClusterQueue, *testCohort]()
    m.AddCohort(newCohort("cohort"))
}

type Cohort[CQ nodeBase] struct {
    childCqs []CQ

    // ERROR: field explicit is unused (U1000)
    explicit bool
}

func NewCohort[CQ nodeBase]() Cohort[CQ] {
    return Cohort[CQ]{}
}

func (c *Cohort[CQ]) ChildCQs() []CQ {
    return c.childCqs
}

// ERROR: func (*Cohort[CQ]).markExplicit is unused (U1000)
func (c *Cohort[CQ]) isExplicit() bool {
    return c.explicit
}

// ERROR: func (*Cohort[CQ]).markExplicit is unused (U1000)
func (c *Cohort[CQ]) markExplicit() {
    c.explicit = true
}

type Manager[CQ nodeBase, C cohortNode[CQ]] struct {
    Cohorts map[string]C
}

func NewManager[CQ nodeBase, C cohortNode[CQ]]() Manager[CQ, C] {
    return Manager[CQ, C]{
        make(map[string]C),
    }
}

func (c *Manager[CQ, C]) AddCohort(cohort C) {
    if !cohort.isExplicit() {
        cohort.markExplicit()
    }
    c.Cohorts[cohort.GetName()] = cohort
}

type nodeBase interface {
    GetName() string
    comparable
}

type cohortNode[CQ nodeBase] interface {
    ChildCQs() []CQ
    isExplicit() bool
    markExplicit()
    nodeBase
}

type testCohort struct {
    name string
    Cohort[*testClusterQueue]
}

func newCohort(name string) *testCohort {
    return &testCohort{
        name:   name,
        Cohort: NewCohort[*testClusterQueue](),
    }
}

func (t *testCohort) GetName() string {
    return t.name
}

type testClusterQueue struct {
    name string
}

func (t *testClusterQueue) GetName() string {
    return t.name
}

But I have an error:

main.go:12:2: field explicit is unused (U1000)
main.go:24:22: func (*Cohort[CQ]).isExplicit is unused (U1000)
main.go:29:22: func (*Cohort[CQ]).markExplicit is unused (U1000)

How to reproduce it (as minimally and precisely as possible):

Execute this command:

staticcheck ./main.go

Anything else we need to know?: Also the same issue on master branch.

Environment:

$ staticcheck -version
staticcheck 2024.1.1 (0.5.1)

$ staticcheck -debug.version
staticcheck 2024.1.1 (0.5.1)

Compiled with Go version: go1.23.0
Main module:
        honnef.co/go/tools
Dependencies:
        github.com/BurntSushi/toml@v1.4.1-0.20240526193622-a339e1f7089c (sum: h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=)
        golang.org/x/exp/typeparams@v0.0.0-20231108232855-2478ac86f678 (sum: h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=)
        golang.org/x/mod@v0.17.0 (sum: h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=)
        golang.org/x/sync@v0.7.0 (sum: h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=)
        golang.org/x/tools@v0.21.1-0.20240531212143-b6235391adb3 (sum: h1:SHq4Rl+B7WvyM4XODon1LXtP7gcG49+7Jubt1gWWswY=)

$ go version
go version go1.23.0 darwin/arm64

$ go env    
GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/mykhailo_bobrovskyi/Library/Caches/go-build'
GOENV='/Users/mykhailo_bobrovskyi/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/mykhailo_bobrovskyi/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/mykhailo_bobrovskyi/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.23.0/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.23.0/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.0'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/mykhailo_bobrovskyi/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/mykhailo_bobrovskyi/Projects/mbobrovskyi/go-tools/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/y3/rwbwnplx2cz9gf0t71dhp5k00000gn/T/go-build3437274241=/tmp/go-build -gno-record-gcc-switches -fno-common'
mbobrovskyi commented 2 months ago

Looks like the problem is here:

https://github.com/dominikh/go-tools/blob/915b568982be0ad65a98e822471748b328240ed0/unused/implements.go#L130-L134

The Results type for ChildCQs() []CQ is *types.Slice. For this type I think it should be like here.

dominikh commented 2 months ago

This is a duplicate of #1294 or #1440 and is waiting on golang/go#63982. The code you have found is a workaround for the most common cases. An exhaustive solution would be identical to Go's type unification.