golangci-lint/pkg/result/processors/autogenerated_exclude.go
Denis Isaev 7f833070b1
Properly detect generated files: fix detection when
there is extra line between comment about generated file and package
name
2018-06-11 12:38:52 +03:00

115 lines
2.6 KiB
Go

package processors
import (
"fmt"
"go/ast"
"go/token"
"strings"
"github.com/golangci/golangci-lint/pkg/lint/astcache"
"github.com/golangci/golangci-lint/pkg/result"
)
type ageFileSummary struct {
isGenerated bool
}
type ageFileSummaryCache map[string]*ageFileSummary
type AutogeneratedExclude struct {
fileSummaryCache ageFileSummaryCache
astCache *astcache.Cache
}
func NewAutogeneratedExclude(astCache *astcache.Cache) *AutogeneratedExclude {
return &AutogeneratedExclude{
fileSummaryCache: ageFileSummaryCache{},
astCache: astCache,
}
}
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 (p *AutogeneratedExclude) shouldPassIssue(i *result.Issue) (bool, error) {
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) {
return true
}
}
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
f := p.astCache.GetOrParse(i.FilePath())
if f.Err != nil {
return nil, fmt.Errorf("can't parse file %s: %s", i.FilePath(), f.Err)
}
doc := getDoc(f.F, f.Fset)
fs.isGenerated = isGeneratedFileByComment(doc)
return fs, nil
}
func getDoc(f *ast.File, fset *token.FileSet) string {
// don't use just f.Doc: e.g. mockgen leaves extra line between comment and package name
importPos := f.End()
if len(f.Imports) != 0 {
importPos = f.Imports[0].Pos()
}
var neededComments []string
for _, g := range f.Comments {
if g.Pos() < importPos && fset.Position(g.Pos()).Column == 1 {
neededComments = append(neededComments, g.Text())
}
}
if len(neededComments) == 0 {
return ""
}
return strings.Join(neededComments, "\n")
}
func (p AutogeneratedExclude) Finish() {}