2018-05-06 19:08:34 +03:00
package pkg
2018-05-06 13:25:50 +03:00
import (
"context"
"fmt"
"sync"
"github.com/golangci/golangci-lint/pkg/config"
2018-05-06 19:08:34 +03:00
"github.com/golangci/golangci-lint/pkg/golinters"
2018-05-07 21:44:40 +03:00
"github.com/sirupsen/logrus"
2018-05-06 13:25:50 +03:00
)
type LinterConfig struct {
Desc string
2018-05-06 19:08:34 +03:00
Linter Linter
2018-05-06 21:08:53 +03:00
EnabledByDefault bool
2018-05-06 19:08:34 +03:00
DoesFullImport bool
2018-05-07 09:48:43 +03:00
NeedsSSARepr bool
2018-05-06 13:25:50 +03:00
}
2018-05-06 19:08:34 +03:00
var nameToLC map [ string ] LinterConfig
var nameToLCOnce sync . Once
func GetLinterConfig ( name string ) * LinterConfig {
nameToLCOnce . Do ( func ( ) {
nameToLC = make ( map [ string ] LinterConfig )
for _ , lc := range GetAllSupportedLinterConfigs ( ) {
nameToLC [ lc . Linter . Name ( ) ] = lc
}
} )
lc , ok := nameToLC [ name ]
if ! ok {
return nil
}
return & lc
}
2018-05-07 09:48:43 +03:00
func enabledByDefault ( linter Linter , desc string , doesFullImport , needsSSARepr bool ) LinterConfig {
2018-05-06 13:25:50 +03:00
return LinterConfig {
EnabledByDefault : true ,
Linter : linter ,
Desc : desc ,
2018-05-06 19:08:34 +03:00
DoesFullImport : doesFullImport ,
2018-05-07 09:48:43 +03:00
NeedsSSARepr : needsSSARepr ,
2018-05-06 13:25:50 +03:00
}
}
2018-05-07 09:48:43 +03:00
func disabledByDefault ( linter Linter , desc string , doesFullImport , needsSSARepr bool ) LinterConfig {
2018-05-06 13:25:50 +03:00
return LinterConfig {
EnabledByDefault : false ,
Linter : linter ,
Desc : desc ,
2018-05-06 19:08:34 +03:00
DoesFullImport : doesFullImport ,
2018-05-07 09:48:43 +03:00
NeedsSSARepr : needsSSARepr ,
2018-05-06 13:25:50 +03:00
}
}
func GetAllSupportedLinterConfigs ( ) [ ] LinterConfig {
return [ ] LinterConfig {
2018-05-07 09:48:43 +03:00
enabledByDefault ( golinters . Govet { } , "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string" , false , false ) ,
enabledByDefault ( golinters . Errcheck { } , "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases" , true , false ) ,
enabledByDefault ( golinters . Golint { } , "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes" , false , false ) ,
2018-05-07 12:43:52 +03:00
enabledByDefault ( golinters . Megacheck { } , "Megacheck: 3 sub-linters in one: staticcheck, gosimple and unused" , true , true ) ,
2018-05-07 14:02:27 +03:00
enabledByDefault ( golinters . Gas { } , "Inspects source code for security problems" , true , false ) ,
2018-05-07 09:48:43 +03:00
enabledByDefault ( golinters . Structcheck { } , "Finds unused struct fields" , true , false ) ,
enabledByDefault ( golinters . Varcheck { } , "Finds unused global variables and constants" , true , false ) ,
enabledByDefault ( golinters . Interfacer { } , "Linter that suggests narrower interface types" , true , true ) ,
2018-05-07 12:00:17 +03:00
enabledByDefault ( golinters . Unconvert { } , "Remove unnecessary type conversions" , true , false ) ,
2018-05-07 12:43:52 +03:00
enabledByDefault ( golinters . Ineffassign { } , "Detects when assignments to existing variables are not used" , false , false ) ,
enabledByDefault ( golinters . Dupl { } , "Tool for code clone detection" , false , false ) ,
enabledByDefault ( golinters . Goconst { } , "Finds repeated strings that could be replaced by a constant" , false , false ) ,
enabledByDefault ( golinters . Deadcode { } , "Finds unused code" , true , false ) ,
enabledByDefault ( golinters . Gocyclo { } , "Computes and checks the cyclomatic complexity of functions" , false , false ) ,
2018-05-07 09:48:43 +03:00
disabledByDefault ( golinters . Gofmt { } , "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification" , false , false ) ,
disabledByDefault ( golinters . Gofmt { UseGoimports : true } , "Goimports does everything that gofmt does. Additionally it checks unused imports" , false , false ) ,
disabledByDefault ( golinters . Maligned { } , "Tool to detect Go structs that would take less memory if their fields were sorted" , true , false ) ,
2018-05-06 13:25:50 +03:00
}
}
2018-05-06 19:08:34 +03:00
func getAllSupportedLinters ( ) [ ] Linter {
var ret [ ] Linter
2018-05-06 13:25:50 +03:00
for _ , lc := range GetAllSupportedLinterConfigs ( ) {
ret = append ( ret , lc . Linter )
}
return ret
}
2018-05-06 19:08:34 +03:00
func getAllEnabledByDefaultLinters ( ) [ ] Linter {
var ret [ ] Linter
2018-05-06 13:25:50 +03:00
for _ , lc := range GetAllSupportedLinterConfigs ( ) {
if lc . EnabledByDefault {
ret = append ( ret , lc . Linter )
}
}
return ret
}
2018-05-06 19:08:34 +03:00
var supportedLintersByName map [ string ] Linter
2018-05-06 13:25:50 +03:00
var linterByNameMapOnce sync . Once
2018-05-06 19:08:34 +03:00
func getLinterByName ( name string ) Linter {
2018-05-06 13:25:50 +03:00
linterByNameMapOnce . Do ( func ( ) {
2018-05-06 19:08:34 +03:00
supportedLintersByName = make ( map [ string ] Linter )
2018-05-06 13:25:50 +03:00
for _ , lc := range GetAllSupportedLinterConfigs ( ) {
supportedLintersByName [ lc . Linter . Name ( ) ] = lc . Linter
}
} )
return supportedLintersByName [ name ]
}
2018-05-06 19:08:34 +03:00
func lintersToMap ( linters [ ] Linter ) map [ string ] Linter {
ret := map [ string ] Linter { }
2018-05-06 13:25:50 +03:00
for _ , linter := range linters {
ret [ linter . Name ( ) ] = linter
}
return ret
}
func validateEnabledDisabledLintersConfig ( cfg * config . Run ) error {
allNames := append ( [ ] string { } , cfg . EnabledLinters ... )
allNames = append ( allNames , cfg . DisabledLinters ... )
for _ , name := range allNames {
if getLinterByName ( name ) == nil {
return fmt . Errorf ( "no such linter %q" , name )
}
}
if cfg . EnableAllLinters && cfg . DisableAllLinters {
return fmt . Errorf ( "--enable-all and --disable-all options must not be combined" )
}
if cfg . DisableAllLinters {
if len ( cfg . EnabledLinters ) == 0 {
return fmt . Errorf ( "all linters were disabled, but no one linter was enabled: must enable at least one" )
}
if len ( cfg . DisabledLinters ) != 0 {
return fmt . Errorf ( "can't combine options --disable-all and --disable %s" , cfg . DisabledLinters [ 0 ] )
}
}
if cfg . EnableAllLinters && len ( cfg . EnabledLinters ) != 0 {
return fmt . Errorf ( "can't combine options --enable-all and --enable %s" , cfg . EnabledLinters [ 0 ] )
}
enabledLintersSet := map [ string ] bool { }
for _ , name := range cfg . EnabledLinters {
enabledLintersSet [ name ] = true
}
for _ , name := range cfg . DisabledLinters {
if enabledLintersSet [ name ] {
return fmt . Errorf ( "linter %q can't be disabled and enabled at one moment" , name )
}
}
return nil
}
2018-05-06 19:08:34 +03:00
func GetEnabledLinters ( ctx context . Context , cfg * config . Run ) ( [ ] Linter , error ) {
2018-05-06 13:25:50 +03:00
if err := validateEnabledDisabledLintersConfig ( cfg ) ; err != nil {
return nil , err
}
2018-05-06 19:08:34 +03:00
resultLintersSet := map [ string ] Linter { }
2018-05-06 13:25:50 +03:00
switch {
case cfg . EnableAllLinters :
resultLintersSet = lintersToMap ( getAllSupportedLinters ( ) )
case cfg . DisableAllLinters :
break
default :
resultLintersSet = lintersToMap ( getAllEnabledByDefaultLinters ( ) )
}
for _ , name := range cfg . EnabledLinters {
resultLintersSet [ name ] = getLinterByName ( name )
}
for _ , name := range cfg . DisabledLinters {
delete ( resultLintersSet , name )
}
2018-05-06 19:08:34 +03:00
var resultLinters [ ] Linter
2018-05-06 13:25:50 +03:00
var resultLinterNames [ ] string
for name , linter := range resultLintersSet {
resultLinters = append ( resultLinters , linter )
resultLinterNames = append ( resultLinterNames , name )
}
2018-05-07 21:44:40 +03:00
logrus . Infof ( "Enabled linters: %s" , resultLinterNames )
2018-05-06 13:25:50 +03:00
return resultLinters , nil
}