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 }