github.com/outer199/golangci-lint@v1.10.1/pkg/lint/lintersdb/lintersdb.go (about) 1 package lintersdb 2 3 import ( 4 "fmt" 5 "os" 6 "sort" 7 "strings" 8 "sync" 9 10 "github.com/golangci/golangci-lint/pkg/config" 11 "github.com/golangci/golangci-lint/pkg/golinters" 12 "github.com/golangci/golangci-lint/pkg/lint/linter" 13 "github.com/golangci/golangci-lint/pkg/logutils" 14 ) 15 16 func AllPresets() []string { 17 return []string{linter.PresetBugs, linter.PresetUnused, linter.PresetFormatting, 18 linter.PresetStyle, linter.PresetComplexity, linter.PresetPerformance} 19 } 20 21 func allPresetsSet() map[string]bool { 22 ret := map[string]bool{} 23 for _, p := range AllPresets() { 24 ret[p] = true 25 } 26 return ret 27 } 28 29 var nameToLC map[string]linter.Config 30 var nameToLCOnce sync.Once 31 32 func getLinterConfig(name string) *linter.Config { 33 nameToLCOnce.Do(func() { 34 nameToLC = make(map[string]linter.Config) 35 for _, lc := range GetAllSupportedLinterConfigs() { 36 nameToLC[lc.Linter.Name()] = lc 37 } 38 }) 39 40 lc, ok := nameToLC[name] 41 if !ok { 42 return nil 43 } 44 45 return &lc 46 } 47 48 func enableLinterConfigs(lcs []linter.Config, isEnabled func(lc *linter.Config) bool) []linter.Config { 49 var ret []linter.Config 50 for _, lc := range lcs { 51 lc.EnabledByDefault = isEnabled(&lc) 52 ret = append(ret, lc) 53 } 54 55 return ret 56 } 57 58 func GetAllSupportedLinterConfigs() []linter.Config { 59 lcs := []linter.Config{ 60 linter.NewConfig(golinters.Govet{}). 61 WithFullImport(). // TODO: depend on it's configuration here 62 WithPresets(linter.PresetBugs). 63 WithSpeed(4). 64 WithURL("https://golang.org/cmd/vet/"), 65 linter.NewConfig(golinters.Errcheck{}). 66 WithFullImport(). 67 WithPresets(linter.PresetBugs). 68 WithSpeed(10). 69 WithURL("https://github.com/kisielk/errcheck"), 70 linter.NewConfig(golinters.Golint{}). 71 WithPresets(linter.PresetStyle). 72 WithSpeed(3). 73 WithURL("https://github.com/golang/lint"), 74 75 linter.NewConfig(golinters.Megacheck{StaticcheckEnabled: true}). 76 WithSSA(). 77 WithPresets(linter.PresetBugs). 78 WithSpeed(2). 79 WithURL("https://staticcheck.io/"), 80 linter.NewConfig(golinters.Megacheck{UnusedEnabled: true}). 81 WithSSA(). 82 WithPresets(linter.PresetUnused). 83 WithSpeed(5). 84 WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/unused"), 85 linter.NewConfig(golinters.Megacheck{GosimpleEnabled: true}). 86 WithSSA(). 87 WithPresets(linter.PresetStyle). 88 WithSpeed(5). 89 WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/gosimple"), 90 91 linter.NewConfig(golinters.Gas{}). 92 WithFullImport(). 93 WithPresets(linter.PresetBugs). 94 WithSpeed(8). 95 WithURL("https://github.com/GoASTScanner/gas"), 96 linter.NewConfig(golinters.Structcheck{}). 97 WithFullImport(). 98 WithPresets(linter.PresetUnused). 99 WithSpeed(10). 100 WithURL("https://github.com/opennota/check"), 101 linter.NewConfig(golinters.Varcheck{}). 102 WithFullImport(). 103 WithPresets(linter.PresetUnused). 104 WithSpeed(10). 105 WithURL("https://github.com/opennota/check"), 106 linter.NewConfig(golinters.Interfacer{}). 107 WithSSA(). 108 WithPresets(linter.PresetStyle). 109 WithSpeed(6). 110 WithURL("https://github.com/mvdan/interfacer"), 111 linter.NewConfig(golinters.Unconvert{}). 112 WithFullImport(). 113 WithPresets(linter.PresetStyle). 114 WithSpeed(10). 115 WithURL("https://github.com/mdempsky/unconvert"), 116 linter.NewConfig(golinters.Ineffassign{}). 117 WithPresets(linter.PresetUnused). 118 WithSpeed(9). 119 WithURL("https://github.com/gordonklaus/ineffassign"), 120 linter.NewConfig(golinters.Dupl{}). 121 WithPresets(linter.PresetStyle). 122 WithSpeed(7). 123 WithURL("https://github.com/mibk/dupl"), 124 linter.NewConfig(golinters.Goconst{}). 125 WithPresets(linter.PresetStyle). 126 WithSpeed(9). 127 WithURL("https://github.com/jgautheron/goconst"), 128 linter.NewConfig(golinters.Deadcode{}). 129 WithFullImport(). 130 WithPresets(linter.PresetUnused). 131 WithSpeed(10). 132 WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"), 133 linter.NewConfig(golinters.Gocyclo{}). 134 WithPresets(linter.PresetComplexity). 135 WithSpeed(8). 136 WithURL("https://github.com/alecthomas/gocyclo"), 137 linter.NewConfig(golinters.TypeCheck{}). 138 WithFullImport(). 139 WithPresets(linter.PresetBugs). 140 WithSpeed(10). 141 WithURL(""), 142 143 linter.NewConfig(golinters.Gofmt{}). 144 WithPresets(linter.PresetFormatting). 145 WithSpeed(7). 146 WithURL("https://golang.org/cmd/gofmt/"), 147 linter.NewConfig(golinters.Gofmt{UseGoimports: true}). 148 WithPresets(linter.PresetFormatting). 149 WithSpeed(5). 150 WithURL("https://godoc.org/golang.org/x/tools/cmd/goimports"), 151 linter.NewConfig(golinters.Maligned{}). 152 WithFullImport(). 153 WithPresets(linter.PresetPerformance). 154 WithSpeed(10). 155 WithURL("https://github.com/mdempsky/maligned"), 156 linter.NewConfig(golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}). 157 WithSSA(). 158 WithPresets(linter.PresetStyle, linter.PresetBugs, linter.PresetUnused). 159 WithSpeed(1). 160 WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/megacheck"), 161 linter.NewConfig(golinters.Depguard{}). 162 WithFullImport(). 163 WithPresets(linter.PresetStyle). 164 WithSpeed(6). 165 WithURL("https://github.com/OpenPeeDeeP/depguard"), 166 linter.NewConfig(golinters.Misspell{}). 167 WithPresets(linter.PresetStyle). 168 WithSpeed(7). 169 WithURL("https://github.com/client9/misspell"), 170 linter.NewConfig(golinters.Lll{}). 171 WithPresets(linter.PresetStyle). 172 WithSpeed(10). 173 WithURL("https://github.com/walle/lll"), 174 linter.NewConfig(golinters.Unparam{}). 175 WithPresets(linter.PresetUnused). 176 WithSpeed(3). 177 WithFullImport(). 178 WithSSA(). 179 WithURL("https://github.com/mvdan/unparam"), 180 linter.NewConfig(golinters.Nakedret{}). 181 WithPresets(linter.PresetComplexity). 182 WithSpeed(10). 183 WithURL("https://github.com/alexkohler/nakedret"), 184 linter.NewConfig(golinters.Prealloc{}). 185 WithPresets(linter.PresetPerformance). 186 WithSpeed(8). 187 WithURL("https://github.com/alexkohler/prealloc"), 188 } 189 190 isLocalRun := os.Getenv("GOLANGCI_COM_RUN") == "" 191 enabled := map[string]bool{ 192 golinters.Govet{}.Name(): true, 193 golinters.Errcheck{}.Name(): true, 194 golinters.Megacheck{StaticcheckEnabled: true}.Name(): true, 195 golinters.Megacheck{UnusedEnabled: true}.Name(): true, 196 golinters.Megacheck{GosimpleEnabled: true}.Name(): true, 197 golinters.Structcheck{}.Name(): true, 198 golinters.Varcheck{}.Name(): true, 199 golinters.Ineffassign{}.Name(): true, 200 golinters.Deadcode{}.Name(): true, 201 202 // don't typecheck for golangci.com: too many troubles 203 golinters.TypeCheck{}.Name(): isLocalRun, 204 } 205 return enableLinterConfigs(lcs, func(lc *linter.Config) bool { 206 return enabled[lc.Linter.Name()] 207 }) 208 } 209 210 func GetAllEnabledByDefaultLinters() []linter.Config { 211 var ret []linter.Config 212 for _, lc := range GetAllSupportedLinterConfigs() { 213 if lc.EnabledByDefault { 214 ret = append(ret, lc) 215 } 216 } 217 218 return ret 219 } 220 221 func linterConfigsToMap(lcs []linter.Config) map[string]*linter.Config { 222 ret := map[string]*linter.Config{} 223 for _, lc := range lcs { 224 lc := lc // local copy 225 ret[lc.Linter.Name()] = &lc 226 } 227 228 return ret 229 } 230 231 func validateLintersNames(cfg *config.Linters) error { 232 allNames := append([]string{}, cfg.Enable...) 233 allNames = append(allNames, cfg.Disable...) 234 for _, name := range allNames { 235 if getLinterConfig(name) == nil { 236 return fmt.Errorf("no such linter %q", name) 237 } 238 } 239 240 return nil 241 } 242 243 func validatePresets(cfg *config.Linters) error { 244 allPresets := allPresetsSet() 245 for _, p := range cfg.Presets { 246 if !allPresets[p] { 247 return fmt.Errorf("no such preset %q: only next presets exist: (%s)", p, strings.Join(AllPresets(), "|")) 248 } 249 } 250 251 if len(cfg.Presets) != 0 && cfg.EnableAll { 252 return fmt.Errorf("--presets is incompatible with --enable-all") 253 } 254 255 return nil 256 } 257 258 func validateAllDisableEnableOptions(cfg *config.Linters) error { 259 if cfg.EnableAll && cfg.DisableAll { 260 return fmt.Errorf("--enable-all and --disable-all options must not be combined") 261 } 262 263 if cfg.DisableAll { 264 if len(cfg.Enable) == 0 && len(cfg.Presets) == 0 { 265 return fmt.Errorf("all linters were disabled, but no one linter was enabled: must enable at least one") 266 } 267 268 if len(cfg.Disable) != 0 { 269 return fmt.Errorf("can't combine options --disable-all and --disable %s", cfg.Disable[0]) 270 } 271 } 272 273 if cfg.EnableAll && len(cfg.Enable) != 0 && !cfg.Fast { 274 return fmt.Errorf("can't combine options --enable-all and --enable %s", cfg.Enable[0]) 275 } 276 277 return nil 278 } 279 280 func validateDisabledAndEnabledAtOneMoment(cfg *config.Linters) error { 281 enabledLintersSet := map[string]bool{} 282 for _, name := range cfg.Enable { 283 enabledLintersSet[name] = true 284 } 285 286 for _, name := range cfg.Disable { 287 if enabledLintersSet[name] { 288 return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name) 289 } 290 } 291 292 return nil 293 } 294 295 func validateEnabledDisabledLintersConfig(cfg *config.Linters) error { 296 validators := []func(cfg *config.Linters) error{ 297 validateLintersNames, 298 validatePresets, 299 validateAllDisableEnableOptions, 300 validateDisabledAndEnabledAtOneMoment, 301 } 302 for _, v := range validators { 303 if err := v(cfg); err != nil { 304 return err 305 } 306 } 307 308 return nil 309 } 310 311 func GetAllLinterConfigsForPreset(p string) []linter.Config { 312 ret := []linter.Config{} 313 for _, lc := range GetAllSupportedLinterConfigs() { 314 for _, ip := range lc.InPresets { 315 if p == ip { 316 ret = append(ret, lc) 317 break 318 } 319 } 320 } 321 322 return ret 323 } 324 325 // nolint:gocyclo 326 func getEnabledLintersSet(lcfg *config.Linters, 327 enabledByDefaultLinters []linter.Config) map[string]*linter.Config { 328 329 resultLintersSet := map[string]*linter.Config{} 330 switch { 331 case len(lcfg.Presets) != 0: 332 break // imply --disable-all 333 case lcfg.EnableAll: 334 resultLintersSet = linterConfigsToMap(GetAllSupportedLinterConfigs()) 335 case lcfg.DisableAll: 336 break 337 default: 338 resultLintersSet = linterConfigsToMap(enabledByDefaultLinters) 339 } 340 341 // --presets can only add linters to default set 342 for _, p := range lcfg.Presets { 343 for _, lc := range GetAllLinterConfigsForPreset(p) { 344 lc := lc 345 resultLintersSet[lc.Linter.Name()] = &lc 346 } 347 } 348 349 // --fast removes slow linters from current set. 350 // It should be after --presets to be able to run only fast linters in preset. 351 // It should be before --enable and --disable to be able to enable or disable specific linter. 352 if lcfg.Fast { 353 for name := range resultLintersSet { 354 if getLinterConfig(name).DoesFullImport { 355 delete(resultLintersSet, name) 356 } 357 } 358 } 359 360 for _, name := range lcfg.Enable { 361 resultLintersSet[name] = getLinterConfig(name) 362 } 363 364 for _, name := range lcfg.Disable { 365 if name == "megacheck" { 366 for _, ln := range getAllMegacheckSubLinterNames() { 367 delete(resultLintersSet, ln) 368 } 369 } 370 delete(resultLintersSet, name) 371 } 372 373 optimizeLintersSet(resultLintersSet) 374 return resultLintersSet 375 } 376 377 func getAllMegacheckSubLinterNames() []string { 378 unusedName := golinters.Megacheck{UnusedEnabled: true}.Name() 379 gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name() 380 staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name() 381 return []string{unusedName, gosimpleName, staticcheckName} 382 } 383 384 func optimizeLintersSet(linters map[string]*linter.Config) { 385 unusedName := golinters.Megacheck{UnusedEnabled: true}.Name() 386 gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name() 387 staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name() 388 fullName := golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}.Name() 389 allNames := []string{unusedName, gosimpleName, staticcheckName, fullName} 390 391 megacheckCount := 0 392 for _, n := range allNames { 393 if linters[n] != nil { 394 megacheckCount++ 395 } 396 } 397 398 if megacheckCount <= 1 { 399 return 400 } 401 402 isFullEnabled := linters[fullName] != nil 403 m := golinters.Megacheck{ 404 UnusedEnabled: isFullEnabled || linters[unusedName] != nil, 405 GosimpleEnabled: isFullEnabled || linters[gosimpleName] != nil, 406 StaticcheckEnabled: isFullEnabled || linters[staticcheckName] != nil, 407 } 408 409 for _, n := range allNames { 410 delete(linters, n) 411 } 412 413 lc := *getLinterConfig("megacheck") 414 lc.Linter = m 415 linters[m.Name()] = &lc 416 } 417 418 func GetEnabledLinters(cfg *config.Config, log logutils.Log) ([]linter.Config, error) { 419 if err := validateEnabledDisabledLintersConfig(&cfg.Linters); err != nil { 420 return nil, err 421 } 422 423 resultLintersSet := getEnabledLintersSet(&cfg.Linters, GetAllEnabledByDefaultLinters()) 424 425 var resultLinters []linter.Config 426 for _, lc := range resultLintersSet { 427 resultLinters = append(resultLinters, *lc) 428 } 429 430 verbosePrintLintersStatus(cfg, resultLinters, log) 431 432 return resultLinters, nil 433 } 434 435 func verbosePrintLintersStatus(cfg *config.Config, lcs []linter.Config, log logutils.Log) { 436 var linterNames []string 437 for _, lc := range lcs { 438 linterNames = append(linterNames, lc.Linter.Name()) 439 } 440 sort.StringSlice(linterNames).Sort() 441 log.Infof("Active %d linters: %s", len(linterNames), linterNames) 442 443 if len(cfg.Linters.Presets) != 0 { 444 sort.StringSlice(cfg.Linters.Presets).Sort() 445 log.Infof("Active presets: %s", cfg.Linters.Presets) 446 } 447 }