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