135 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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() {}
 | 
