Merge pull request #1036 from iwankgb/exclude-case-sensitive

Adding case-sensitive exclude processor
This commit is contained in:
Aleksandr Razumov 2020-04-24 12:48:42 +03:00 committed by GitHub
commit e2b927f029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 203 additions and 27 deletions

View File

@ -574,6 +574,7 @@ Flags:
# gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)' # gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)'
- Potential file inclusion via variable - Potential file inclusion via variable
(default true) (default true)
--exclude-case-sensitive If set to true exclude and exclude rules regular expressions are case sensitive
--max-issues-per-linter int Maximum issues count per one linter. Set to 0 to disable (default 50) --max-issues-per-linter int Maximum issues count per one linter. Set to 0 to disable (default 50)
--max-same-issues int Maximum count of issues with the same text. Set to 0 to disable (default 3) --max-same-issues int Maximum count of issues with the same text. Set to 0 to disable (default 3)
-n, --new Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed. -n, --new Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

View File

@ -189,6 +189,8 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, is
ic := &cfg.Issues ic := &cfg.Issues
fs.StringSliceVarP(&ic.ExcludePatterns, "exclude", "e", nil, wh("Exclude issue by regexp")) fs.StringSliceVarP(&ic.ExcludePatterns, "exclude", "e", nil, wh("Exclude issue by regexp"))
fs.BoolVar(&ic.UseDefaultExcludes, "exclude-use-default", true, getDefaultIssueExcludeHelp()) fs.BoolVar(&ic.UseDefaultExcludes, "exclude-use-default", true, getDefaultIssueExcludeHelp())
fs.BoolVar(&ic.ExcludeCaseSensitive, "exclude-case-sensitive", false, wh("If set to true exclude "+
"and exclude rules regular expressions are case sensitive"))
fs.IntVar(&ic.MaxIssuesPerLinter, "max-issues-per-linter", 50, fs.IntVar(&ic.MaxIssuesPerLinter, "max-issues-per-linter", 50,
wh("Maximum issues count per one linter. Set to 0 to disable")) wh("Maximum issues count per one linter. Set to 0 to disable"))

View File

