add exhaustive linter (#1166)

* wip

more

add new files

run command fixes

more

* go.mod

* order

* same package

* review comment

* enable linter in .golangci.yml

* add testcase for default-signifies-exhaustive: true

* adjust runGoErrchk instead

* disable the linter

* cleanup

* more cleanup

* cleanup
This commit is contained in:
Nishanth Shanmugham 2020-05-29 19:01:46 +05:30 committed by GitHub
parent 71b2f04e88
commit f3376cab71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 102 additions and 6 deletions

View File

@ -98,6 +98,11 @@ linters-settings:
# path to a file containing a list of functions to exclude from checking # path to a file containing a list of functions to exclude from checking
# see https://github.com/kisielk/errcheck#excluding-functions for details # see https://github.com/kisielk/errcheck#excluding-functions for details
exclude: /path/to/file.txt exclude: /path/to/file.txt
exhaustive:
# indicates that switch statements are to be considered exhaustive if a
# 'default' case is present, even if all enum members aren't listed in the
# switch
default-signifies-exhaustive: false
funlen: funlen:
lines: 60 lines: 60
statements: 40 statements: 40
@ -174,7 +179,7 @@ linters-settings:
modules: # List of blocked modules modules: # List of blocked modules
# - github.com/uudashr/go-module: # Blocked module # - github.com/uudashr/go-module: # Blocked module
# recommendations: # Recommended modules that should be used instead (Optional) # recommendations: # Recommended modules that should be used instead (Optional)
# - golang.org/x/mod # - golang.org/x/mod
# reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional) # reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional)
versions: # List of blocked module version constraints versions: # List of blocked module version constraints
# - github.com/mitchellh/go-homedir: # Blocked module with version constraint # - github.com/mitchellh/go-homedir: # Blocked module with version constraint

View File

@ -9,6 +9,8 @@ linters-settings:
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
dupl: dupl:
threshold: 100 threshold: 100
exhaustive:
default-signifies-exhaustive: false
funlen: funlen:
lines: 100 lines: 100
statements: 50 statements: 50
@ -104,6 +106,7 @@ linters:
# don't enable: # don't enable:
# - asciicheck # - asciicheck
# - exhaustive (TODO: enable after next release; current release at time of writing is v1.27)
# - gochecknoglobals # - gochecknoglobals
# - gocognit # - gocognit
# - godot # - godot

3
go.mod
View File

@ -34,6 +34,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/go-ps v1.0.0
github.com/nakabonne/nestif v0.3.0 github.com/nakabonne/nestif v0.3.0
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/ryancurrah/gomodguard v1.1.0 github.com/ryancurrah/gomodguard v1.1.0
github.com/securego/gosec/v2 v2.3.0 github.com/securego/gosec/v2 v2.3.0
@ -52,7 +53,7 @@ require (
github.com/ultraware/whitespace v0.0.4 github.com/ultraware/whitespace v0.0.4
github.com/uudashr/gocognit v1.0.1 github.com/uudashr/gocognit v1.0.1
github.com/valyala/quicktemplate v1.5.0 github.com/valyala/quicktemplate v1.5.0
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
honnef.co/go/tools v0.0.1-2020.1.4 honnef.co/go/tools v0.0.1-2020.1.4
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed

4
go.sum
View File

@ -267,6 +267,8 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132 h1:NjznefjSrral0MiR4KlB41io/d3OklvhcgQUdfZTqJE=
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
@ -511,6 +513,8 @@ golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJ
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE= golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE= golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a h1:gILuVKC+ZPD6g/tj6zBOdnOH1ZHI0zZ86+KLMogc6/s=
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=

View File

@ -242,6 +242,7 @@ type LintersSettings struct {
Testpackage TestpackageSettings Testpackage TestpackageSettings
Nestif NestifSettings Nestif NestifSettings
NoLintLint NoLintLintSettings NoLintLint NoLintLintSettings
Exhaustive ExhaustiveSettings
Custom map[string]CustomLinterSettings Custom map[string]CustomLinterSettings
} }
@ -339,6 +340,10 @@ type NestifSettings struct {
MinComplexity int `mapstructure:"min-complexity"` MinComplexity int `mapstructure:"min-complexity"`
} }
type ExhaustiveSettings struct {
DefaultSignifiesExhaustive bool `mapstructure:"default-signifies-exhaustive"`
}
var defaultLintersSettings = LintersSettings{ var defaultLintersSettings = LintersSettings{
Lll: LllSettings{ Lll: LllSettings{
LineLength: 120, LineLength: 120,
@ -389,6 +394,9 @@ var defaultLintersSettings = LintersSettings{
Nestif: NestifSettings{ Nestif: NestifSettings{
MinComplexity: 5, MinComplexity: 5,
}, },
Exhaustive: ExhaustiveSettings{
DefaultSignifiesExhaustive: false,
},
} }
type CustomLinterSettings struct { type CustomLinterSettings struct {

View File

@ -0,0 +1,25 @@
package golinters
import (
"github.com/nishanths/exhaustive"
"golang.org/x/tools/go/analysis"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
)
func NewExhaustive(settings *config.ExhaustiveSettings) *goanalysis.Linter {
a := exhaustive.Analyzer
var cfg map[string]map[string]interface{}
if settings != nil {
cfg = map[string]map[string]interface{}{
a.Name: {
exhaustive.DefaultSignifiesExhaustiveFlag: settings.DefaultSignifiesExhaustive,
},
}
}
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, cfg).
WithLoadMode(goanalysis.LoadModeTypesInfo)
}

View File

@ -935,7 +935,8 @@ func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struc
return rv.Len() return rv.Len()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64, reflect.UnsafePointer: reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64,
reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer:
return int(rv.Type().Size()) return int(rv.Type().Size())
case reflect.Invalid: case reflect.Invalid:
return 0 return 0

View File

@ -87,9 +87,11 @@ func enableLinterConfigs(lcs []*linter.Config, isEnabled func(lc *linter.Config)
func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
var govetCfg *config.GovetSettings var govetCfg *config.GovetSettings
var testpackageCfg *config.TestpackageSettings var testpackageCfg *config.TestpackageSettings
var exhaustiveCfg *config.ExhaustiveSettings
if m.cfg != nil { if m.cfg != nil {
govetCfg = &m.cfg.LintersSettings.Govet govetCfg = &m.cfg.LintersSettings.Govet
testpackageCfg = &m.cfg.LintersSettings.Testpackage testpackageCfg = &m.cfg.LintersSettings.Testpackage
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
} }
const megacheckName = "megacheck" const megacheckName = "megacheck"
lcs := []*linter.Config{ lcs := []*linter.Config{
@ -275,6 +277,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
linter.NewConfig(golinters.NewExportLoopRef()). linter.NewConfig(golinters.NewExportLoopRef()).
WithPresets(linter.PresetBugs). WithPresets(linter.PresetBugs).
WithURL("https://github.com/kyoh86/exportloopref"), WithURL("https://github.com/kyoh86/exportloopref"),
linter.NewConfig(golinters.NewExhaustive(exhaustiveCfg)).
WithPresets(linter.PresetBugs).
WithLoadForGoAnalysis().
WithURL("https://github.com/nishanths/exhaustive"),
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
linter.NewConfig(golinters.NewNoLintLint()). linter.NewConfig(golinters.NewNoLintLint()).
WithPresets(linter.PresetStyle). WithPresets(linter.PresetStyle).

View File

@ -17,9 +17,13 @@ import (
func runGoErrchk(c *exec.Cmd, files []string, t *testing.T) { func runGoErrchk(c *exec.Cmd, files []string, t *testing.T) {
output, err := c.CombinedOutput() output, err := c.CombinedOutput()
assert.Error(t, err) // The returned error will be nil if the test file does not have any issues
_, ok := err.(*exec.ExitError) // and thus the linter exits with exit code 0. So perform the additional
assert.True(t, ok, err) // assertions only if the error is non-nil.
if err != nil {
_, ok := err.(*exec.ExitError)
assert.True(t, ok, err)
}
// TODO: uncomment after deprecating go1.11 // TODO: uncomment after deprecating go1.11
// assert.Equal(t, exitcodes.IssuesFound, exitErr.ExitCode()) // assert.Equal(t, exitcodes.IssuesFound, exitErr.ExitCode())

3
test/testdata/configs/exhaustive.yml vendored Normal file
View File

@ -0,0 +1,3 @@
linters-settings:
exhaustive:
default-signifies-exhaustive: true

17
test/testdata/exhaustive.go vendored Normal file
View File

@ -0,0 +1,17 @@
//args: -Eexhaustive
package testdata
type Direction int
const (
North Direction = iota
East
South
West
)
func processDirection(d Direction) {
switch d { // ERROR "missing cases in switch of type Direction: East, West"
case North, South:
}
}

19
test/testdata/exhaustive_default.go vendored Normal file
View File

@ -0,0 +1,19 @@
//args: -Eexhaustive
//config_path: testdata/configs/exhaustive.yml
package testdata
type Direction int
const (
North Direction = iota
East
South
West
)
func processDirectionDefault(d Direction) {
switch d {
case North, South:
default:
}
}