Skip to content

Commit c881efc

Browse files
committed
feat: new exclusion paths processor
1 parent 7e3f589 commit c881efc

File tree

4 files changed

+191
-0
lines changed

4 files changed

+191
-0
lines changed

pkg/lint/runner.go

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ func NewRunner(log logutils.Log, cfg *config.Config, args []string, goenv *gouti
4646
return nil, fmt.Errorf("error creating path relativity processor: %w", err)
4747
}
4848

49+
exclusionPaths, err := processors.NewExclusionPaths(log, &cfg.Linters.LinterExclusions)
50+
if err != nil {
51+
return nil, err
52+
}
53+
4954
skipFilesProcessor, err := processors.NewSkipFiles(cfg.Issues.ExcludeFiles, cfg.Output.PathPrefix)
5055
if err != nil {
5156
return nil, err
@@ -88,6 +93,7 @@ func NewRunner(log logutils.Log, cfg *config.Config, args []string, goenv *gouti
8893
pathRelativity,
8994

9095
// Must be after PathRelativity.
96+
exclusionPaths,
9197
skipFilesProcessor,
9298
skipDirsProcessor,
9399

pkg/logutils/logutils.go

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242

4343
// Processors.
4444
const (
45+
DebugKeyExclusionPaths = "exclusion_paths"
4546
DebugKeyExclusionRules = "exclusion_rules"
4647
DebugKeyFilenameUnadjuster = "filename_unadjuster"
4748
DebugKeyGeneratedFileFilter = "generated_file_filter" // Debugs a filter excluding autogenerated source code.
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package processors
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
7+
"github.com/golangci/golangci-lint/pkg/config"
8+
"github.com/golangci/golangci-lint/pkg/fsutils"
9+
"github.com/golangci/golangci-lint/pkg/logutils"
10+
"github.com/golangci/golangci-lint/pkg/result"
11+
)
12+
13+
var _ Processor = (*ExclusionPaths)(nil)
14+
15+
type ExclusionPaths struct {
16+
patterns []*regexp.Regexp
17+
18+
warnUnused bool
19+
skippedPathCounter map[*regexp.Regexp]int
20+
21+
log logutils.Log
22+
}
23+
24+
func NewExclusionPaths(log logutils.Log, cfg *config.LinterExclusions) (*ExclusionPaths, error) {
25+
var counter = make(map[*regexp.Regexp]int)
26+
27+
var patternsRe []*regexp.Regexp
28+
for _, p := range cfg.Paths {
29+
p = fsutils.NormalizePathInRegex(p)
30+
31+
patternRe, err := regexp.Compile(p)
32+
if err != nil {
33+
return nil, fmt.Errorf("can't compile regexp %q: %w", p, err)
34+
}
35+
36+
patternsRe = append(patternsRe, patternRe)
37+
counter[patternRe] = 0
38+
}
39+
40+
return &ExclusionPaths{
41+
patterns: patternsRe,
42+
warnUnused: cfg.WarnUnused,
43+
skippedPathCounter: counter,
44+
log: log.Child(logutils.DebugKeyExclusionPaths),
45+
}, nil
46+
}
47+
48+
func (*ExclusionPaths) Name() string {
49+
return "exclusion_paths"
50+
}
51+
52+
func (p *ExclusionPaths) Process(issues []result.Issue) ([]result.Issue, error) {
53+
if len(p.patterns) == 0 {
54+
return issues, nil
55+
}
56+
57+
return filterIssues(issues, p.shouldPassIssue), nil
58+
}
59+
60+
func (p *ExclusionPaths) Finish() {
61+
for pattern, count := range p.skippedPathCounter {
62+
if p.warnUnused && count == 0 {
63+
p.log.Warnf("Skipped %d issues by pattern %q", count, pattern)
64+
} else {
65+
p.log.Infof("Skipped %d issues by pattern %q", count, pattern)
66+
}
67+
}
68+
}
69+
70+
func (p *ExclusionPaths) shouldPassIssue(issue *result.Issue) bool {
71+
for _, pattern := range p.patterns {
72+
if pattern.MatchString(issue.RelativePath) {
73+
p.skippedPathCounter[pattern] += 1
74+
return false
75+
}
76+
}
77+
78+
return true
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package processors
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/golangci/golangci-lint/pkg/config"
10+
"github.com/golangci/golangci-lint/pkg/logutils"
11+
"github.com/golangci/golangci-lint/pkg/result"
12+
)
13+
14+
func TestExclusionPaths_Process(t *testing.T) {
15+
logger := logutils.NewStderrLog(logutils.DebugKeyEmpty)
16+
logger.SetLevel(logutils.LogLevelDebug)
17+
18+
testCases := []struct {
19+
desc string
20+
patterns []string
21+
issues []result.Issue
22+
expected []result.Issue
23+
}{
24+
{
25+
desc: "word",
26+
patterns: []string{"foo"},
27+
issues: []result.Issue{
28+
{RelativePath: "foo.go"},
29+
{RelativePath: "foo/foo.go"},
30+
{RelativePath: "foo/bar.go"},
31+
{RelativePath: "bar/foo.go"},
32+
{RelativePath: "bar/bar.go"},
33+
},
34+
expected: []result.Issue{
35+
{RelativePath: "bar/bar.go"},
36+
},
37+
},
38+
{
39+
desc: "begin with word",
40+
patterns: []string{"^foo"},
41+
issues: []result.Issue{
42+
{RelativePath: "foo.go"},
43+
{RelativePath: "foo/foo.go"},
44+
{RelativePath: "foo/bar.go"},
45+
{RelativePath: "bar/foo.go"},
46+
{RelativePath: "bar/bar.go"},
47+
},
48+
expected: []result.Issue{
49+
{RelativePath: "bar/foo.go"},
50+
{RelativePath: "bar/bar.go"},
51+
},
52+
},
53+
{
54+
desc: "directory begin with word",
55+
patterns: []string{"^foo/"},
56+
issues: []result.Issue{
57+
{RelativePath: "foo.go"},
58+
{RelativePath: "foo/foo.go"},
59+
{RelativePath: "foo/bar.go"},
60+
{RelativePath: "bar/foo.go"},
61+
{RelativePath: "bar/bar.go"},
62+
},
63+
expected: []result.Issue{
64+
{RelativePath: "foo.go"},
65+
{RelativePath: "bar/foo.go"},
66+
{RelativePath: "bar/bar.go"},
67+
},
68+
},
69+
{
70+
desc: "same suffix with unconstrained expression",
71+
patterns: []string{"c/d.go"},
72+
issues: []result.Issue{
73+
{RelativePath: "a/b/c/d.go"},
74+
{RelativePath: "c/d.go"},
75+
},
76+
expected: []result.Issue{},
77+
},
78+
{
79+
desc: "same suffix with constrained expression",
80+
patterns: []string{"^c/d.go"},
81+
issues: []result.Issue{
82+
{RelativePath: "a/b/c/d.go"},
83+
{RelativePath: "c/d.go"},
84+
},
85+
expected: []result.Issue{
86+
{RelativePath: "a/b/c/d.go"},
87+
},
88+
},
89+
}
90+
91+
for _, test := range testCases {
92+
t.Run(test.desc, func(t *testing.T) {
93+
t.Parallel()
94+
95+
p, err := NewExclusionPaths(logger, &config.LinterExclusions{Paths: test.patterns})
96+
require.NoError(t, err)
97+
98+
processedIssues := process(t, p, test.issues...)
99+
100+
assert.Equal(t, test.expected, processedIssues)
101+
102+
p.Finish()
103+
})
104+
}
105+
}

0 commit comments

Comments
 (0)