Skip to content

SA5010: False Positive When Comparing Generic Types Against Concrete Types #1354

@superlinkx

Description

@superlinkx

The following example is a bit contrived in order to be as minimal as possible while showing the exceptional case. What appears to be happening is that this check assumes all type assertions will be against concrete types, which works well in the general case but in this generics case we are getting an unhelpful linter message. This code does correctly run the type assertions, counter to what the linter rule suggests.

  • The output of 'staticcheck -version'
    • staticcheck 2022.1.3 (v0.3.3)
  • The output of 'staticcheck -debug.version' (it is fine if this command fails)
    •    staticcheck 2022.1.3 (v0.3.3)
         
         Compiled with Go version: go1.18.2
         Main module:
                 honnef.co/go/tools@v0.3.3 (sum: h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA=)
         Dependencies:
                 github.com/BurntSushi/toml@v0.4.1 (sum: h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=)
                 golang.org/x/exp/typeparams@v0.0.0-20220218215828-6cf2b201936e (sum: h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=)
                 golang.org/x/mod@v0.6.0-dev.0.20220419223038-86c51ed26bb4 (sum: h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=)
                 golang.org/x/sys@v0.0.0-20211019181941-9d821ace8654 (sum: h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=)
                 golang.org/x/tools@v0.1.11-0.20220513221640-090b14e8501f (sum: h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=)
      
  • The output of 'go version'
    • go version go1.18.2 linux/amd64
  • The output of 'go env'
    •    GO111MODULE=""
         GOARCH="amd64"
         GOBIN=""
         GOCACHE="/home/alyx/.cache/go-build"
         GOENV="/home/alyx/.config/go/env"
         GOEXE=""
         GOEXPERIMENT=""
         GOFLAGS=""
         GOHOSTARCH="amd64"
         GOHOSTOS="linux"
         GOINSECURE=""
         GOMODCACHE="/home/alyx/go/pkg/mod"
         GONOPROXY=""
         GONOSUMDB=""
         GOOS="linux"
         GOPATH="/home/alyx/go"
         GOPRIVATE=""
         GOPROXY="https://proxy.golang.org,direct"
         GOROOT="/usr/local/go"
         GOSUMDB="sum.golang.org"
         GOTMPDIR=""
         GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
         GOVCS=""
         GOVERSION="go1.18.2"
         GCCGO="gccgo"
         GOAMD64="v1"
         AR="ar"
         CC="gcc"
         CXX="g++"
         CGO_ENABLED="1"
         GOMOD="/home/alyx/workspace/monorepo_tests/go.mod"
         GOWORK=""
         CGO_CFLAGS="-g -O2"
         CGO_CPPFLAGS=""
         CGO_CXXFLAGS="-g -O2"
         CGO_FFLAGS="-g -O2"
         CGO_LDFLAGS="-g -O2"
         PKG_CONFIG="pkg-config"
         GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3620740618=/tmp/go-build -gno-record-gcc-switches"
      
  • Exactly which command you ran
    • staticcheck (within the folder containing the example package below)
  • Output of the command and what's wrong with the output
    •    generic_type_switch.go:23:10: impossible type assertion; ExampleType[uint32] and ExampleType[T] contradict each other:
                 wrong type for SomeMethod method
                         have func() generics_exception.ExampleType[uint32]
                         want func() generics_exception.ExampleType[T] (SA5010)
         generic_type_switch.go:25:10: impossible type assertion; ExampleType[uint64] and ExampleType[T] contradict each other:
                 wrong type for SomeMethod method
                         have func() generics_exception.ExampleType[uint64]
                         want func() generics_exception.ExampleType[T] (SA5010)
      
  • Where we can read the code you're running Staticcheck on
    •    package generics_exception
      
         type ExampleType[T uint32 | uint64] interface {
           SomeMethod() ExampleType[T]
         }
      
         type emptyExampleType[T uint32 | uint64] struct{}
         ​
         func (s emptyExampleType[T]) SomeMethod() ExampleType[T] {
           return nil
         }
      
         var (
           emptyUint32ExampleType ExampleType[uint32] = emptyExampleType[uint32]{}
           emptyUint64ExampleType ExampleType[uint64] = emptyExampleType[uint64]{}
         )
      
         func EmptyExampleType[T uint32 | uint64]() ExampleType[T] {
           var emptyT T
      
           switch any(emptyT).(type) {
           case uint32:
             return emptyUint32ExampleType.(ExampleType[T])
           case uint64:
             return emptyUint64ExampleType.(ExampleType[T])
           }
      
           return nil
         }

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions