forked from golangci/golangci-lint
/
autogenerated_exclude.go
134 lines (105 loc) · 3.33 KB
/
autogenerated_exclude.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package processors
import (
"fmt"
"go/parser"
"go/token"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/golangci/golangci-lint/pkg/logutils"
"github.com/golangci/golangci-lint/pkg/result"
)
var autogenDebugf = logutils.Debug("autogen_exclude")
type ageFileSummary struct {
isGenerated bool
}
type ageFileSummaryCache map[string]*ageFileSummary
type AutogeneratedExclude struct {
fileSummaryCache ageFileSummaryCache
}
func NewAutogeneratedExclude() *AutogeneratedExclude {
return &AutogeneratedExclude{
fileSummaryCache: ageFileSummaryCache{},
}
}
var _ Processor = &AutogeneratedExclude{}
func (p AutogeneratedExclude) Name() string {
return "autogenerated_exclude"
}
func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, error) {
return filterIssuesErr(issues, p.shouldPassIssue)
}
func isSpecialAutogeneratedFile(filePath string) bool {
fileName := filepath.Base(filePath)
// fake files or generation definitions to which //line points to for generated files
return filepath.Ext(fileName) != ".go"
}
func (p *AutogeneratedExclude) shouldPassIssue(i *result.Issue) (bool, error) {
if i.FromLinter == "typecheck" {
// don't hide typechecking errors in generated files: users expect to see why the project isn't compiling
return true, nil
}
if filepath.Base(i.FilePath()) == "go.mod" {
return true, nil
}
if isSpecialAutogeneratedFile(i.FilePath()) {
return false, nil
}
fs, err := p.getOrCreateFileSummary(i)
if err != nil {
return false, err
}
// don't report issues for autogenerated files
return !fs.isGenerated, nil
}
// isGenerated reports whether the source file is generated code.
// Using a bit laxer rules than https://golang.org/s/generatedcode to
// match more generated code. See #48 and #72.
func isGeneratedFileByComment(doc string) bool {
const (
genCodeGenerated = "code generated"
genDoNotEdit = "do not edit"
genAutoFile = "autogenerated file" // easyjson
)
markers := []string{genCodeGenerated, genDoNotEdit, genAutoFile}
doc = strings.ToLower(doc)
for _, marker := range markers {
if strings.Contains(doc, marker) {
autogenDebugf("doc contains marker %q: file is generated", marker)
return true
}
}
autogenDebugf("doc of len %d doesn't contain any of markers: %s", len(doc), markers)
return false
}
func (p *AutogeneratedExclude) getOrCreateFileSummary(i *result.Issue) (*ageFileSummary, error) {
fs := p.fileSummaryCache[i.FilePath()]
if fs != nil {
return fs, nil
}
fs = &ageFileSummary{}
p.fileSummaryCache[i.FilePath()] = fs
if i.FilePath() == "" {
return nil, fmt.Errorf("no file path for issue")
}
doc, err := getDoc(i.FilePath())
if err != nil {
return nil, errors.Wrapf(err, "failed to get doc of file %s", i.FilePath())
}
fs.isGenerated = isGeneratedFileByComment(doc)
autogenDebugf("file %q is generated: %t", i.FilePath(), fs.isGenerated)
return fs, nil
}
func getDoc(filePath string) (string, error) {
fset := token.NewFileSet()
syntax, err := parser.ParseFile(fset, filePath, nil, parser.PackageClauseOnly|parser.ParseComments)
if err != nil {
return "", errors.Wrap(err, "failed to parse file")
}
var docLines []string
for _, c := range syntax.Comments {
docLines = append(docLines, strings.TrimSpace(c.Text()))
}
return strings.Join(docLines, "\n"), nil
}
func (p AutogeneratedExclude) Finish() {}