8000 QF1003: suggested fix generates an invalid `switch nil` statement · Issue #1842 · dominikh/go-tools · GitHub
[go: up one dir, main page]

Skip to content

QF1003: suggested fix generates an invalid switch nil statement #1842

Open

Description

@TCOTC

staticcheck version

$ staticcheck -version
staticcheck 2026.1 (v0.7.0)

staticcheck -debug.version

Details
$ staticcheck -debug.version
staticcheck 2026.1 (v0.7.0)

Compiled with Go version: go1.25.8
Main module:
	honnef.co/go/tools@v0.7.0 (sum: h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU=)
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.31.0 (sum: h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=)
	golang.org/x/sync@v0.19.0 (sum: h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=)
	golang.org/x/tools@v0.40.1-0.20260108161641-ca281cf95054 (sum: h1:CHVDrNHx9ZoOrNN9kKWYIbT5Rj+WF2rlwPkhbQQ5V4U=)

go version

$ go version
go version go1.27.2 windows/amd64

go env

Details
set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=1
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=
set GOAMD64=v1
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=C:\Users\Admin\go\bin
set GOCACHE=C:\Users\Admin\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\Admin\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\Admin\AppData\Local\Temp\go-build606148118=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=C:\Users\Admin\AppData\Local\Temp\qf1003-tmpl\go.mod
set GOMODCACHE=C:\Users\Admin\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPACKAGESDRIVER=
set GOPATH=C:\Users\Admin\go
set GOPRIVATE=
set GOPROXY=https://goproxy.cn,direct
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\Admin\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.27.2
set GOWORK=
set PKG_CONFIG=pkg-config

Exactly which command I ran

QF1003 is a quickfix analyzer, and cmd/staticcheck only registers quickfix analyzers behind a debug flag, so the check is invisible without it:

$ staticcheck -checks=QF1003 -debug.run-quickfix-analyzers ./...
repro.go:6:2: could use tagged switch on nil (QF1003)

Output of the command and what is wrong with it

The diagnostic itself is correct, but the fix that QF1003 offers does not 775C compile. This version of the CLI cannot apply fixes (-fix is not a flag):

$ staticcheck -fix ./...
flag provided but not defined: -fix
Usage: staticcheck [flags] [packages]

Flags:
  -checks checks
    	Comma-separated list of checks to enable. (default "inherit")
  -explain check
    	Print description of check
  -f format
    	Output format (valid choices are 'stylish', 'text' and 'json') (default text)
  -fail checks
    	Comma-separated list of checks that can cause a non-zero exit status. (default "all")
  -go version
    	Target Go version in the format '1.x', or the literal 'module' to use the module's Go version (default "module")
  -list-checks
    	List all available checks
  -matrix
    	Read a build config matrix from stdin
  -merge
    	Merge results of multiple Staticcheck runs
  -show-ignored
    	Don't filter ignored diagnostics
  -tags build tags
    	List of build tags
  -tests
    	Include tests (default true)
  -version
    	Print version and exit

For help about specifying packages, see 'go help packages'

I therefore applied the suggested edit through gopls v0.24.0, which vendors honnef.co/go/tools and exposes quickfixes as code actions:

Details
$ gopls -v version
Build info
----------
golang.org/x/tools/gopls v0.24.0
$ gopls codeaction -exec -write -kind=quickfix -title='Replace with tagged switch' ./repro.go

The resulting file:

// Package qf1003repro demonstrates the QF1003 suggestion.
package qf1003repro

// Record stores rating under name and reports the first error seen.
func Record(err, firstErr error, ratings map[string]int, name string, rating int) error {
	switch nil {
case err:
		ratings[name] = rating
	case firstErr:
		firstErr = err
	}
	return firstErr
}

and the compile error it produces:

$ go build ./...
# example.com/qf1003repro
.\repro.go:6:9: use of untyped nil in switch expression

nil has no default type, so it is not a valid switch tag. The suggestion is not semantically convertible either: the second condition compares nil against a different variable (firstErr), so the two conditions share no usable operand. Using the right-hand operand as the tag would compile but change behaviour.

Cause

quickfix/qf1003/qf1003.go reuses the first comparison's left-hand operand as the switch tag without checking whether it is the predeclared nil:

x = pair[0].X

and renders it verbatim:

edits := []analysis.TextEdit{edit.ReplaceWithString(edit.Range{ifstmt.If, ifstmt.If}, fmt.Sprintf("switch %s {\n", report.Render(pass, x)))}

Where the code is (embedded)

module example.com/qf1003repro

go 1.24
// Package qf1003repro demonstrates the QF1003 suggestion.
package qf1003repro

// Record stores rating under name and reports the first error seen.
func Record(err, firstErr error, ratings map[string]int, name string, rating int) error {
	if nil == err {
		ratings[name] = rating
	} else if nil == firstErr {
		firstErr = err
	}
	return firstErr
}

Suggested fix

Skip the suggestion when the common operand is the untyped nil, i.e. an *ast.Ident with Name == "nil" and Obj == nil.

Notes on reproducing

  • The quickfix analyzers must be enabled with -debug.run-quickfix-analyzers; otherwise -list-checks does not list QF checks, -explain QF1003 reports Couldn't find check QF1003, and -checks=all does not report it.
  • The module's go directive must not exceed the Go version staticcheck was built with (go1.25.8). With go 1.26 the run fails with package requires newer Go version go1.26 (application built with go1.25).
  • Reading quickfix/qf1003/qf1003.go at tag 2026.2.2 (v0.8.2) shows the same unchecked assignment and the same rendering. I did not run that release, so that observation comes from the source only.

Related

The same text edit also drops indentation, which the source already notes with FIXME this edit forces the first case to begin in column 0 because we ignore indentation. try to fix that., directly above the edits construction; every affected file then needs gofmt. I did not open a separate report for it, since it is already tracked in the source.

Occurred in the wild

https://github.com/siyuan-note/siyuan/blob/dev/kernel/model/bazaar_rating.go uses if nil == err { ... } else if nil == firstErr { ... }.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0