Thank you for providing this linter. It has already helped catch several cases where I did not use the %w error-wrapping directive in my projects.
It did however flag one case that may be a false-positive.
I have a project that is still using Go 1.19 and the linter identifies what it believes to be a missing use of %w, but I already have one such usage in place. I understand that this syntax would be valid in Go 1.20, but since the project README indicates that the linter supports Go 1.16 and newer I'm reporting this as a potential problem.
$ errwrap main.go
main.go:41:48: call could wrap the error with error-wrapping directive %w
Reproducible example
Code after following linter advice:
code example
```go
// main.go
package main
import (
"context"
"errors"
"fmt"
)
// errRuntimeTimeoutReached indicates that plugin runtime exceeded specified
// timeout value.
var errRuntimeTimeoutReached = errors.New("plugin runtime exceeded specified timeout value")
// runtimeTimeoutReachedAdvice offers advice to the sysadmin for routine
// occurrence.
const runtimeTimeoutReachedAdvice string = "consider increasing value if this is routinely encountered"
func annotateError(errs ...error) []error {
isNilErrCollection := func(collection []error) bool {
if len(collection) != 0 {
for _, err := range errs {
if err != nil {
return false
}
}
}
return true
}
switch {
// Process errors as long as the collection is not empty or not composed
// entirely of nil values.
case !isNilErrCollection(errs):
annotatedErrors := make([]error, 0, len(errs))
for _, err := range errs {
if err != nil {
switch {
case errors.Is(err, context.DeadlineExceeded):
annotatedErrors = append(annotatedErrors, fmt.Errorf(
"%w: %w; %s",
err,
errRuntimeTimeoutReached,
runtimeTimeoutReachedAdvice,
))
default:
// Record error unmodified if additional decoration isn't defined
// for the error type.
annotatedErrors = append(annotatedErrors, err)
}
}
}
return annotatedErrors
// No errors were provided for evaluation.
default:
return nil
}
}
func main() {
sampleErr := fmt.Errorf(
"my sample error: %w",
context.DeadlineExceeded,
)
errCollection := annotateError(sampleErr)
fmt.Println(errCollection)
}
// main_test.go
package main
import (
"context"
"fmt"
"testing"
)
func TestAnnotateError(t *testing.T) {
sampleErr := fmt.Errorf(
"my sample error: %w",
context.DeadlineExceeded,
)
errCollection := annotateError(sampleErr)
t.Log(errCollection)
}
```
Results
If I follow the linter's advice, this is what I get when running the code:
$ go run main.go
[my sample error: context deadline exceeded: %!w(*errors.errorString=&{plugin runtime exceeded specified timeout value}); consider increasing value if this is routinely encountered]
$ go test ./...
# errwrap-false-positive
.\main.go:41:48: fmt.Errorf call has more than one error-wrapping directive %w
FAIL errwrap-false-positive [build failed]
FAIL
Linter version details
$ go version -m $(which errwrap.exe)
C:/Users/atc0005/go/bin/errwrap.exe: go1.19.7
path github.com/fatih/errwrap
mod github.com/fatih/errwrap v1.5.0 h1:/z6jzrekbYYeJukzq9h3nY+SHREDevEB0vJYC4kE9D0=
dep golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
dep golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
dep golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
build -compiler=gc
build CGO_ENABLED=1
build CGO_CFLAGS=
build CGO_CPPFLAGS=
build CGO_CXXFLAGS=
build CGO_LDFLAGS=
build GOARCH=amd64
build GOOS=windows
build GOAMD64=v1
Overview
Hi @fatih,
Thank you for providing this linter. It has already helped catch several cases where I did not use the
%w
error-wrapping directive in my projects.It did however flag one case that may be a false-positive.
I have a project that is still using Go 1.19 and the linter identifies what it believes to be a missing use of
%w
, but I already have one such usage in place. I understand that this syntax would be valid in Go 1.20, but since the project README indicates that the linter supports Go 1.16 and newer I'm reporting this as a potential problem.Short version (before following linter advice):
Linter output:
Reproducible example
Code after following linter advice:
code example
```go // main.go package main import ( "context" "errors" "fmt" ) // errRuntimeTimeoutReached indicates that plugin runtime exceeded specified // timeout value. var errRuntimeTimeoutReached = errors.New("plugin runtime exceeded specified timeout value") // runtimeTimeoutReachedAdvice offers advice to the sysadmin for routine // occurrence. const runtimeTimeoutReachedAdvice string = "consider increasing value if this is routinely encountered" func annotateError(errs ...error) []error { isNilErrCollection := func(collection []error) bool { if len(collection) != 0 { for _, err := range errs { if err != nil { return false } } } return true } switch { // Process errors as long as the collection is not empty or not composed // entirely of nil values. case !isNilErrCollection(errs): annotatedErrors := make([]error, 0, len(errs)) for _, err := range errs { if err != nil { switch { case errors.Is(err, context.DeadlineExceeded): annotatedErrors = append(annotatedErrors, fmt.Errorf( "%w: %w; %s", err, errRuntimeTimeoutReached, runtimeTimeoutReachedAdvice, )) default: // Record error unmodified if additional decoration isn't defined // for the error type. annotatedErrors = append(annotatedErrors, err) } } } return annotatedErrors // No errors were provided for evaluation. default: return nil } } func main() { sampleErr := fmt.Errorf( "my sample error: %w", context.DeadlineExceeded, ) errCollection := annotateError(sampleErr) fmt.Println(errCollection) } // main_test.go package main import ( "context" "fmt" "testing" ) func TestAnnotateError(t *testing.T) { sampleErr := fmt.Errorf( "my sample error: %w", context.DeadlineExceeded, ) errCollection := annotateError(sampleErr) t.Log(errCollection) } ```Results
If I follow the linter's advice, this is what I get when running the code:
Linter version details