Ludovic Fernandez 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
typecheck: display compilation errors as report instead of error (#1861)
2021-03-25 23:52:55 +01:00

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)
}