Skip to content

Commit 59397a3

Browse files
committed
refactor: split processor and matcher
1 parent 2b24c4e commit 59397a3

File tree

7 files changed

+287
-259
lines changed

7 files changed

+287
-259
lines changed

pkg/commands/flagsets.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func setupIssuesFlagSet(v *viper.Viper, fs *pflag.FlagSet) {
102102
internal.AddFlagAndBind(v, fs, fs.Bool, "exclude-dirs-use-default", "issues.exclude-dirs-use-default", true,
103103
formatList("Use or not use default excluded directories:", processors.StdExcludeDirRegexps))
104104

105-
internal.AddFlagAndBind(v, fs, fs.String, "exclude-generated", "issues.exclude-generated", processors.AutogeneratedModeLax,
105+
internal.AddFlagAndBind(v, fs, fs.String, "exclude-generated", "issues.exclude-generated", config.GeneratedModeLax,
106106
color.GreenString("Mode of the generated files analysis"))
107107

108108
const newDesc = "Show only new issues: if there are unstaged changes or untracked files, only those changes " +

pkg/config/linters_exclusions.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import (
55
"slices"
66
)
77

8+
const (
9+
GeneratedModeLax = "lax"
10+
GeneratedModeStrict = "strict"
11+
GeneratedModeDisable = "disable"
12+
)
13+
814
const (
915
ExclusionPresetComments = "comments"
1016
ExclusionPresetStdErrorHandling = "stdErrorHandling"

pkg/config/loader.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func (l *Loader) Load(opts LoadOptions) error {
7070

7171
if l.cfg.Linters.LinterExclusions.Generated == "" {
7272
// `l.cfg.Issues.ExcludeGenerated` is always non-empty because of the flag default value.
73-
l.cfg.Linters.LinterExclusions.Generated = cmp.Or(l.cfg.Issues.ExcludeGenerated, "strict")
73+
l.cfg.Linters.LinterExclusions.Generated = cmp.Or(l.cfg.Issues.ExcludeGenerated, GeneratedModeStrict)
7474
}
7575

7676
// Compatibility layer with v1.

pkg/result/processors/exclusion_generated_file_filter.go

Lines changed: 13 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,36 @@ package processors
22

33
import (
44
"fmt"
5-
"go/parser"
6-
"go/token"
75
"path/filepath"
8-
"regexp"
9-
"strings"
106

7+
"github.com/golangci/golangci-lint/pkg/config"
118
"github.com/golangci/golangci-lint/pkg/logutils"
129
"github.com/golangci/golangci-lint/pkg/result"
1310
)
1411

15-
const (
16-
AutogeneratedModeLax = "lax"
17-
AutogeneratedModeStrict = "strict"
18-
AutogeneratedModeDisable = "disable"
19-
)
20-
21-
// The values must be in lowercase.
22-
const (
23-
genCodeGenerated = "code generated"
24-
genDoNotEdit = "do not edit"
25-
26-
// Related to easyjson.
27-
genAutoFile = "autogenerated file"
28-
29-
//nolint:lll // Long URL
30-
// Related to Swagger Codegen.
31-
// https://github.com/swagger-api/swagger-codegen/blob/61cfeac3b9d855b4eb8bffa0d118bece117bcb7d/modules/swagger-codegen/src/main/resources/go/partial_header.mustache#L16
32-
// https://github.com/swagger-api/swagger-codegen/issues/12358
33-
genSwaggerCodegen = "* generated by: swagger codegen "
34-
)
35-
3612
var _ Processor = (*GeneratedFileFilter)(nil)
3713

3814
type fileSummary struct {
3915
generated bool
4016
}
4117

4218
// GeneratedFileFilter filters generated files.
43-
// - mode "lax": see `isGeneratedFileLax` documentation.
44-
// - mode "strict": see `isGeneratedFileStrict` documentation.
45-
// - mode "disable": skips this processor.
4619
type GeneratedFileFilter struct {
4720
debugf logutils.DebugFunc
4821

49-
mode string
50-
strictPattern *regexp.Regexp
22+
mode string
23+
matcher *GeneratedFileMatcher
5124

5225
fileSummaryCache map[string]*fileSummary
5326
}
5427

5528
func NewGeneratedFileFilter(mode string) *GeneratedFileFilter {
5629
return &GeneratedFileFilter{
57-
debugf: logutils.Debug(logutils.DebugKeyGeneratedFileFilter),
58-
mode: mode,
59-
strictPattern: regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`),
30+
debugf: logutils.Debug(logutils.DebugKeyGeneratedFileFilter),
31+
32+
mode: mode,
33+
matcher: NewGeneratedFileMatcher(mode),
34+
6035
fileSummaryCache: map[string]*fileSummary{},
6136
}
6237
}
@@ -66,7 +41,7 @@ func (*GeneratedFileFilter) Name() string {
6641
}
6742

6843
func (p *GeneratedFileFilter) Process(issues []result.Issue) ([]result.Issue, error) {
69-
if p.mode == AutogeneratedModeDisable {
44+
if p.mode == config.GeneratedModeDisable {
7045
return issues, nil
7146
}
7247

@@ -89,93 +64,14 @@ func (p *GeneratedFileFilter) shouldPassIssue(issue *result.Issue) (bool, error)
8964
fs = &fileSummary{}
9065
p.fileSummaryCache[issue.FilePath()] = fs
9166

92-
if p.mode == AutogeneratedModeStrict {
93-
var err error
94-
fs.generated, err = p.isGeneratedFileStrict(issue.FilePath())
95-
if err != nil {
96-
return false, fmt.Errorf("failed to get doc (strict) of file %s: %w", issue.FilePath(), err)
97-
}
98-
} else {
99-
doc, err := getComments(issue.FilePath())
100-
if err != nil {
101-
return false, fmt.Errorf("failed to get doc (lax) of file %s: %w", issue.FilePath(), err)
102-
}
103-
104-
fs.generated = p.isGeneratedFileLax(doc)
67+
var err error
68+
fs.generated, err = p.matcher.IsGeneratedFile(issue.FilePath(), nil)
69+
if err != nil {
70+
return false, fmt.Errorf("failed to get doc (%s) of file %s: %w", p.mode, issue.FilePath(), err)
10571
}
10672

10773
p.debugf("file %q is generated: %t", issue.FilePath(), fs.generated)
10874

10975
// don't report issues for autogenerated files
11076
return !fs.generated, nil
11177
}
112-
113-
// isGeneratedFileLax reports whether the source file is generated code.
114-
// The function uses a bit laxer rules than isGeneratedFileStrict to match more generated code.
115-
// See https://github.com/golangci/golangci-lint/issues/48 and https://github.com/golangci/golangci-lint/issues/72.
116-
func (p *GeneratedFileFilter) isGeneratedFileLax(doc string) bool {
117-
markers := []string{genCodeGenerated, genDoNotEdit, genAutoFile, genSwaggerCodegen}
118-
119-
doc = strings.ToLower(doc)
120-
121-
for _, marker := range markers {
122-
if strings.Contains(doc, marker) {
123-
p.debugf("doc contains marker %q: file is generated", marker)
124-
125-
return true
126-
}
127-
}
128-
129-
p.debugf("doc of len %d doesn't contain any of markers: %s", len(doc), markers)
130-
131-
return false
132-
}
133-
134-
// isGeneratedFileStrict returns true if the source file has a line that matches the regular expression:
135-
//
136-
// ^// Code generated .* DO NOT EDIT\.$
137-
//
138-
// This line must appear before the first non-comment, non-blank text in the file.
139-
// Based on https://go.dev/s/generatedcode.
140-
func (p *GeneratedFileFilter) isGeneratedFileStrict(filePath string) (bool, error) {
141-
file, err := parser.ParseFile(token.NewFileSet(), filePath, nil, parser.PackageClauseOnly|parser.ParseComments)
142-
if err != nil {
143-
return false, fmt.Errorf("failed to parse file: %w", err)
144-
}
145-
146-
if file == nil || len(file.Comments) == 0 {
147-
return false, nil
148-
}
149-
150-
for _, comment := range file.Comments {
151-
if comment.Pos() > file.Package {
152-
return false, nil
153-
}
154-
155-
for _, line := range comment.List {
156-
generated := p.strictPattern.MatchString(line.Text)
157-
if generated {
158-
p.debugf("doc contains ignore expression: file is generated")
159-
160-
return true, nil
161-
}
162-
}
163-
}
164-
165-
return false, nil
166-
}
167-
168-
func getComments(filePath string) (string, error) {
169-
fset := token.NewFileSet()
170-
syntax, err := parser.ParseFile(fset, filePath, nil, parser.PackageClauseOnly|parser.ParseComments)
171-
if err != nil {
172-
return "", fmt.Errorf("failed to parse file: %w", err)
173-
}
174-
175-
var docLines []string
176-
for _, c := range syntax.Comments {
177-
docLines = append(docLines, strings.TrimSpace(c.Text()))
178-
}
179-
180-
return strings.Join(docLines, "\n"), nil
181-
}

pkg/result/processors/exclusion_generated_file_filter_test.go

Lines changed: 5 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -10,145 +10,10 @@ import (
1010
"github.com/stretchr/testify/assert"
1111
"github.com/stretchr/testify/require"
1212

13+
"github.com/golangci/golangci-lint/pkg/config"
1314
"github.com/golangci/golangci-lint/pkg/result"
1415
)
1516

16-
func TestGeneratedFileFilter_isGeneratedFileLax_generated(t *testing.T) {
17-
p := NewGeneratedFileFilter(AutogeneratedModeLax)
18-
19-
comments := []string{
20-
` // generated by stringer -type Pill pill.go; DO NOT EDIT`,
21-
`// Code generated by "stringer -type Pill pill.go"; DO NOT EDIT`,
22-
`// Code generated by vfsgen; DO NOT EDIT`,
23-
`// Created by cgo -godefs - DO NOT EDIT`,
24-
`/* Created by cgo - DO NOT EDIT. */`,
25-
`// Generated by stringer -i a.out.go -o anames.go -p ppc64
26-
// Do not edit.`,
27-
`// DO NOT EDIT
28-
// generated by: x86map -fmt=decoder ../x86.csv`,
29-
`// DO NOT EDIT.
30-
// Generate with: go run gen.go -full -output md5block.go`,
31-
`// generated by "go run gen.go". DO NOT EDIT.`,
32-
`// DO NOT EDIT. This file is generated by mksyntaxgo from the RE2 distribution.`,
33-
`// GENERATED BY make_perl_groups.pl; DO NOT EDIT.`,
34-
`// generated by mknacl.sh - do not edit`,
35-
`// DO NOT EDIT ** This file was generated with the bake tool ** DO NOT EDIT //`,
36-
`// Generated by running
37-
// maketables --tables=all --data=http://www.unicode.org/Public/8.0.0/ucd/UnicodeData.txt
38-
// --casefolding=http://www.unicode.org/Public/8.0.0/ucd/CaseFolding.txt
39-
// DO NOT EDIT`,
40-
`/*
41-
* CODE GENERATED AUTOMATICALLY WITH github.com/ernesto-jimenez/gogen/unmarshalmap
42-
* THIS FILE SHOULD NOT BE EDITED BY HAND
43-
*/`,
44-
`// AUTOGENERATED FILE: easyjson file.go`,
45-
` * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)`,
46-
}
47-
48-
for _, comment := range comments {
49-
t.Run(comment, func(t *testing.T) {
50-
t.Parallel()
51-
52-
generated := p.isGeneratedFileLax(comment)
53-
assert.True(t, generated)
54-
})
55-
}
56-
}
57-
58-
func TestGeneratedFileFilter_isGeneratedFileLax_nonGenerated(t *testing.T) {
59-
p := NewGeneratedFileFilter(AutogeneratedModeLax)
60-
61-
comments := []string{
62-
"code not generated by",
63-
"test",
64-
}
65-
66-
for _, comment := range comments {
67-
t.Run(comment, func(t *testing.T) {
68-
t.Parallel()
69-
70-
generated := p.isGeneratedFileLax(comment)
71-
assert.False(t, generated)
72-
})
73-
}
74-
}
75-
76-
func TestGeneratedFileFilter_isGeneratedFileStrict(t *testing.T) {
77-
p := NewGeneratedFileFilter(AutogeneratedModeStrict)
78-
79-
testCases := []struct {
80-
desc string
81-
filepath string
82-
assert assert.BoolAssertionFunc
83-
}{
84-
{
85-
desc: "go strict",
86-
filepath: filepath.FromSlash("testdata/exclusion_generated_file_filter/go_strict.go"),
87-
assert: assert.True,
88-
},
89-
{
90-
desc: "go strict invalid",
91-
filepath: filepath.FromSlash("testdata/exclusion_generated_file_filter/go_strict_invalid.go"),
92-
assert: assert.False,
93-
},
94-
}
95-
96-
for _, test := range testCases {
97-
t.Run(test.desc, func(t *testing.T) {
98-
t.Parallel()
99-
100-
generated, err := p.isGeneratedFileStrict(test.filepath)
101-
require.NoError(t, err)
102-
103-
test.assert(t, generated)
104-
})
105-
}
106-
}
107-
108-
func Test_getComments(t *testing.T) {
109-
testCases := []struct {
110-
fpath string
111-
doc string
112-
}{
113-
{
114-
fpath: filepath.FromSlash("testdata/exclusion_generated_file_filter/exclude.go"),
115-
doc: `first line
116-
second line
117-
third line
118-
this text also
119-
and this text also`,
120-
},
121-
{
122-
fpath: filepath.FromSlash("testdata/exclusion_generated_file_filter/exclude_doc.go"),
123-
doc: `DO NOT EDIT`,
124-
},
125-
{
126-
fpath: filepath.FromSlash("testdata/exclusion_generated_file_filter/exclude_block_comment.go"),
127-
doc: `* first line
128-
*
129-
* second line
130-
* third line
131-
and this text also
132-
this type of block comment also
133-
this one line comment also`,
134-
},
135-
}
136-
137-
for _, tc := range testCases {
138-
doc, err := getComments(tc.fpath)
139-
require.NoError(t, err)
140-
assert.Equal(t, tc.doc, doc)
141-
}
142-
}
143-
144-
// Issue 954: Some lines can be very long, e.g. auto-generated
145-
// embedded resources. Reported on file of 86.2KB.
146-
func Test_getComments_fileWithLongLine(t *testing.T) {
147-
fpath := filepath.FromSlash("testdata/exclusion_generated_file_filter/exclude_long_line.go")
148-
_, err := getComments(fpath)
149-
assert.NoError(t, err)
150-
}
151-
15217
func TestGeneratedFileFilter_shouldPassIssue(t *testing.T) {
15318
testCases := []struct {
15419
desc string
@@ -158,7 +23,7 @@ func TestGeneratedFileFilter_shouldPassIssue(t *testing.T) {
15823
}{
15924
{
16025
desc: "lax ",
161-
mode: AutogeneratedModeLax,
26+
mode: config.GeneratedModeLax,
16227
issue: &result.Issue{
16328
FromLinter: "example",
16429
Pos: token.Position{
@@ -169,7 +34,7 @@ func TestGeneratedFileFilter_shouldPassIssue(t *testing.T) {
16934
},
17035
{
17136
desc: "strict ",
172-
mode: AutogeneratedModeStrict,
37+
mode: config.GeneratedModeStrict,
17338
issue: &result.Issue{
17439
FromLinter: "example",
17540
Pos: token.Position{
@@ -208,7 +73,7 @@ func TestGeneratedFileFilter_shouldPassIssue_error(t *testing.T) {
20873
}{
20974
{
21075
desc: "non-existing file (lax)",
211-
mode: AutogeneratedModeLax,
76+
mode: config.GeneratedModeLax,
21277
issue: &result.Issue{
21378
FromLinter: "example",
21479
Pos: token.Position{
@@ -220,7 +85,7 @@ func TestGeneratedFileFilter_shouldPassIssue_error(t *testing.T) {
22085
},
22186
{
22287
desc: "non-existing file (strict)",
223-
mode: AutogeneratedModeStrict,
88+
mode: config.GeneratedModeStrict,
22489
issue: &result.Issue{
22590
FromLinter: "example",
22691
Pos: token.Position{

0 commit comments

Comments
 (0)