@ -411,9 +411,10 @@ func (e ExcludeRule) Validate() error {
} }
type Issues struct { type Issues struct {
ExcludePatterns []string `mapstructure:"exclude"` ExcludeCaseSensitive bool `mapstructure:"exclude-case-sensitive"`
ExcludeRules []ExcludeRule `mapstructure:"exclude-rules"` ExcludePatterns []string `mapstructure:"exclude"`
UseDefaultExcludes bool `mapstructure:"exclude-use-default"` ExcludeRules []ExcludeRule `mapstructure:"exclude-rules"`
UseDefaultExcludes bool `mapstructure:"exclude-use-default"`
MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"` MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"`
MaxSameIssues int `mapstructure:"max-same-issues"` MaxSameIssues int `mapstructure:"max-same-issues"`

View File

@ -40,6 +40,13 @@ func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env,
excludeTotalPattern = fmt.Sprintf("(%s)", strings.Join(excludePatterns, "|")) excludeTotalPattern = fmt.Sprintf("(%s)", strings.Join(excludePatterns, "|"))
} }
var excludeProcessor processors.Processor
if cfg.Issues.ExcludeCaseSensitive {
excludeProcessor = processors.NewExcludeCaseSensitive(excludeTotalPattern)
} else {
excludeProcessor = processors.NewExclude(excludeTotalPattern)
}
skipFilesProcessor, err := processors.NewSkipFiles(cfg.Run.SkipFiles) skipFilesProcessor, err := processors.NewSkipFiles(cfg.Run.SkipFiles)
if err != nil { if err != nil {
return nil, err return nil, err
@ -63,6 +70,12 @@ func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env,
Linters: r.Linters, Linters: r.Linters,
}) })
} }
var excludeRulesProcessor processors.Processor
if cfg.Issues.ExcludeCaseSensitive {
excludeRulesProcessor = processors.NewExcludeRulesCaseSensitive(excludeRules, lineCache, log.Child("exclude_rules"))
} else {
excludeRulesProcessor = processors.NewExcludeRules(excludeRules, lineCache, log.Child("exclude_rules"))
}
return &Runner{ return &Runner{
Processors: []processors.Processor{ Processors: []processors.Processor{
@ -81,8 +94,8 @@ func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env,
// Must be before exclude because users see already marked output and configure excluding by it. // Must be before exclude because users see already marked output and configure excluding by it.
processors.NewIdentifierMarker(), processors.NewIdentifierMarker(),
processors.NewExclude(excludeTotalPattern), excludeProcessor,
processors.NewExcludeRules(excludeRules, lineCache, log.Child("exclude_rules")), excludeRulesProcessor,
processors.NewNolint(log.Child("nolint"), dbManager), processors.NewNolint(log.Child("nolint"), dbManager),
processors.NewUniqByLine(cfg), processors.NewUniqByLine(cfg),

View File

@ -37,3 +37,23 @@ func (p Exclude) Process(issues []result.Issue) ([]result.Issue, error) {
} }
func (p Exclude) Finish() {} func (p Exclude) Finish() {}
type ExcludeCaseSensitive struct {
*Exclude
}
var _ Processor = ExcludeCaseSensitive{}
func NewExcludeCaseSensitive(pattern string) *ExcludeCaseSensitive {
var patternRe *regexp.Regexp
if pattern != "" {
patternRe = regexp.MustCompile(pattern)
}
return &ExcludeCaseSensitive{
&Exclude{pattern: patternRe},
}
}
func (p ExcludeCaseSensitive) Name() string {
return "exclude-case-sensitive"
}

View File

@ -37,24 +37,29 @@ func NewExcludeRules(rules []ExcludeRule, lineCache *fsutils.LineCache, log logu
lineCache: lineCache, lineCache: lineCache,
log: log, log: log,
} }
r.rules = createRules(rules, "(?i)")
return r
}
func createRules(rules []ExcludeRule, prefix string) []excludeRule {
parsedRules := make([]excludeRule, 0, len(rules))
for _, rule := range rules { for _, rule := range rules {
parsedRule := excludeRule{ parsedRule := excludeRule{
linters: rule.Linters, linters: rule.Linters,
} }
if rule.Text != "" { if rule.Text != "" {
parsedRule.text = regexp.MustCompile("(?i)" + rule.Text) parsedRule.text = regexp.MustCompile(prefix + rule.Text)
} }
if rule.Source != "" { if rule.Source != "" {
parsedRule.source = regexp.MustCompile("(?i)" + rule.Source) parsedRule.source = regexp.MustCompile(prefix + rule.Source)
} }
if rule.Path != "" { if rule.Path != "" {
parsedRule.path = regexp.MustCompile(rule.Path) parsedRule.path = regexp.MustCompile(rule.Path)
} }
r.rules = append(r.rules, parsedRule) parsedRules = append(parsedRules, parsedRule)
} }
return parsedRules
return r
} }
func (p ExcludeRules) Process(issues []result.Issue) ([]result.Issue, error) { func (p ExcludeRules) Process(issues []result.Issue) ([]result.Issue, error) {
@ -118,3 +123,21 @@ func (ExcludeRules) Name() string { return "exclude-rules" }
func (ExcludeRules) Finish() {} func (ExcludeRules) Finish() {}
var _ Processor = ExcludeRules{} var _ Processor = ExcludeRules{}
type ExcludeRulesCaseSensitive struct {
*ExcludeRules
}
func NewExcludeRulesCaseSensitive(rules []ExcludeRule, lineCache *fsutils.LineCache, log logutils.Log) *ExcludeRulesCaseSensitive {
r := &ExcludeRules{
lineCache: lineCache,
log: log,
}
r.rules = createRules(rules, "")
return &ExcludeRulesCaseSensitive{r}
}
func (ExcludeRulesCaseSensitive) Name() string { return "exclude-rules-case-sensitive" }
var _ Processor = ExcludeCaseSensitive{}

View File

@ -31,26 +31,12 @@ func TestExcludeRulesMultiple(t *testing.T) {
Linters: []string{"lll"}, Linters: []string{"lll"},
}, },
}, lineCache, nil) }, lineCache, nil)
type issueCase struct {
Path string
Line int
Text string
Linter string
}
var newIssueCase = func(c issueCase) result.Issue {
return result.Issue{
Text: c.Text,
FromLinter: c.Linter,
Pos: token.Position{
Filename: c.Path,
Line: c.Line,
},
}
}
cases := []issueCase{ cases := []issueCase{
{Path: "e.go", Text: "exclude", Linter: "linter"}, {Path: "e.go", Text: "exclude", Linter: "linter"},
{Path: "e.go", Text: "some", Linter: "linter"}, {Path: "e.go", Text: "some", Linter: "linter"},
{Path: "e_test.go", Text: "normal", Linter: "testlinter"}, {Path: "e_test.go", Text: "normal", Linter: "testlinter"},
{Path: "e_Test.go", Text: "normal", Linter: "testlinter"},
{Path: "e_test.go", Text: "another", Linter: "linter"}, {Path: "e_test.go", Text: "another", Linter: "linter"},
{Path: "e_test.go", Text: "testonly", Linter: "linter"}, {Path: "e_test.go", Text: "testonly", Linter: "linter"},
{Path: filepath.Join("testdata", "exclude_rules.go"), Line: 3, Linter: "lll"}, {Path: filepath.Join("testdata", "exclude_rules.go"), Line: 3, Linter: "lll"},
@ -71,11 +57,30 @@ func TestExcludeRulesMultiple(t *testing.T) {
} }
expectedCases := []issueCase{ expectedCases := []issueCase{
{Path: "e.go", Text: "some", Linter: "linter"}, {Path: "e.go", Text: "some", Linter: "linter"},
{Path: "e_Test.go", Text: "normal", Linter: "testlinter"},
{Path: "e_test.go", Text: "another", Linter: "linter"}, {Path: "e_test.go", Text: "another", Linter: "linter"},
} }
assert.Equal(t, expectedCases, resultingCases) assert.Equal(t, expectedCases, resultingCases)
} }
type issueCase struct {
Path string
Line int
Text string
Linter string
}
func newIssueCase(c issueCase) result.Issue {
return result.Issue{
Text: c.Text,
FromLinter: c.Linter,
Pos: token.Position{
Filename: c.Path,
Line: c.Line,
},
}
}
func TestExcludeRulesText(t *testing.T) { func TestExcludeRulesText(t *testing.T) {
p := NewExcludeRules([]ExcludeRule{ p := NewExcludeRules([]ExcludeRule{
{ {
@ -103,6 +108,96 @@ func TestExcludeRulesText(t *testing.T) {
} }
assert.Equal(t, texts[1:], processedTexts) assert.Equal(t, texts[1:], processedTexts)
} }
func TestExcludeRulesEmpty(t *testing.T) { func TestExcludeRulesEmpty(t *testing.T) {
processAssertSame(t, NewExcludeRules(nil, nil, nil), newTextIssue("test")) processAssertSame(t, NewExcludeRules(nil, nil, nil), newTextIssue("test"))
} }
func TestExcludeRulesCaseSensitiveMultiple(t *testing.T) {
lineCache := fsutils.NewLineCache(fsutils.NewFileCache())
p := NewExcludeRulesCaseSensitive([]ExcludeRule{
{
Text: "^exclude$",
Linters: []string{"linter"},
},
{
Linters: []string{"testlinter"},
Path: `_test\.go`,
},
{
Text: "^testonly$",
Path: `_test\.go`,
},
{
Source: "^//go:generate ",
Linters: []string{"lll"},
},
}, lineCache, nil)
cases := []issueCase{
{Path: "e.go", Text: "exclude", Linter: "linter"},
{Path: "e.go", Text: "excLude", Linter: "linter"},
{Path: "e.go", Text: "some", Linter: "linter"},
{Path: "e_test.go", Text: "normal", Linter: "testlinter"},
{Path: "e_Test.go", Text: "normal", Linter: "testlinter"},
{Path: "e_test.go", Text: "another", Linter: "linter"},
{Path: "e_test.go", Text: "testonly", Linter: "linter"},
{Path: "e_test.go", Text: "testOnly", Linter: "linter"},
{Path: filepath.Join("testdata", "exclude_rules_case_sensitive.go"), Line: 3, Linter: "lll"},
}
var issues []result.Issue
for _, c := range cases {
issues = append(issues, newIssueCase(c))
}
processedIssues := process(t, p, issues...)
var resultingCases []issueCase
for _, i := range processedIssues {
resultingCases = append(resultingCases, issueCase{
Path: i.FilePath(),
Linter: i.FromLinter,
Text: i.Text,
Line: i.Line(),
})
}
expectedCases := []issueCase{
{Path: "e.go", Text: "excLude", Linter: "linter"},
{Path: "e.go", Text: "some", Linter: "linter"},
{Path: "e_Test.go", Text: "normal", Linter: "testlinter"},
{Path: "e_test.go", Text: "another", Linter: "linter"},
{Path: "e_test.go", Text: "testOnly", Linter: "linter"},
{Path: filepath.Join("testdata", "exclude_rules_case_sensitive.go"), Line: 3, Linter: "lll"},
}
assert.Equal(t, expectedCases, resultingCases)
}
func TestExcludeRulesCaseSensitiveText(t *testing.T) {
p := NewExcludeRulesCaseSensitive([]ExcludeRule{
{
Text: "^exclude$",
Linters: []string{
"linter",
},
},
}, nil, nil)
texts := []string{"exclude", "excLude", "1", "", "exclud", "notexclude"}
var issues []result.Issue
for _, t := range texts {
issues = append(issues, result.Issue{
Text: t,
FromLinter: "linter",
})
}
processedIssues := process(t, p, issues...)
assert.Len(t, processedIssues, len(issues)-1)
var processedTexts []string
for _, i := range processedIssues {
processedTexts = append(processedTexts, i.Text)
}
assert.Equal(t, texts[1:], processedTexts)
}
func TestExcludeRulesCaseSensitiveEmpty(t *testing.T) {
processAssertSame(t, NewExcludeRulesCaseSensitive(nil, nil, nil), newTextIssue("test"))
}

View File

@ -51,3 +51,21 @@ func TestExclude(t *testing.T) {
func TestNoExclude(t *testing.T) { func TestNoExclude(t *testing.T) {
processAssertSame(t, NewExclude(""), newTextIssue("test")) processAssertSame(t, NewExclude(""), newTextIssue("test"))
} }
func TestExcludeCaseSensitive(t *testing.T) {
p := NewExcludeCaseSensitive("^exclude$")
texts := []string{"excLude", "1", "", "exclud", "exclude"}
var issues []result.Issue
for _, t := range texts {
issues = append(issues, newTextIssue(t))
}
processedIssues := process(t, p, issues...)
assert.Len(t, processedIssues, len(issues)-1)
var processedTexts []string
for _, i := range processedIssues {
processedTexts = append(processedTexts, i.Text)
}
assert.Equal(t, texts[:len(texts)-1], processedTexts)
}

View File

@ -0,0 +1,3 @@
package testdata
//GO:generate long line that will be excluded by default processor but will not be affected by case-sensitive one because of capital GO