diff --git a/forbidigo/forbidigo_test.go b/forbidigo/forbidigo_test.go index 953b74b..6de984c 100644 --- a/forbidigo/forbidigo_test.go +++ b/forbidigo/forbidigo_test.go @@ -20,13 +20,13 @@ func foo() { }) t.Run("displays custom messages", func(t *testing.T) { - linter, _ := NewLinter([]string{`#[Please don't use this!]fmt\.Printf`}) + linter, _ := NewLinter([]string{`fmt\.Printf(# Please don't use this!)?`}) expectIssues(t, linter, ` -package bar + package bar -func foo() { + func foo() { fmt.Printf("here i am") -}`, "use of `fmt.Printf` forbidden by pattern `fmt\\.Printf` at testing.go:5:2: Please don't use this!") + }`, "use of `fmt.Printf` forbidden by pattern `fmt\\.Printf` at testing.go:5:2: Please don't use this!") }) t.Run("it doesn't require a package on the identifier", func(t *testing.T) { diff --git a/forbidigo/patterns.go b/forbidigo/patterns.go index 25af7c5..ab689c4 100644 --- a/forbidigo/patterns.go +++ b/forbidigo/patterns.go @@ -3,10 +3,10 @@ package forbidigo import ( "fmt" "regexp" + "regexp/syntax" + "strings" ) -var ptrnWithMsg = regexp.MustCompile(`(#\[(?P[^\]]+)\])?(?P.+)`) - type pattern struct { pattern *regexp.Regexp msg string @@ -14,18 +14,35 @@ type pattern struct { func parse(ptrn string) (*pattern, error) { p := &pattern{} - matches := ptrnWithMsg.FindStringSubmatch(ptrn) - for i, name := range ptrnWithMsg.SubexpNames() { - if name == "msg" { - p.msg = matches[i] - } else if name == "pattern" { - re, err := regexp.Compile(matches[i]) - if err != nil { - return nil, fmt.Errorf("unable to compile pattern `%s`: %s", matches[i], err) - } - p.pattern = re + parsedPattern, err := syntax.Parse(ptrn, syntax.Perl) + if err != nil { + return nil, fmt.Errorf("unable to parse pattern: %s: %s", ptrn, err) + } + if len(parsedPattern.Sub) == 0 { + p.pattern, err = regexp.Compile(parsedPattern.String()) + if err != nil { + return nil, fmt.Errorf("unable to compile pattern: %s: %s", ptrn, err) } + return p, nil + } + p.pattern, err = regexp.Compile(parsedPattern.Sub[0].String()) + if err != nil { + return nil, fmt.Errorf("unable to compile pattern: %s: %s", ptrn, err) + } + if len(parsedPattern.Sub) < 2 { + return p, nil } + msgPattern := deepestSubmatch(parsedPattern).String() + p.msg = strings.TrimSpace(strings.TrimPrefix(msgPattern, "#")) return p, nil } + +func deepestSubmatch(expr *syntax.Regexp) *syntax.Regexp { + for { + if len(expr.Sub) == 0 { + return expr + } + expr = expr.Sub[len(expr.Sub)-1] + } +} diff --git a/forbidigo/patterns_test.go b/forbidigo/patterns_test.go index 729a87d..2ac5b8b 100644 --- a/forbidigo/patterns_test.go +++ b/forbidigo/patterns_test.go @@ -13,16 +13,13 @@ func TestParseValidPattern(t *testing.T) { assert.Equal(t, `fmt\.Errorf`, ptrn.pattern.String()) } -func TestParseValidPatternThatUsesSquareBrackets(t *testing.T) { - ptrn, err := parse(`[f]mt\.Errorf`) - require.Nil(t, err) - assert.Equal(t, `[f]mt\.Errorf`, ptrn.pattern.String()) -} - func TestParseValidPatternWithCustomMessage(t *testing.T) { - ptrn, err := parse(`#[Please don't use this!]fmt\.Println`) + ptrn, err := parse(`fmt\.Println(# Please don't use this!)?`) require.Nil(t, err) + + // Remove custom message from pattern so as not to duplicate it in the output. assert.Equal(t, `fmt\.Println`, ptrn.pattern.String()) + assert.Equal(t, "Please don't use this!", ptrn.msg) }