Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: cmd/vet: do not warn about nil comparisons after for loop #28871

Closed
mschoch opened this issue Nov 19, 2018 · 8 comments

Comments

Projects
None yet
8 participants
@mschoch
Copy link

commented Nov 19, 2018

What version of Go are you using (go version)?

$ go version
go version devel +e003c6f971 Mon Nov 19 17:33:37 2018 +0000 linux/amd64

Does this issue reproduce with the latest release?

I see this issue with vet installed via:

$ go get -v -u golang.org/x/tools/go/analysis/cmd/vet

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/mschoch/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/mschoch/go"
GOPROXY=""
GORACE=""
GOROOT="/home/mschoch/Documents/research/gosrc"
GOTMPDIR=""
GOTOOLDIR="/home/mschoch/Documents/research/gosrc/pkg/tool/linux_amd64"
GCCGO="gccgo"
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-build975661669=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Run vet . on the following program:

package main

import (
	"io"
	"log"
)

func main() {

	var i int
	var err error
	for err == nil {
		i++
		if i == 10 {
			err = io.EOF
		}
	}
	if err != nil && err != io.EOF {
		log.Fatalf("got a real error")
	}
}

What did you expect to see?

No output

What did you see instead?

/home/mschoch/go/src/github.com/mschoch/vetter/main.go:18:9: tautological condition: non-nil != nil

Discussion

Following the output of this tool, one might rewrite the program as:

package main

import (
	"io"
	"log"
)

func main() {

	var i int
	var err error
	for err == nil {
		i++
		if i == 10 {
			err = io.EOF
		}
	}
	if err != io.EOF {
		log.Fatalf("got a real error")
	}
}

And for now, the program remains correct.

However, if a developer were to add a break statement, such as:

package main

import (
	"io"
	"log"
)

func main() {

	var i int
	var err error
	for err == nil {
		i++
		if i == 7 {
			break
		}
		if i == 10 {
			err = io.EOF
		}
	}
	if err != io.EOF {
		log.Fatalf("got a real error")
	}
}

Now, the program will incorrectly process an error, because the nil check was removed.

In my opinion, break is a first class feature of the language, and because of this, developers should not assume that the for loop condition is satisfied, just because control flow has proceeded past it. Following the advice of the vet tool in this instance has made the code more brittle.

NOTE: this example is silly, but it came up in real code here: blevesearch/bleve#1051

CC: @dgryski

@bcmills

This comment has been minimized.

Copy link
Member

commented Nov 19, 2018

Contrast #19727 and #20148.

@bcmills

This comment has been minimized.

Copy link
Member

commented Nov 19, 2018

Now, the program will incorrectly process an error, because the nil check was removed.

How often will such a mistake make it through testing? If a developer adds a break, presumably they had a reason to do so — what kinds of changes that would result in err == nil would also make it past (even cursory) testing?

@bcmills bcmills changed the title cmd/vet: debatable suggestion, regarding nil comparisons after for loop? cmd/vet: do not warn about nil comparisons after for loop Nov 19, 2018

@bcmills

This comment has been minimized.

Copy link
Member

commented Nov 19, 2018

(CC @alandonovan @josharian @mvdan for cmd/vet.)

@bcmills bcmills changed the title cmd/vet: do not warn about nil comparisons after for loop proposal: cmd/vet: do not warn about nil comparisons after for loop Nov 19, 2018

@gopherbot gopherbot added this to the Proposal milestone Nov 19, 2018

@gopherbot gopherbot added the Proposal label Nov 19, 2018

@alandonovan

This comment has been minimized.

Copy link
Contributor

commented Nov 19, 2018

I would argue that this is the checker working as intended. Much more often than not, a non-constant condition that is always true or false represents a misunderstanding about the local control flow. When a programmer adds a break statement, they're changing the control flow, and it's upon to them to evaluate the consequences of that change.

@dominikh

This comment has been minimized.

Copy link
Member

commented Nov 19, 2018

One can also argue that the redundant form is much more resistant to accidental breakage. If Go had assert, assert(err != nil) is something one may want to put after the loop. Lacking that, one pulls it into the if statement instead.

@mvdan

This comment has been minimized.

Copy link
Member

commented Nov 19, 2018

I haven't made up my mind about this issue, but I lean towards Dominik's opinion that vet shouldn't fight programmers that are being explicit with their invariants and pre/post conditions.

Also, this issue reminds me of #28446. That was about a different check, but the underlying point is similar. Vet complains about an expression that is technically pointless, but in practice it serves a purpose - to help future contributors not break an invariant.

@dgryski

This comment has been minimized.

Copy link
Contributor

commented Nov 19, 2018

Another step down the slippery slope: vet notices that all callers to a particular non-exported function pass a non-nil parameter. Does it warn that the nil check in the function body is useless?

@andybons

This comment has been minimized.

Copy link
Member

commented Dec 5, 2018

Per discussion with @golang/proposal-review, we agree the vet tool is working as intended with its current behavior.

@andybons andybons closed this Dec 5, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.