github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/lint/lintersdb/enabled_set.go (about)

     1  package lintersdb
     2  
     3  import (
     4  	"os"
     5  	"sort"
     6  
     7  	"github.com/golangci/golangci-lint/pkg/config"
     8  	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
     9  	"github.com/golangci/golangci-lint/pkg/lint/linter"
    10  	"github.com/golangci/golangci-lint/pkg/logutils"
    11  )
    12  
    13  // EnvTestRun value: "1"
    14  const EnvTestRun = "GL_TEST_RUN"
    15  
    16  type EnabledSet struct {
    17  	m      *Manager
    18  	v      *Validator
    19  	log    logutils.Log
    20  	cfg    *config.Config
    21  	debugf logutils.DebugFunc
    22  }
    23  
    24  func NewEnabledSet(m *Manager, v *Validator, log logutils.Log, cfg *config.Config) *EnabledSet {
    25  	return &EnabledSet{
    26  		m:      m,
    27  		v:      v,
    28  		log:    log,
    29  		cfg:    cfg,
    30  		debugf: logutils.Debug(logutils.DebugKeyEnabledLinters),
    31  	}
    32  }
    33  
    34  func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*linter.Config) map[string]*linter.Config {
    35  	es.debugf("Linters config: %#v", lcfg)
    36  	resultLintersSet := map[string]*linter.Config{}
    37  	switch {
    38  	case len(lcfg.Presets) != 0:
    39  		break // imply --disable-all
    40  	case lcfg.EnableAll:
    41  		resultLintersSet = linterConfigsToMap(es.m.GetAllSupportedLinterConfigs())
    42  	case lcfg.DisableAll:
    43  		break
    44  	default:
    45  		resultLintersSet = linterConfigsToMap(enabledByDefaultLinters)
    46  	}
    47  
    48  	// --presets can only add linters to default set
    49  	for _, p := range lcfg.Presets {
    50  		for _, lc := range es.m.GetAllLinterConfigsForPreset(p) {
    51  			lc := lc
    52  			resultLintersSet[lc.Name()] = lc
    53  		}
    54  	}
    55  
    56  	// --fast removes slow linters from current set.
    57  	// It should be after --presets to be able to run only fast linters in preset.
    58  	// It should be before --enable and --disable to be able to enable or disable specific linter.
    59  	if lcfg.Fast {
    60  		for name, lc := range resultLintersSet {
    61  			if lc.IsSlowLinter() {
    62  				delete(resultLintersSet, name)
    63  			}
    64  		}
    65  	}
    66  
    67  	for _, name := range lcfg.Enable {
    68  		for _, lc := range es.m.GetLinterConfigs(name) {
    69  			// it's important to use lc.Name() nor name because name can be alias
    70  			resultLintersSet[lc.Name()] = lc
    71  		}
    72  	}
    73  
    74  	for _, name := range lcfg.Disable {
    75  		for _, lc := range es.m.GetLinterConfigs(name) {
    76  			// it's important to use lc.Name() nor name because name can be alias
    77  			delete(resultLintersSet, lc.Name())
    78  		}
    79  	}
    80  
    81  	return resultLintersSet
    82  }
    83  
    84  func (es EnabledSet) GetEnabledLintersMap() (map[string]*linter.Config, error) {
    85  	if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	enabledLinters := es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters())
    90  	if os.Getenv(EnvTestRun) == "1" {
    91  		es.verbosePrintLintersStatus(enabledLinters)
    92  	}
    93  	return enabledLinters, nil
    94  }
    95  
    96  // GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters
    97  // into a fewer number of linters. E.g. some go/analysis linters can be optimized into
    98  // one metalinter for data reuse and speed up.
    99  func (es EnabledSet) GetOptimizedLinters() ([]*linter.Config, error) {
   100  	if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	resultLintersSet := es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters())
   105  	es.verbosePrintLintersStatus(resultLintersSet)
   106  	es.combineGoAnalysisLinters(resultLintersSet)
   107  
   108  	var resultLinters []*linter.Config
   109  	for _, lc := range resultLintersSet {
   110  		resultLinters = append(resultLinters, lc)
   111  	}
   112  
   113  	// Make order of execution of linters (go/analysis metalinter and unused) stable.
   114  	sort.Slice(resultLinters, func(i, j int) bool {
   115  		a, b := resultLinters[i], resultLinters[j]
   116  
   117  		if b.Name() == linter.LastLinter {
   118  			return true
   119  		}
   120  
   121  		if a.Name() == linter.LastLinter {
   122  			return false
   123  		}
   124  
   125  		if a.DoesChangeTypes != b.DoesChangeTypes {
   126  			return b.DoesChangeTypes // move type-changing linters to the end to optimize speed
   127  		}
   128  		return a.Name() < b.Name()
   129  	})
   130  
   131  	return resultLinters, nil
   132  }
   133  
   134  func (es EnabledSet) combineGoAnalysisLinters(linters map[string]*linter.Config) {
   135  	var goanalysisLinters []*goanalysis.Linter
   136  	goanalysisPresets := map[string]bool{}
   137  	for _, linter := range linters {
   138  		lnt, ok := linter.Linter.(*goanalysis.Linter)
   139  		if !ok {
   140  			continue
   141  		}
   142  		if lnt.LoadMode() == goanalysis.LoadModeWholeProgram {
   143  			// It's ineffective by CPU and memory to run whole-program and incremental analyzers at once.
   144  			continue
   145  		}
   146  		goanalysisLinters = append(goanalysisLinters, lnt)
   147  		for _, p := range linter.InPresets {
   148  			goanalysisPresets[p] = true
   149  		}
   150  	}
   151  
   152  	if len(goanalysisLinters) <= 1 {
   153  		es.debugf("Didn't combine go/analysis linters: got only %d linters", len(goanalysisLinters))
   154  		return
   155  	}
   156  
   157  	for _, lnt := range goanalysisLinters {
   158  		delete(linters, lnt.Name())
   159  	}
   160  
   161  	// Make order of execution of go/analysis analyzers stable.
   162  	sort.Slice(goanalysisLinters, func(i, j int) bool {
   163  		a, b := goanalysisLinters[i], goanalysisLinters[j]
   164  
   165  		if b.Name() == linter.LastLinter {
   166  			return true
   167  		}
   168  
   169  		if a.Name() == linter.LastLinter {
   170  			return false
   171  		}
   172  
   173  		return a.Name() <= b.Name()
   174  	})
   175  
   176  	ml := goanalysis.NewMetaLinter(goanalysisLinters)
   177  
   178  	var presets []string
   179  	for p := range goanalysisPresets {
   180  		presets = append(presets, p)
   181  	}
   182  
   183  	mlConfig := &linter.Config{
   184  		Linter:           ml,
   185  		EnabledByDefault: false,
   186  		InPresets:        presets,
   187  		AlternativeNames: nil,
   188  		OriginalURL:      "",
   189  	}
   190  
   191  	mlConfig = mlConfig.WithLoadForGoAnalysis()
   192  
   193  	linters[ml.Name()] = mlConfig
   194  	es.debugf("Combined %d go/analysis linters into one metalinter", len(goanalysisLinters))
   195  }
   196  
   197  func (es EnabledSet) verbosePrintLintersStatus(lcs map[string]*linter.Config) {
   198  	var linterNames []string
   199  	for _, lc := range lcs {
   200  		linterNames = append(linterNames, lc.Name())
   201  	}
   202  	sort.StringSlice(linterNames).Sort()
   203  	es.log.Infof("Active %d linters: %s", len(linterNames), linterNames)
   204  
   205  	if len(es.cfg.Linters.Presets) != 0 {
   206  		sort.StringSlice(es.cfg.Linters.Presets).Sort()
   207  		es.log.Infof("Active presets: %s", es.cfg.Linters.Presets)
   208  	}
   209  }