github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/golinters/goanalysis/linter.go (about)

     1  package goanalysis
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/pkg/errors"
    10  	"golang.org/x/tools/go/analysis"
    11  
    12  	"github.com/golangci/golangci-lint/pkg/lint/linter"
    13  	"github.com/golangci/golangci-lint/pkg/result"
    14  )
    15  
    16  const (
    17  	TheOnlyAnalyzerName = "the_only_name"
    18  	TheOnlyanalyzerDoc  = "the_only_doc"
    19  )
    20  
    21  type LoadMode int
    22  
    23  func (loadMode LoadMode) String() string {
    24  	switch loadMode {
    25  	case LoadModeNone:
    26  		return "none"
    27  	case LoadModeSyntax:
    28  		return "syntax"
    29  	case LoadModeTypesInfo:
    30  		return "types info"
    31  	case LoadModeWholeProgram:
    32  		return "whole program"
    33  	}
    34  	panic(fmt.Sprintf("unknown load mode %d", loadMode))
    35  }
    36  
    37  const (
    38  	LoadModeNone LoadMode = iota
    39  	LoadModeSyntax
    40  	LoadModeTypesInfo
    41  	LoadModeWholeProgram
    42  )
    43  
    44  type Linter struct {
    45  	name, desc              string
    46  	analyzers               []*analysis.Analyzer
    47  	cfg                     map[string]map[string]interface{}
    48  	issuesReporter          func(*linter.Context) []Issue
    49  	contextSetter           func(*linter.Context)
    50  	loadMode                LoadMode
    51  	needUseOriginalPackages bool
    52  }
    53  
    54  func NewLinter(name, desc string, analyzers []*analysis.Analyzer, cfg map[string]map[string]interface{}) *Linter {
    55  	return &Linter{name: name, desc: desc, analyzers: analyzers, cfg: cfg}
    56  }
    57  
    58  func (lnt *Linter) Run(_ context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
    59  	if err := lnt.preRun(lintCtx); err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return runAnalyzers(lnt, lintCtx)
    64  }
    65  
    66  func (lnt *Linter) UseOriginalPackages() {
    67  	lnt.needUseOriginalPackages = true
    68  }
    69  
    70  func (lnt *Linter) LoadMode() LoadMode {
    71  	return lnt.loadMode
    72  }
    73  
    74  func (lnt *Linter) WithLoadMode(loadMode LoadMode) *Linter {
    75  	lnt.loadMode = loadMode
    76  	return lnt
    77  }
    78  
    79  func (lnt *Linter) WithIssuesReporter(r func(*linter.Context) []Issue) *Linter {
    80  	lnt.issuesReporter = r
    81  	return lnt
    82  }
    83  
    84  func (lnt *Linter) WithContextSetter(cs func(*linter.Context)) *Linter {
    85  	lnt.contextSetter = cs
    86  	return lnt
    87  }
    88  
    89  func (lnt *Linter) Name() string {
    90  	return lnt.name
    91  }
    92  
    93  func (lnt *Linter) Desc() string {
    94  	return lnt.desc
    95  }
    96  
    97  func (lnt *Linter) allAnalyzerNames() []string {
    98  	var ret []string
    99  	for _, a := range lnt.analyzers {
   100  		ret = append(ret, a.Name)
   101  	}
   102  	return ret
   103  }
   104  
   105  func (lnt *Linter) configureAnalyzer(a *analysis.Analyzer, cfg map[string]interface{}) error {
   106  	for k, v := range cfg {
   107  		f := a.Flags.Lookup(k)
   108  		if f == nil {
   109  			validFlagNames := allFlagNames(&a.Flags)
   110  			if len(validFlagNames) == 0 {
   111  				return errors.New("analyzer doesn't have settings")
   112  			}
   113  
   114  			return fmt.Errorf("analyzer doesn't have setting %q, valid settings: %v",
   115  				k, validFlagNames)
   116  		}
   117  
   118  		if err := f.Value.Set(valueToString(v)); err != nil {
   119  			return errors.Wrapf(err, "failed to set analyzer setting %q with value %v", k, v)
   120  		}
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func (lnt *Linter) configure() error {
   127  	analyzersMap := map[string]*analysis.Analyzer{}
   128  	for _, a := range lnt.analyzers {
   129  		analyzersMap[a.Name] = a
   130  	}
   131  
   132  	for analyzerName, analyzerSettings := range lnt.cfg {
   133  		a := analyzersMap[analyzerName]
   134  		if a == nil {
   135  			return fmt.Errorf("settings key %q must be valid analyzer name, valid analyzers: %v",
   136  				analyzerName, lnt.allAnalyzerNames())
   137  		}
   138  
   139  		if err := lnt.configureAnalyzer(a, analyzerSettings); err != nil {
   140  			return errors.Wrapf(err, "failed to configure analyzer %s", analyzerName)
   141  		}
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  func (lnt *Linter) preRun(lintCtx *linter.Context) error {
   148  	if err := analysis.Validate(lnt.analyzers); err != nil {
   149  		return errors.Wrap(err, "failed to validate analyzers")
   150  	}
   151  
   152  	if err := lnt.configure(); err != nil {
   153  		return errors.Wrap(err, "failed to configure analyzers")
   154  	}
   155  
   156  	if lnt.contextSetter != nil {
   157  		lnt.contextSetter(lintCtx)
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func (lnt *Linter) getName() string {
   164  	return lnt.name
   165  }
   166  
   167  func (lnt *Linter) getLinterNameForDiagnostic(*Diagnostic) string {
   168  	return lnt.name
   169  }
   170  
   171  func (lnt *Linter) getAnalyzers() []*analysis.Analyzer {
   172  	return lnt.analyzers
   173  }
   174  
   175  func (lnt *Linter) useOriginalPackages() bool {
   176  	return lnt.needUseOriginalPackages
   177  }
   178  
   179  func (lnt *Linter) reportIssues(lintCtx *linter.Context) []Issue {
   180  	if lnt.issuesReporter != nil {
   181  		return lnt.issuesReporter(lintCtx)
   182  	}
   183  	return nil
   184  }
   185  
   186  func (lnt *Linter) getLoadMode() LoadMode {
   187  	return lnt.loadMode
   188  }
   189  
   190  func allFlagNames(fs *flag.FlagSet) []string {
   191  	var ret []string
   192  	fs.VisitAll(func(f *flag.Flag) {
   193  		ret = append(ret, f.Name)
   194  	})
   195  	return ret
   196  }
   197  
   198  func valueToString(v interface{}) string {
   199  	if ss, ok := v.([]string); ok {
   200  		return strings.Join(ss, ",")
   201  	}
   202  
   203  	if is, ok := v.([]interface{}); ok {
   204  		var ss []string
   205  		for _, i := range is {
   206  			ss = append(ss, fmt.Sprint(i))
   207  		}
   208  
   209  		return valueToString(ss)
   210  	}
   211  
   212  	return fmt.Sprint(v)
   213  }
   214  
   215  func DummyRun(_ *analysis.Pass) (interface{}, error) {
   216  	return nil, nil
   217  }