fatih / errwrap

Go tool to wrap and fix errors with the new %w verb directive
https://arslan.io
Other
369 stars 16 forks source link

errwrap (incorrectly?) identifies missing error-wrapping directive `%w` #21

Open atc0005 opened 1 year ago

atc0005 commented 1 year ago

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):

annotatedErrors := make([]error, 0, len(errs))

// ...
annotatedErrors = append(annotatedErrors, fmt.Errorf(
    "%w: %v; %s",
    err,
    errRuntimeTimeoutReached,
    runtimeTimeoutReachedAdvice,
))

Linter output:

$ 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
nasreen-begum commented 1 year ago

Yes, Even I am also facing same issue

nasreen-begum commented 1 year ago

cmd/telegraf/main.go:199: fmt.Errorf call has more than one error-wrapping directive %w