Skip to content

Commit 2de11e2

Browse files
alecthomasldez
authored andcommitted
feat: add gochecksumtype linter
`gochecksumtype` is a linter that checks that type switches on "sum types" in Go are exhaustive. https://github.com/alecthomas/go-check-sumtype This is based on BurntSushi/go-sumtype, but fixes, modernises and simplifies it.
1 parent 5195575 commit 2de11e2

File tree

7 files changed

+119
-1
lines changed

7 files changed

+119
-1
lines changed

.golangci.reference.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,7 @@ linters:
20992099
- gocheckcompilerdirectives
21002100
- gochecknoglobals
21012101
- gochecknoinits
2102+
- gochecksumtype
21022103
- gocognit
21032104
- goconst
21042105
- gocritic
@@ -2211,6 +2212,7 @@ linters:
22112212
- gocheckcompilerdirectives
22122213
- gochecknoglobals
22132214
- gochecknoinits
2215+
- gochecksumtype
22142216
- gocognit
22152217
- goconst
22162218
- gocritic

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ require (
122122

123123
require (
124124
github.com/Masterminds/semver v1.5.0 // indirect
125+
github.com/alecthomas/go-check-sumtype v0.1.3
125126
github.com/beorn7/perks v1.0.1 // indirect
126127
github.com/cespare/xxhash/v2 v2.1.2 // indirect
127128
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/golinters/gochecksumtype.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package golinters
2+
3+
import (
4+
"strings"
5+
"sync"
6+
7+
gochecksumtype "github.com/alecthomas/go-check-sumtype"
8+
"golang.org/x/tools/go/analysis"
9+
"golang.org/x/tools/go/packages"
10+
11+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
12+
"github.com/golangci/golangci-lint/pkg/lint/linter"
13+
"github.com/golangci/golangci-lint/pkg/result"
14+
)
15+
16+
const goCheckSumTypeName = "gochecksumtype"
17+
18+
func NewGoCheckSumType() *goanalysis.Linter {
19+
var mu sync.Mutex
20+
var resIssues []goanalysis.Issue
21+
22+
analyzer := &analysis.Analyzer{
23+
Name: goCheckSumTypeName,
24+
Doc: goanalysis.TheOnlyanalyzerDoc,
25+
Run: func(pass *analysis.Pass) (interface{}, error) {
26+
issues, err := runGoCheckSumType(pass)
27+
if err != nil {
28+
return nil, err
29+
}
30+
if len(issues) == 0 {
31+
return nil, nil
32+
}
33+
mu.Lock()
34+
resIssues = append(resIssues, issues...)
35+
mu.Unlock()
36+
return nil, nil
37+
},
38+
}
39+
return goanalysis.NewLinter(
40+
goCheckSumTypeName,
41+
`Run exhaustiveness checks on Go "sum types"`,
42+
[]*analysis.Analyzer{analyzer},
43+
nil,
44+
).WithIssuesReporter(func(ctx *linter.Context) []goanalysis.Issue {
45+
return resIssues
46+
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
47+
}
48+
49+
func runGoCheckSumType(pass *analysis.Pass) ([]goanalysis.Issue, error) {
50+
resIssues := []goanalysis.Issue{}
51+
pkg := &packages.Package{
52+
Fset: pass.Fset,
53+
Syntax: pass.Files,
54+
Types: pass.Pkg,
55+
TypesInfo: pass.TypesInfo,
56+
}
57+
var unknownError error
58+
errors := gochecksumtype.Run([]*packages.Package{pkg})
59+
for _, err := range errors {
60+
err, ok := err.(gochecksumtype.Error)
61+
if !ok {
62+
unknownError = err
63+
continue
64+
}
65+
prefix := err.Pos().String() + ": "
66+
resIssues = append(resIssues, goanalysis.NewIssue(&result.Issue{
67+
FromLinter: goCheckSumTypeName,
68+
Text: strings.TrimPrefix(err.Error(), prefix),
69+
Pos: err.Pos(),
70+
}, pass))
71+
}
72+
return resIssues, unknownError
73+
}

pkg/golinters/unparam.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ func NewUnparam(settings *config.UnparamSettings) *goanalysis.Linter {
3737
mu.Lock()
3838
resIssues = append(resIssues, issues...)
3939
mu.Unlock()
40-
4140
return nil, nil
4241
},
4342
}

pkg/lint/lintersdb/manager.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
461461
WithSince("v1.12.0").
462462
WithPresets(linter.PresetStyle),
463463

464+
linter.NewConfig(golinters.NewGoCheckSumType()).
465+
WithSince("v1.51.2").
466+
WithPresets(linter.PresetBugs).
467+
WithURL("https://github.com/alecthomas/go-check-sumtype"),
468+
464469
linter.NewConfig(golinters.NewGocognit(gocognitCfg)).
465470
WithSince("v1.20.0").
466471
WithPresets(linter.PresetComplexity).

test/testdata/gochecksumtype.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//golangcitest:args -Egochecksumtype
2+
package testdata
3+
4+
//sumtype:decl
5+
type SumType interface{ isSumType() }
6+
7+
//sumtype:decl
8+
type One struct{} // want "type 'One' is not an interface"
9+
10+
func (One) isSumType() {}
11+
12+
type Two struct{}
13+
14+
func (Two) isSumType() {}
15+
16+
func sumTypeTest() {
17+
var sum SumType = One{}
18+
switch sum.(type) { // want "exhaustiveness check failed for sum type.*SumType.*missing cases for Two"
19+
case One:
20+
}
21+
22+
switch sum.(type) { // want "exhaustiveness check failed for sum type.*SumType.*missing cases for Two"
23+
case One:
24+
default:
25+
panic("??")
26+
}
27+
28+
switch sum.(type) {
29+
case One:
30+
case Two:
31+
}
32+
}

0 commit comments

Comments
 (0)