Skip to content

Commit

Permalink
feat: ignore signatures via regex (#21)
Browse files Browse the repository at this point in the history
* Ignore error signatures via regexp

* Update docs with new ignoreSigRegexps configuration

* Fix docs typo
  • Loading branch information
timkral committed Oct 29, 2021
1 parent 849598b commit 1d12a32
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 1 deletion.
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

0 comments on commit 1d12a32

Please sign in to comment.