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