go-tools icon indicating copy to clipboard operation
go-tools copied to clipboard

staticcheck: SA4006 false-negative when a defer is used

Open ainar-g opened this issue 6 years ago • 1 comments

Formalities
$ staticcheck --version
staticcheck (devel, v0.0.0-20200121224137-2774002210e8)
$ staticcheck --debug.version
staticcheck (devel, v0.0.0-20200121224137-2774002210e8)

Compiled with Go version: go1.13.7
Main module:
	honnef.co/go/[email protected] (sum: h1:LDZNqNckGrHSM2hO0hPWJKIqdodYH0Tov7n86ikeGJE=)
Dependencies:
	github.com/BurntSushi/[email protected] (sum: h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=)
	golang.org/x/[email protected] (sum: h1:/iIZNFGxc/a7C3yWjGcnboV+Tkc7mxr+p6fDztwoxuM=)
$ go version
go version go1.13.7 linux/amd64
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/ainar/.cache/go-build"
GOENV="/home/ainar/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/ainar/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/ainar/go/go1.13"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/ainar/go/go1.13/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
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-build783553461=/tmp/go-build -gno-record-gcc-switches"

The code itself: https://play.golang.org/p/97Vr6raQX-i.

On line 21, an error check is missing, but because of the following defer, staticcheck doesn't complain:

$ staticcheck ./go.tmp.go
$

I remember a similar bug, but I couldn't find the issue.

ainar-g avatar Feb 04 '20 10:02 ainar-g

From an implementation point of view: the anonymous function refers to err, which means that the variable can't be lifted into registers; it'll be an allocation. The way we check for unread assignments is by checking whether the register gets read.

For allocations, this isn't nearly as trivial (nor solvable for all cases.) For example, err could be aliased and read via the alias, or g could panic, leading to the deferred function reading the value. We could handle some of these cases (e.g. handle allocations that can't be aliased), but for your concrete example, we still couldn't claim that err is unread, due to the possibility of g panicking and thus reading that value.

dominikh avatar Feb 04 '20 14:02 dominikh