-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
I noticed a mistake during a code review that I think could be automatically detected. The idea is to detect code which looks like:
if f(); err != nil {
...
}
which should instead assign the return value of f()
if err := f(); err != nil {
...
}
For the code to compile at all, err must be declared in the outer scope.
Correctness:
This check detects code that the programmer didn't intend to write. In some instances the mistake is caused by what I personally might call a readability issue, such as when f() is a multi-line func literal that obscures the if statement. Nevertheless, this reports on the missing assignment, not the style.
Frequency:
A number of these have been detected at Google. The check is lightweight. I did not do a large review of open-source Go, but examples can be seen at:
https://github.com/golang/crypto/blob/459e26527287adbc2adcc5d0d49abff9a5f315a7/acme/acme.go#L157
https://github.com/tensorflow/tensorflow/blob/c44601f5411408c324c81dd38bc8686cbd2568aa/tensorflow/go/tensor.go#L101
Precision:
It is difficult to define a false negative for this test, but for my particular implementation perhaps errors that are not called "err", and are not checked against nil. Others implementations may be able to increase the scope.
False positives seem rare, though it is possible to write code like:
var err error
f := func() {
mu.Lock()
defer mu.Unlock()
err = g()
}
if f(); err != nil {
I do not think it would any less clear for f() to return err in that case though.
A false positive of my initial implementation is
this could be eliminated by looking for err (or &err) being passed as a parameter, though that function could avoid modifying its input parameter and instead return an error.
A more thorough check for if conditions that are always true/false would detect some of these situations, especially if a previous check for nil caused a return. But it would miss some, so I think a simple check is worth introducing. Some mistakes occur in tests:
if err := f(); err != nil {
t.Error(err)
}
if g(); err != nil {
t.Error(err)
}
whilst others aggregate errors rather than returning:
for x := range xs {
if f(x); err != nil {
errs = append(errs, err)
}
}
An initial implementation is here: https://go-review.googlesource.com/c/38631/