
1. Log all warnings, don't hide none of them 2. Write fatal messages (stop analysis) with error log level 3. Remove ugly timestamp counter from logrus output 4. Print nested module prefix in log 5. Make logger abstraction: no global logging anymore 6. Refactor config reading to config.FileReader struct to avoid passing logger into every function 7. Replace exit codes hardcoding with constants in exitcodes package 8. Fail test if any warning was logged 9. Fix calculation of relative path if we analyze parent dir ../ 10. Move Runner initialization from Executor to NewRunner func 11. Log every AST parsing error 12. Properly print used config file path in verbose mode 13. Print package files if only 1 package is analyzedin verbose mode, print not compiling packages in verbose mode 14. Forbid usage of github.com/sirupsen/logrus by DepGuard linter 15. Add default ignore pattern to folint: "comment on exported const"
431 lines
12 KiB
Go
431 lines
12 KiB
Go
package lintersdb
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/config"
|
|
"github.com/golangci/golangci-lint/pkg/golinters"
|
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
|
"github.com/golangci/golangci-lint/pkg/logutils"
|
|
)
|
|
|
|
func AllPresets() []string {
|
|
return []string{linter.PresetBugs, linter.PresetUnused, linter.PresetFormatting, linter.PresetStyle, linter.PresetComplexity, linter.PresetPerformance}
|
|
}
|
|
|
|
func allPresetsSet() map[string]bool {
|
|
ret := map[string]bool{}
|
|
for _, p := range AllPresets() {
|
|
ret[p] = true
|
|
}
|
|
return ret
|
|
}
|
|
|
|
var nameToLC map[string]linter.Config
|
|
var nameToLCOnce sync.Once
|
|
|
|
func getLinterConfig(name string) *linter.Config {
|
|
nameToLCOnce.Do(func() {
|
|
nameToLC = make(map[string]linter.Config)
|
|
for _, lc := range GetAllSupportedLinterConfigs() {
|
|
nameToLC[lc.Linter.Name()] = lc
|
|
}
|
|
})
|
|
|
|
lc, ok := nameToLC[name]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return &lc
|
|
}
|
|
|
|
func enableLinterConfigs(lcs []linter.Config, isEnabled func(lc *linter.Config) bool) []linter.Config {
|
|
var ret []linter.Config
|
|
for _, lc := range lcs {
|
|
lc.EnabledByDefault = isEnabled(&lc)
|
|
ret = append(ret, lc)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func GetAllSupportedLinterConfigs() []linter.Config {
|
|
lcs := []linter.Config{
|
|
linter.NewConfig(golinters.Govet{}).
|
|
WithPresets(linter.PresetBugs).
|
|
WithSpeed(4).
|
|
WithURL("https://golang.org/cmd/vet/"),
|
|
linter.NewConfig(golinters.Errcheck{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetBugs).
|
|
WithSpeed(10).
|
|
WithURL("https://github.com/kisielk/errcheck"),
|
|
linter.NewConfig(golinters.Golint{}).
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(3).
|
|
WithURL("https://github.com/golang/lint"),
|
|
|
|
linter.NewConfig(golinters.Megacheck{StaticcheckEnabled: true}).
|
|
WithSSA().
|
|
WithPresets(linter.PresetBugs).
|
|
WithSpeed(2).
|
|
WithURL("https://staticcheck.io/"),
|
|
linter.NewConfig(golinters.Megacheck{UnusedEnabled: true}).
|
|
WithSSA().
|
|
WithPresets(linter.PresetUnused).
|
|
WithSpeed(5).
|
|
WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/unused"),
|
|
linter.NewConfig(golinters.Megacheck{GosimpleEnabled: true}).
|
|
WithSSA().
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(5).
|
|
WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/gosimple"),
|
|
|
|
linter.NewConfig(golinters.Gas{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetBugs).
|
|
WithSpeed(8).
|
|
WithURL("https://github.com/GoASTScanner/gas"),
|
|
linter.NewConfig(golinters.Structcheck{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetUnused).
|
|
WithSpeed(10).
|
|
WithURL("https://github.com/opennota/check"),
|
|
linter.NewConfig(golinters.Varcheck{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetUnused).
|
|
WithSpeed(10).
|
|
WithURL("https://github.com/opennota/check"),
|
|
linter.NewConfig(golinters.Interfacer{}).
|
|
WithSSA().
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(6).
|
|
WithURL("https://github.com/mvdan/interfacer"),
|
|
linter.NewConfig(golinters.Unconvert{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(10).
|
|
WithURL("https://github.com/mdempsky/unconvert"),
|
|
linter.NewConfig(golinters.Ineffassign{}).
|
|
WithPresets(linter.PresetUnused).
|
|
WithSpeed(9).
|
|
WithURL("https://github.com/gordonklaus/ineffassign"),
|
|
linter.NewConfig(golinters.Dupl{}).
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(7).
|
|
WithURL("https://github.com/mibk/dupl"),
|
|
linter.NewConfig(golinters.Goconst{}).
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(9).
|
|
WithURL("https://github.com/jgautheron/goconst"),
|
|
linter.NewConfig(golinters.Deadcode{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetUnused).
|
|
WithSpeed(10).
|
|
WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"),
|
|
linter.NewConfig(golinters.Gocyclo{}).
|
|
WithPresets(linter.PresetComplexity).
|
|
WithSpeed(8).
|
|
WithURL("https://github.com/alecthomas/gocyclo"),
|
|
linter.NewConfig(golinters.TypeCheck{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetBugs).
|
|
WithSpeed(10).
|
|
WithURL(""),
|
|
|
|
linter.NewConfig(golinters.Gofmt{}).
|
|
WithPresets(linter.PresetFormatting).
|
|
WithSpeed(7).
|
|
WithURL("https://golang.org/cmd/gofmt/"),
|
|
linter.NewConfig(golinters.Gofmt{UseGoimports: true}).
|
|
WithPresets(linter.PresetFormatting).
|
|
WithSpeed(5).
|
|
WithURL("https://godoc.org/golang.org/x/tools/cmd/goimports"),
|
|
linter.NewConfig(golinters.Maligned{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetPerformance).
|
|
WithSpeed(10).
|
|
WithURL("https://github.com/mdempsky/maligned"),
|
|
linter.NewConfig(golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}).
|
|
WithSSA().
|
|
WithPresets(linter.PresetStyle, linter.PresetBugs, linter.PresetUnused).
|
|
WithSpeed(1).
|
|
WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/megacheck"),
|
|
linter.NewConfig(golinters.Depguard{}).
|
|
WithFullImport().
|
|
WithPresets(linter.PresetStyle).
|
|
WithSpeed(6).
|
|
WithURL("https://github.com/OpenPeeDeeP/depguard"),
|
|
}
|
|
|
|
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
|
|
disabled := map[string]bool{
|
|
golinters.Gocyclo{}.Name(): true, // annoying
|
|
golinters.Dupl{}.Name(): true, // annoying
|
|
golinters.Maligned{}.Name(): true, // rarely usable
|
|
golinters.TypeCheck{}.Name(): true, // annoying because of different building envs
|
|
}
|
|
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
|
|
return !disabled[lc.Linter.Name()]
|
|
})
|
|
}
|
|
|
|
enabled := map[string]bool{
|
|
golinters.Govet{}.Name(): true,
|
|
golinters.Errcheck{}.Name(): true,
|
|
golinters.Megacheck{StaticcheckEnabled: true}.Name(): true,
|
|
golinters.Megacheck{UnusedEnabled: true}.Name(): true,
|
|
golinters.Megacheck{GosimpleEnabled: true}.Name(): true,
|
|
golinters.Gas{}.Name(): true,
|
|
golinters.Structcheck{}.Name(): true,
|
|
golinters.Varcheck{}.Name(): true,
|
|
golinters.Ineffassign{}.Name(): true,
|
|
golinters.Deadcode{}.Name(): true,
|
|
golinters.TypeCheck{}.Name(): true,
|
|
}
|
|
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
|
|
return enabled[lc.Linter.Name()]
|
|
})
|
|
}
|
|
|
|
func GetAllEnabledByDefaultLinters() []linter.Config {
|
|
var ret []linter.Config
|
|
for _, lc := range GetAllSupportedLinterConfigs() {
|
|
if lc.EnabledByDefault {
|
|
ret = append(ret, lc)
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func linterConfigsToMap(lcs []linter.Config) map[string]*linter.Config {
|
|
ret := map[string]*linter.Config{}
|
|
for _, lc := range lcs {
|
|
lc := lc // local copy
|
|
ret[lc.Linter.Name()] = &lc
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func validateLintersNames(cfg *config.Linters) error {
|
|
allNames := append([]string{}, cfg.Enable...)
|
|
allNames = append(allNames, cfg.Disable...)
|
|
for _, name := range allNames {
|
|
if getLinterConfig(name) == nil {
|
|
return fmt.Errorf("no such linter %q", name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validatePresets(cfg *config.Linters) error {
|
|
allPresets := allPresetsSet()
|
|
for _, p := range cfg.Presets {
|
|
if !allPresets[p] {
|
|
return fmt.Errorf("no such preset %q: only next presets exist: (%s)", p, strings.Join(AllPresets(), "|"))
|
|
}
|
|
}
|
|
|
|
if len(cfg.Presets) != 0 && cfg.EnableAll {
|
|
return fmt.Errorf("--presets is incompatible with --enable-all")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateAllDisableEnableOptions(cfg *config.Linters) error {
|
|
if cfg.EnableAll && cfg.DisableAll {
|
|
return fmt.Errorf("--enable-all and --disable-all options must not be combined")
|
|
}
|
|
|
|
if cfg.DisableAll {
|
|
if len(cfg.Enable) == 0 && len(cfg.Presets) == 0 {
|
|
return fmt.Errorf("all linters were disabled, but no one linter was enabled: must enable at least one")
|
|
}
|
|
|
|
if len(cfg.Disable) != 0 {
|
|
return fmt.Errorf("can't combine options --disable-all and --disable %s", cfg.Disable[0])
|
|
}
|
|
}
|
|
|
|
if cfg.EnableAll && len(cfg.Enable) != 0 && !cfg.Fast {
|
|
return fmt.Errorf("can't combine options --enable-all and --enable %s", cfg.Enable[0])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateDisabledAndEnabledAtOneMoment(cfg *config.Linters) error {
|
|
enabledLintersSet := map[string]bool{}
|
|
for _, name := range cfg.Enable {
|
|
enabledLintersSet[name] = true
|
|
}
|
|
|
|
for _, name := range cfg.Disable {
|
|
if enabledLintersSet[name] {
|
|
return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateEnabledDisabledLintersConfig(cfg *config.Linters) error {
|
|
validators := []func(cfg *config.Linters) error{
|
|
validateLintersNames,
|
|
validatePresets,
|
|
validateAllDisableEnableOptions,
|
|
validateDisabledAndEnabledAtOneMoment,
|
|
}
|
|
for _, v := range validators {
|
|
if err := v(cfg); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetAllLinterConfigsForPreset(p string) []linter.Config {
|
|
ret := []linter.Config{}
|
|
for _, lc := range GetAllSupportedLinterConfigs() {
|
|
for _, ip := range lc.InPresets {
|
|
if p == ip {
|
|
ret = append(ret, lc)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []linter.Config) map[string]*linter.Config { // nolint:gocyclo
|
|
resultLintersSet := map[string]*linter.Config{}
|
|
switch {
|
|
case len(lcfg.Presets) != 0:
|
|
break // imply --disable-all
|
|
case lcfg.EnableAll:
|
|
resultLintersSet = linterConfigsToMap(GetAllSupportedLinterConfigs())
|
|
case lcfg.DisableAll:
|
|
break
|
|
default:
|
|
resultLintersSet = linterConfigsToMap(enabledByDefaultLinters)
|
|
}
|
|
|
|
// --presets can only add linters to default set
|
|
for _, p := range lcfg.Presets {
|
|
for _, lc := range GetAllLinterConfigsForPreset(p) {
|
|
lc := lc
|
|
resultLintersSet[lc.Linter.Name()] = &lc
|
|
}
|
|
}
|
|
|
|
// --fast removes slow linters from current set.
|
|
// It should be after --presets to be able to run only fast linters in preset.
|
|
// It should be before --enable and --disable to be able to enable or disable specific linter.
|
|
if lcfg.Fast {
|
|
for name := range resultLintersSet {
|
|
if getLinterConfig(name).DoesFullImport {
|
|
delete(resultLintersSet, name)
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, name := range lcfg.Enable {
|
|
resultLintersSet[name] = getLinterConfig(name)
|
|
}
|
|
|
|
for _, name := range lcfg.Disable {
|
|
if name == "megacheck" {
|
|
for _, ln := range getAllMegacheckSubLinterNames() {
|
|
delete(resultLintersSet, ln)
|
|
}
|
|
}
|
|
delete(resultLintersSet, name)
|
|
}
|
|
|
|
optimizeLintersSet(resultLintersSet)
|
|
return resultLintersSet
|
|
}
|
|
|
|
func getAllMegacheckSubLinterNames() []string {
|
|
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
|
|
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
|
|
staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
|
|
return []string{unusedName, gosimpleName, staticcheckName}
|
|
}
|
|
|
|
func optimizeLintersSet(linters map[string]*linter.Config) {
|
|
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
|
|
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
|
|
staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
|
|
fullName := golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}.Name()
|
|
allNames := []string{unusedName, gosimpleName, staticcheckName, fullName}
|
|
|
|
megacheckCount := 0
|
|
for _, n := range allNames {
|
|
if linters[n] != nil {
|
|
megacheckCount++
|
|
}
|
|
}
|
|
|
|
if megacheckCount <= 1 {
|
|
return
|
|
}
|
|
|
|
isFullEnabled := linters[fullName] != nil
|
|
m := golinters.Megacheck{
|
|
UnusedEnabled: isFullEnabled || linters[unusedName] != nil,
|
|
GosimpleEnabled: isFullEnabled || linters[gosimpleName] != nil,
|
|
StaticcheckEnabled: isFullEnabled || linters[staticcheckName] != nil,
|
|
}
|
|
|
|
for _, n := range allNames {
|
|
delete(linters, n)
|
|
}
|
|
|
|
lc := *getLinterConfig("megacheck")
|
|
lc.Linter = m
|
|
linters[m.Name()] = &lc
|
|
}
|
|
|
|
func GetEnabledLinters(cfg *config.Config, log logutils.Log) ([]linter.Config, error) {
|
|
if err := validateEnabledDisabledLintersConfig(&cfg.Linters); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resultLintersSet := getEnabledLintersSet(&cfg.Linters, GetAllEnabledByDefaultLinters())
|
|
|
|
var resultLinters []linter.Config
|
|
for _, lc := range resultLintersSet {
|
|
resultLinters = append(resultLinters, *lc)
|
|
}
|
|
|
|
verbosePrintLintersStatus(cfg, resultLinters, log)
|
|
|
|
return resultLinters, nil
|
|
}
|
|
|
|
func verbosePrintLintersStatus(cfg *config.Config, lcs []linter.Config, log logutils.Log) {
|
|
var linterNames []string
|
|
for _, lc := range lcs {
|
|
linterNames = append(linterNames, lc.Linter.Name())
|
|
}
|
|
sort.StringSlice(linterNames).Sort()
|
|
log.Infof("Active %d linters: %s", len(linterNames), linterNames)
|
|
|
|
if len(cfg.Linters.Presets) != 0 {
|
|
sort.StringSlice(cfg.Linters.Presets).Sort()
|
|
log.Infof("Active presets: %s", cfg.Linters.Presets)
|
|
}
|
|
}
|