Run all linters per package. It allows unloading package data when it's processed. It dramatically reduces memory (and CPU because of GC) usage. Relates: #337
		
			
				
	
	
		
			136 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package goanalysis
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"golang.org/x/tools/go/analysis"
 | 
						|
 | 
						|
	"github.com/golangci/golangci-lint/pkg/lint/linter"
 | 
						|
	"github.com/golangci/golangci-lint/pkg/result"
 | 
						|
)
 | 
						|
 | 
						|
type MetaLinter struct {
 | 
						|
	linters []*Linter
 | 
						|
}
 | 
						|
 | 
						|
func NewMetaLinter(linters []*Linter) *MetaLinter {
 | 
						|
	return &MetaLinter{linters: linters}
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) Name() string {
 | 
						|
	return "goanalysis_metalinter"
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) Desc() string {
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) isTypecheckMode() bool {
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		if linter.isTypecheckMode {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) getLoadMode() LoadMode {
 | 
						|
	loadMode := LoadModeNone
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		if linter.loadMode > loadMode {
 | 
						|
			loadMode = linter.loadMode
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return loadMode
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) runContextSetters(lintCtx *linter.Context) {
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		if linter.contextSetter != nil {
 | 
						|
			linter.contextSetter(lintCtx)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) getAllAnalyzers() []*analysis.Analyzer {
 | 
						|
	var allAnalyzers []*analysis.Analyzer
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		allAnalyzers = append(allAnalyzers, linter.analyzers...)
 | 
						|
	}
 | 
						|
	return allAnalyzers
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) getAnalyzerToLinterNameMapping() map[*analysis.Analyzer]string {
 | 
						|
	analyzerToLinterName := map[*analysis.Analyzer]string{}
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		for _, a := range linter.analyzers {
 | 
						|
			analyzerToLinterName[a] = linter.Name()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return analyzerToLinterName
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) configure() error {
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		if err := linter.configure(); err != nil {
 | 
						|
			return errors.Wrapf(err, "failed to configure analyzers of %s", linter.Name())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) validate() error {
 | 
						|
	for _, linter := range ml.linters {
 | 
						|
		if err := analysis.Validate(linter.analyzers); err != nil {
 | 
						|
			return errors.Wrapf(err, "failed to validate analyzers of %s", linter.Name())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ml MetaLinter) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
 | 
						|
	if err := ml.validate(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if err := ml.configure(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	ml.runContextSetters(lintCtx)
 | 
						|
 | 
						|
	analyzerToLinterName := ml.getAnalyzerToLinterNameMapping()
 | 
						|
 | 
						|
	runner := newRunner("metalinter", lintCtx.Log.Child("goanalysis"),
 | 
						|
		lintCtx.PkgCache, lintCtx.LoadGuard, ml.getLoadMode())
 | 
						|
 | 
						|
	diags, errs := runner.run(ml.getAllAnalyzers(), lintCtx.Packages)
 | 
						|
 | 
						|
	buildAllIssues := func() []result.Issue {
 | 
						|
		linterNameBuilder := func(diag *Diagnostic) string { return analyzerToLinterName[diag.Analyzer] }
 | 
						|
		issues := buildIssues(diags, linterNameBuilder)
 | 
						|
 | 
						|
		for _, linter := range ml.linters {
 | 
						|
			if linter.issuesReporter != nil {
 | 
						|
				issues = append(issues, linter.issuesReporter(lintCtx)...)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return issues
 | 
						|
	}
 | 
						|
 | 
						|
	if ml.isTypecheckMode() {
 | 
						|
		issues, err := buildIssuesFromErrorsForTypecheckMode(errs, lintCtx)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return append(issues, buildAllIssues()...), nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Don't print all errs: they can duplicate.
 | 
						|
	if len(errs) != 0 {
 | 
						|
		return nil, errs[0]
 | 
						|
	}
 | 
						|
 | 
						|
	return buildAllIssues(), nil
 | 
						|
}
 |