441 lines
12 KiB
Go
441 lines
12 KiB
Go
package lintersdb
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"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/sirupsen/logrus"
|
|
)
|
|
|
|
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 {
|
|
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) {
|
|
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) ([]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)
|
|
|
|
return resultLinters, nil
|
|
}
|
|
|
|
func uniqStrings(ss []string) []string {
|
|
us := map[string]bool{}
|
|
for _, s := range ss {
|
|
us[s] = true
|
|
}
|
|
|
|
var ret []string
|
|
for k := range us {
|
|
ret = append(ret, k)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func verbosePrintLintersStatus(cfg *config.Config, lcs []linter.Config) {
|
|
var linterNames []string
|
|
for _, lc := range lcs {
|
|
linterNames = append(linterNames, lc.Linter.Name())
|
|
}
|
|
logrus.Infof("Active linters: %s", linterNames)
|
|
|
|
if len(cfg.Linters.Presets) != 0 {
|
|
logrus.Infof("Active presets: %s", uniqStrings(cfg.Linters.Presets))
|
|
}
|
|
}
|