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  }