 9aea4aee1c
			
		
	
	
		9aea4aee1c
		
			
		
	
	
	
		
			
	
		
	
	
		
			Some checks failed
		
		
	
	Extra / Vulnerability scanner (push) Has been cancelled
				CI / go-mod (push) Has been cancelled
				CI / golangci-lint (push) Has been cancelled
				Release a tag / release (push) Has been cancelled
				CI / tests-on-windows (push) Has been cancelled
				CI / tests-on-macos (push) Has been cancelled
				CI / tests-on-unix (1.14) (push) Has been cancelled
				CI / tests-on-unix (1.15) (push) Has been cancelled
				CI / tests-on-unix (1.16) (push) Has been cancelled
				CI / check_generated (push) Has been cancelled
				Release a tag / docker-release (map[Dockerfile:build/Dockerfile.alpine]) (push) Has been cancelled
				Release a tag / docker-release (map[Dockerfile:build/Dockerfile]) (push) Has been cancelled
				
		
			
				
	
	
		
			214 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package goanalysis
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"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"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	TheOnlyAnalyzerName = "the_only_name"
 | |
| 	TheOnlyanalyzerDoc  = "the_only_doc"
 | |
| )
 | |
| 
 | |
| type LoadMode int
 | |
| 
 | |
| func (loadMode LoadMode) String() string {
 | |
| 	switch loadMode {
 | |
| 	case LoadModeNone:
 | |
| 		return "none"
 | |
| 	case LoadModeSyntax:
 | |
| 		return "syntax"
 | |
| 	case LoadModeTypesInfo:
 | |
| 		return "types info"
 | |
| 	case LoadModeWholeProgram:
 | |
| 		return "whole program"
 | |
| 	}
 | |
| 	panic(fmt.Sprintf("unknown load mode %d", loadMode))
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	LoadModeNone LoadMode = iota
 | |
| 	LoadModeSyntax
 | |
| 	LoadModeTypesInfo
 | |
| 	LoadModeWholeProgram
 | |
| )
 | |
| 
 | |
| type Linter struct {
 | |
| 	name, desc              string
 | |
| 	analyzers               []*analysis.Analyzer
 | |
| 	cfg                     map[string]map[string]interface{}
 | |
| 	issuesReporter          func(*linter.Context) []Issue
 | |
| 	contextSetter           func(*linter.Context)
 | |
| 	loadMode                LoadMode
 | |
| 	needUseOriginalPackages bool
 | |
| }
 | |
| 
 | |
| func NewLinter(name, desc string, analyzers []*analysis.Analyzer, cfg map[string]map[string]interface{}) *Linter {
 | |
| 	return &Linter{name: name, desc: desc, analyzers: analyzers, cfg: cfg}
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) Run(_ context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
 | |
| 	if err := lnt.preRun(lintCtx); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return runAnalyzers(lnt, lintCtx)
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) UseOriginalPackages() {
 | |
| 	lnt.needUseOriginalPackages = true
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) LoadMode() LoadMode {
 | |
| 	return lnt.loadMode
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) WithLoadMode(loadMode LoadMode) *Linter {
 | |
| 	lnt.loadMode = loadMode
 | |
| 	return lnt
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) WithIssuesReporter(r func(*linter.Context) []Issue) *Linter {
 | |
| 	lnt.issuesReporter = r
 | |
| 	return lnt
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) WithContextSetter(cs func(*linter.Context)) *Linter {
 | |
| 	lnt.contextSetter = cs
 | |
| 	return lnt
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) Name() string {
 | |
| 	return lnt.name
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) Desc() string {
 | |
| 	return lnt.desc
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) allAnalyzerNames() []string {
 | |
| 	var ret []string
 | |
| 	for _, a := range lnt.analyzers {
 | |
| 		ret = append(ret, a.Name)
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) configureAnalyzer(a *analysis.Analyzer, cfg map[string]interface{}) error {
 | |
| 	for k, v := range cfg {
 | |
| 		f := a.Flags.Lookup(k)
 | |
| 		if f == nil {
 | |
| 			validFlagNames := allFlagNames(&a.Flags)
 | |
| 			if len(validFlagNames) == 0 {
 | |
| 				return fmt.Errorf("analyzer doesn't have settings")
 | |
| 			}
 | |
| 
 | |
| 			return fmt.Errorf("analyzer doesn't have setting %q, valid settings: %v",
 | |
| 				k, validFlagNames)
 | |
| 		}
 | |
| 
 | |
| 		if err := f.Value.Set(valueToString(v)); err != nil {
 | |
| 			return errors.Wrapf(err, "failed to set analyzer setting %q with value %v", k, v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) configure() error {
 | |
| 	analyzersMap := map[string]*analysis.Analyzer{}
 | |
| 	for _, a := range lnt.analyzers {
 | |
| 		analyzersMap[a.Name] = a
 | |
| 	}
 | |
| 
 | |
| 	for analyzerName, analyzerSettings := range lnt.cfg {
 | |
| 		a := analyzersMap[analyzerName]
 | |
| 		if a == nil {
 | |
| 			return fmt.Errorf("settings key %q must be valid analyzer name, valid analyzers: %v",
 | |
| 				analyzerName, lnt.allAnalyzerNames())
 | |
| 		}
 | |
| 
 | |
| 		if err := lnt.configureAnalyzer(a, analyzerSettings); err != nil {
 | |
| 			return errors.Wrapf(err, "failed to configure analyzer %s", analyzerName)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) preRun(lintCtx *linter.Context) error {
 | |
| 	if err := analysis.Validate(lnt.analyzers); err != nil {
 | |
| 		return errors.Wrap(err, "failed to validate analyzers")
 | |
| 	}
 | |
| 
 | |
| 	if err := lnt.configure(); err != nil {
 | |
| 		return errors.Wrap(err, "failed to configure analyzers")
 | |
| 	}
 | |
| 
 | |
| 	if lnt.contextSetter != nil {
 | |
| 		lnt.contextSetter(lintCtx)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) getName() string {
 | |
| 	return lnt.name
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) getLinterNameForDiagnostic(*Diagnostic) string {
 | |
| 	return lnt.name
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) getAnalyzers() []*analysis.Analyzer {
 | |
| 	return lnt.analyzers
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) useOriginalPackages() bool {
 | |
| 	return lnt.needUseOriginalPackages
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) reportIssues(lintCtx *linter.Context) []Issue {
 | |
| 	if lnt.issuesReporter != nil {
 | |
| 		return lnt.issuesReporter(lintCtx)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (lnt *Linter) getLoadMode() LoadMode {
 | |
| 	return lnt.loadMode
 | |
| }
 | |
| 
 | |
| func allFlagNames(fs *flag.FlagSet) []string {
 | |
| 	var ret []string
 | |
| 	fs.VisitAll(func(f *flag.Flag) {
 | |
| 		ret = append(ret, f.Name)
 | |
| 	})
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func valueToString(v interface{}) string {
 | |
| 	if ss, ok := v.([]string); ok {
 | |
| 		return strings.Join(ss, ",")
 | |
| 	}
 | |
| 
 | |
| 	if is, ok := v.([]interface{}); ok {
 | |
| 		var ss []string
 | |
| 		for _, i := range is {
 | |
| 			ss = append(ss, fmt.Sprint(i))
 | |
| 		}
 | |
| 
 | |
| 		return valueToString(ss)
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprint(v)
 | |
| }
 |