Skip to content

Commit f3376ca

Browse files
authored
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
1 parent 71b2f04 commit f3376ca

File tree

12 files changed

+102
-6
lines changed

12 files changed

+102
-6
lines changed

.golangci.example.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ linters-settings:
9898
# path to a file containing a list of functions to exclude from checking
9999
# see https://github.com/kisielk/errcheck#excluding-functions for details
100100
exclude: /path/to/file.txt
101+
exhaustive:
102+
# indicates that switch statements are to be considered exhaustive if a
103+
# 'default' case is present, even if all enum members aren't listed in the
104+
# switch
105+
default-signifies-exhaustive: false
101106
funlen:
102107
lines: 60
103108
statements: 40
@@ -174,7 +179,7 @@ linters-settings:
174179
modules: # List of blocked modules
175180
# - github.com/uudashr/go-module: # Blocked module
176181
# recommendations: # Recommended modules that should be used instead (Optional)
177-
# - golang.org/x/mod
182+
# - golang.org/x/mod
178183
# reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional)
179184
versions: # List of blocked module version constraints
180185
# - github.com/mitchellh/go-homedir: # Blocked module with version constraint

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ linters-settings:
99
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
1010
dupl:
1111
threshold: 100
12+
exhaustive:
13+
default-signifies-exhaustive: false
1214
funlen:
1315
lines: 100
1416
statements: 50
@@ -104,6 +106,7 @@ linters:
104106

