Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ ignoreSigs:
- .WithMessagef(
- .WithStack(

# An array of strings which specify regular expressions of signatures to ignore.
# This is similar to the ignoreSigs configuration above, but gives slightly more
# flexibility.
ignoreSigRegexps:
- \.New.*Error\(

# An array of glob patterns which, if any match the package of the function
# returning the error, will skip wrapcheck analysis for this error. This is
# useful for broadly ignoring packages and/or subpackages from wrapcheck
Expand Down
4 changes: 4 additions & 0 deletions wrapcheck/testdata/config_ignoreSigRegexps/.wrapcheck.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ignoreSigRegexps:
- json\.[a-zA-Z0-9_-]+\(
- ^func[\s]+\(.+wrapcheck.+\.error.*\)
- \.NewMod[\d]+Error\(
58 changes: 58 additions & 0 deletions wrapcheck/testdata/config_ignoreSigRegexps/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"encoding/json"
"fmt"
"strings"

"github.com/pkg/errors"
)

type errorer interface {
Decode(v interface{}) error
}

func main() {
do1()

d := json.NewDecoder(strings.NewReader("hello world"))
do2(d)

do3(5)
do4(5)
}

func do1() error {
_, err := json.Marshal(struct{}{}) // external package ignored by package+method-name regexp
if err != nil {
return err
}

return nil
}

func do2(fn errorer) error {
var str string
err := fn.Decode(&str) // interface ignored by regexp
if err != nil {
return err
}

return nil
}

func do3(i int) error {
if i%2 == 0 {
return errors.NewMod2Error(fmt.Sprintf("%d is an even number", i)) // external package ignored by method-name regexp
}

return nil
}

func do4(i int) error {
if i%3 == 0 {
return errors.NewMod3Error(fmt.Sprintf("%d is divisible by 3", i)) // external package ignored by method-name regexp
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package errors

type Mod2Error struct {
msg string
}

func NewMod2Error(msg string) error {
return &Mod2Error{msg}
}

func (e Mod2Error) Error() string {
return e.msg
}

type Mod3Error struct {
msg string
}

func NewMod3Error(msg string) error {
return &Mod3Error{msg}
}

func (e Mod3Error) Error() string {
return e.msg
}
36 changes: 35 additions & 1 deletion wrapcheck/wrapcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"go/types"
"log"
"os"
"regexp"
"strings"

"github.com/gobwas/glob"
Expand Down Expand Up @@ -33,7 +34,7 @@ type WrapcheckConfig struct {
// allows you to specify functions that wrapcheck will not report as
// unwrapped.
//
// For example, an ingoredSig of `[]string{"errors.New("}` will ignore errors
// For example, an ignoreSig of `[]string{"errors.New("}` will ignore errors
// returned from the stdlib package error's function:
//
// `func errors.New(message string) error`
Expand All @@ -45,6 +46,20 @@ type WrapcheckConfig struct {
// list to your config.
IgnoreSigs []string `mapstructure:"ignoreSigs" yaml:"ignoreSigs"`

// IgnoreSigRegexps defines a list of regular expressions which if matched
// to the signature of the function call returning the error, will be ignored. This
// allows you to specify functions that wrapcheck will not report as
// unwrapped.
//
// For example, an ignoreSigRegexp of `[]string{"\.New.*Err\("}`` will ignore errors
// returned from any signture whose method name starts with "New" and ends with "Err"
// due to the signature matching the regular expression `\.New.*Err\(`.
//
// Note that this is similar to the ignoreSigs configuration, but provides
// slightly more flexibility in defining rules by which signtures will be
// ignored.
IgnoreSigRegexps []string `mapstructure:"ignoreSigRegexps" yaml:"ignoreSigRegexps"`

// IgnorePackageGlobs defines a list of globs which, if matching the package
// of the function returning the error, will ignore the error when doing
// wrapcheck analysis.
Expand All @@ -62,6 +77,7 @@ type WrapcheckConfig struct {
func NewDefaultConfig() WrapcheckConfig {
return WrapcheckConfig{
IgnoreSigs: DefaultIgnoreSigs,
IgnoreSigRegexps: []string{},
IgnorePackageGlobs: []string{},
}
}
Expand Down Expand Up @@ -206,6 +222,8 @@ func reportUnwrapped(pass *analysis.Pass, call *ast.CallExpr, tokenPos token.Pos
fnSig := pass.TypesInfo.ObjectOf(sel.Sel).String()
if contains(cfg.IgnoreSigs, fnSig) {
return
} else if containsMatch(cfg.IgnoreSigRegexps, fnSig) {
return
}

// Check if the underlying type of the "x" in x.y.z is an interface, as
Expand Down Expand Up @@ -317,6 +335,22 @@ func contains(slice []string, el string) bool {
return false
}

func containsMatch(slice []string, el string) bool {
for _, s := range slice {
re, err := regexp.Compile(s)
if err != nil {
log.Printf("unable to parse regexp: %s\n", s)
os.Exit(1)
}

if re.MatchString(el) {
return true
}
}

return false
}

// isError returns whether or not the provided type interface is an error
func isError(typ types.Type) bool {
if typ == nil {
Expand Down