github.com/shulhan/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  }