github.com/ldez/golangci-lint@v1.10.1/pkg/result/processors/autogenerated_exclude.go (about) 1 package processors 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 "strings" 8 9 "github.com/golangci/golangci-lint/pkg/lint/astcache" 10 "github.com/golangci/golangci-lint/pkg/logutils" 11 "github.com/golangci/golangci-lint/pkg/result" 12 ) 13 14 var autogenDebugf = logutils.Debug("autogen_exclude") 15 16 type ageFileSummary struct { 17 isGenerated bool 18 } 19 20 type ageFileSummaryCache map[string]*ageFileSummary 21 22 type AutogeneratedExclude struct { 23 fileSummaryCache ageFileSummaryCache 24 astCache *astcache.Cache 25 } 26 27 func NewAutogeneratedExclude(astCache *astcache.Cache) *AutogeneratedExclude { 28 return &AutogeneratedExclude{ 29 fileSummaryCache: ageFileSummaryCache{}, 30 astCache: astCache, 31 } 32 } 33 34 var _ Processor = &AutogeneratedExclude{} 35 36 func (p AutogeneratedExclude) Name() string { 37 return "autogenerated_exclude" 38 } 39 40 func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, error) { 41 return filterIssuesErr(issues, p.shouldPassIssue) 42 } 43 44 func (p *AutogeneratedExclude) shouldPassIssue(i *result.Issue) (bool, error) { 45 fs, err := p.getOrCreateFileSummary(i) 46 if err != nil { 47 return false, err 48 } 49 50 // don't report issues for autogenerated files 51 return !fs.isGenerated, nil 52 } 53 54 // isGenerated reports whether the source file is generated code. 55 // Using a bit laxer rules than https://golang.org/s/generatedcode to 56 // match more generated code. See #48 and #72. 57 func isGeneratedFileByComment(doc string) bool { 58 const ( 59 genCodeGenerated = "code generated" 60 genDoNotEdit = "do not edit" 61 genAutoFile = "autogenerated file" // easyjson 62 ) 63 64 markers := []string{genCodeGenerated, genDoNotEdit, genAutoFile} 65 doc = strings.ToLower(doc) 66 for _, marker := range markers { 67 if strings.Contains(doc, marker) { 68 autogenDebugf("doc contains marker %q: file is generated", marker) 69 return true 70 } 71 } 72 73 autogenDebugf("doc of len %d doesn't contain any of markers: %s", len(doc), markers) 74 return false 75 } 76 77 func (p *AutogeneratedExclude) getOrCreateFileSummary(i *result.Issue) (*ageFileSummary, error) { 78 fs := p.fileSummaryCache[i.FilePath()] 79 if fs != nil { 80 return fs, nil 81 } 82 83 fs = &ageFileSummary{} 84 p.fileSummaryCache[i.FilePath()] = fs 85 86 f := p.astCache.GetOrParse(i.FilePath()) 87 if f.Err != nil { 88 return nil, fmt.Errorf("can't parse file %s: %s", i.FilePath(), f.Err) 89 } 90 91 autogenDebugf("file %q: astcache file is %+v", i.FilePath(), *f) 92 93 doc := getDoc(f.F, f.Fset, i.FilePath()) 94 95 fs.isGenerated = isGeneratedFileByComment(doc) 96 autogenDebugf("file %q is generated: %t", i.FilePath(), fs.isGenerated) 97 return fs, nil 98 } 99 100 func getDoc(f *ast.File, fset *token.FileSet, filePath string) string { 101 // don't use just f.Doc: e.g. mockgen leaves extra line between comment and package name 102 103 var importPos token.Pos 104 if len(f.Imports) != 0 { 105 importPos = f.Imports[0].Pos() 106 autogenDebugf("file %q: search comments until first import pos %d (%s)", 107 filePath, importPos, fset.Position(importPos)) 108 } else { 109 importPos = f.End() 110 autogenDebugf("file %q: search comments until EOF pos %d (%s)", 111 filePath, importPos, fset.Position(importPos)) 112 } 113 114 var neededComments []string 115 for _, g := range f.Comments { 116 pos := g.Pos() 117 filePos := fset.Position(pos) 118 text := g.Text() 119 120 // files using cgo have implicitly added comment "Created by cgo - DO NOT EDIT" for go <= 1.10 121 // and "Code generated by cmd/cgo" for go >= 1.11 122 isCgoGenerated := strings.Contains(text, "Created by cgo") || strings.Contains(text, "Code generated by cmd/cgo") 123 124 isAllowed := pos < importPos && filePos.Column == 1 && !isCgoGenerated 125 if isAllowed { 126 autogenDebugf("file %q: pos=%d, filePos=%s: comment %q: it's allowed", filePath, pos, filePos, text) 127 neededComments = append(neededComments, text) 128 } else { 129 autogenDebugf("file %q: pos=%d, filePos=%s: comment %q: it's NOT allowed", filePath, pos, filePos, text) 130 } 131 } 132 133 autogenDebugf("file %q: got %d allowed comments", filePath, len(neededComments)) 134 135 if len(neededComments) == 0 { 136 return "" 137 } 138 139 return strings.Join(neededComments, "\n") 140 } 141 142 func (p AutogeneratedExclude) Finish() {}