105107
# don't enable:
106108
# - asciicheck
109+
# - exhaustive (TODO: enable after next release; current release at time of writing is v1.27)
107110
# - gochecknoglobals
108111
# - gocognit
109112
# - godot

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ require (
3434
github.com/mitchellh/go-homedir v1.1.0
3535
github.com/mitchellh/go-ps v1.0.0
3636
github.com/nakabonne/nestif v0.3.0
37+
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132
3738
github.com/pkg/errors v0.9.1
3839
github.com/ryancurrah/gomodguard v1.1.0
3940
github.com/securego/gosec/v2 v2.3.0
@@ -52,7 +53,7 @@ require (
5253
github.com/ultraware/whitespace v0.0.4
5354
github.com/uudashr/gocognit v1.0.1
5455
github.com/valyala/quicktemplate v1.5.0
55-
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770
56+
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a
5657
gopkg.in/yaml.v2 v2.3.0
5758
honnef.co/go/tools v0.0.1-2020.1.4
5859
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1
267267
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
268268
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
269269
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
270+
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132 h1:NjznefjSrral0MiR4KlB41io/d3OklvhcgQUdfZTqJE=
271+
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c=
270272
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
271273
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
272274
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
511513
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
512514
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
513515
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
516+
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a h1:gILuVKC+ZPD6g/tj6zBOdnOH1ZHI0zZ86+KLMogc6/s=
517+
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
514518
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
515519
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
516520
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=

pkg/config/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ type LintersSettings struct {
242242
Testpackage TestpackageSettings
243243
Nestif NestifSettings
244244
NoLintLint NoLintLintSettings
245+
Exhaustive ExhaustiveSettings
245246

246247
Custom map[string]CustomLinterSettings
247248
}
@@ -339,6 +340,10 @@ type NestifSettings struct {
339340
MinComplexity int `mapstructure:"min-complexity"`
340341
}
341342

343+
type ExhaustiveSettings struct {
344+
DefaultSignifiesExhaustive bool `mapstructure:"default-signifies-exhaustive"`
345+
}
346+
342347
var defaultLintersSettings = LintersSettings{
343348
Lll: LllSettings{
344349
LineLength: 120,
@@ -389,6 +394,9 @@ var defaultLintersSettings = LintersSettings{
389394
Nestif: NestifSettings{
390395
MinComplexity: 5,
391396
},
397+
Exhaustive: ExhaustiveSettings{
398+
DefaultSignifiesExhaustive: false,
399+
},
392400
}
393401

394402
type CustomLinterSettings struct {

pkg/golinters/exhaustive.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package golinters
2+
3+
import (
4+
"github.com/nishanths/exhaustive"
5+
"golang.org/x/tools/go/analysis"
6+
7+
"github.com/golangci/golangci-lint/pkg/config"
8+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
9+
)
10+
11+
func NewExhaustive(settings *config.ExhaustiveSettings) *goanalysis.Linter {
12+
a := exhaustive.Analyzer
13+
14+
var cfg map[string]map[string]interface{}
15+
if settings != nil {
16+
cfg = map[string]map[string]interface{}{
17+
a.Name: {
18+
exhaustive.DefaultSignifiesExhaustiveFlag: settings.DefaultSignifiesExhaustive,
19+
},
20+
}
21+
}
22+
23+
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, cfg).
24+
WithLoadMode(goanalysis.LoadModeTypesInfo)
25+
}

pkg/golinters/goanalysis/runner.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,8 @@ func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struc
935935
return rv.Len()
936936
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
937937
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
938-
reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64, reflect.UnsafePointer:
938+
reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64,
939+
reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer:
939940
return int(rv.Type().Size())
940941
case reflect.Invalid:
941942
return 0

pkg/lint/lintersdb/manager.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ func enableLinterConfigs(lcs []*linter.Config, isEnabled func(lc *linter.Config)
8787
func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
8888
var govetCfg *config.GovetSettings
8989
var testpackageCfg *config.TestpackageSettings
90+
var exhaustiveCfg *config.ExhaustiveSettings
9091
if m.cfg != nil {
9192
govetCfg = &m.cfg.LintersSettings.Govet
9293
testpackageCfg = &m.cfg.LintersSettings.Testpackage
94+
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
9395
}
9496
const megacheckName = "megacheck"
9597
lcs := []*linter.Config{
@@ -275,6 +277,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
275277
linter.NewConfig(golinters.NewExportLoopRef()).
276278
WithPresets(linter.PresetBugs).
277279
WithURL("https://github.com/kyoh86/exportloopref"),
280+
linter.NewConfig(golinters.NewExhaustive(exhaustiveCfg)).
281+
WithPresets(linter.PresetBugs).
282+
WithLoadForGoAnalysis().
283+
WithURL("https://github.com/nishanths/exhaustive"),
278284
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
279285
linter.NewConfig(golinters.NewNoLintLint()).
280286
WithPresets(linter.PresetStyle).

test/linters_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import (
1717

1818
func runGoErrchk(c *exec.Cmd, files []string, t *testing.T) {
1919
output, err := c.CombinedOutput()
20-
assert.Error(t, err)
21-
_, ok := err.(*exec.ExitError)
22-
assert.True(t, ok, err)
20+
// The returned error will be nil if the test file does not have any issues
21+
// and thus the linter exits with exit code 0. So perform the additional
22+
// assertions only if the error is non-nil.
23+
if err != nil {
24+
_, ok := err.(*exec.ExitError)
25+
assert.True(t, ok, err)
26+
}
2327

2428
// TODO: uncomment after deprecating go1.11
2529
// assert.Equal(t, exitcodes.IssuesFound, exitErr.ExitCode())

test/testdata/configs/exhaustive.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
linters-settings:
2+
exhaustive:
3+
default-signifies-exhaustive: true

test/testdata/exhaustive.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//args: -Eexhaustive
2+
package testdata
3+
4+
type Direction int
5+
6+
const (
7+
North Direction = iota
8+
East
9+
South
10+
West
11+
)
12+
13+
func processDirection(d Direction) {
14+
switch d { // ERROR "missing cases in switch of type Direction: East, West"
15+
case North, South:
16+
}
17+
}

test/testdata/exhaustive_default.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//args: -Eexhaustive
2+
//config_path: testdata/configs/exhaustive.yml
3+
package testdata
4+
5+
type Direction int
6+
7+
const (
8+
North Direction = iota
9+
East
10+
South
11+
West
12+
)
13+
14+
func processDirectionDefault(d Direction) {
15+
switch d {
16+
case North, South:
17+
default:
18+
}
19+
}

0 commit comments

Comments
 (0)