From 69d6cc93cbf512379d42b5433fc844971ddc8d5d Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Tue, 10 Oct 2023 02:37:47 +1100 Subject: [PATCH] feat: add `gochecksumtype` linter (#3671) Co-authored-by: Fernandez Ludovic --- .golangci.reference.yml | 2 + go.mod | 1 + go.sum | 4 ++ pkg/golinters/gochecksumtype.go | 80 +++++++++++++++++++++++++++++++++ pkg/lint/lintersdb/manager.go | 6 +++ test/testdata/gochecksumtype.go | 38 ++++++++++++++++ 6 files changed, 131 insertions(+) create mode 100644 pkg/golinters/gochecksumtype.go create mode 100644 test/testdata/gochecksumtype.go diff --git a/.golangci.reference.yml b/.golangci.reference.yml index a380182c..b8a8a98f 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -2214,6 +2214,7 @@ linters: - gocheckcompilerdirectives - gochecknoglobals - gochecknoinits + - gochecksumtype - gocognit - goconst - gocritic @@ -2330,6 +2331,7 @@ linters: - gocheckcompilerdirectives - gochecknoglobals - gochecknoinits + - gochecksumtype - gocognit - goconst - gocritic diff --git a/go.mod b/go.mod index 3b83d7d2..3b2fd4fd 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 github.com/OpenPeeDeeP/depguard/v2 v2.1.0 + github.com/alecthomas/go-check-sumtype v0.1.3 github.com/alexkohler/nakedret/v2 v2.0.2 github.com/alexkohler/prealloc v1.0.0 github.com/alingse/asasalint v0.0.11 diff --git a/go.sum b/go.sum index 85229921..c0397e79 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,10 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/f1kSDVYY= github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= +github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= +github.com/alecthomas/go-check-sumtype v0.1.3 h1:M+tqMxB68hcgccRXBMVCPI4UJ+QUfdSx0xdbypKCqA8= +github.com/alecthomas/go-check-sumtype v0.1.3/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= diff --git a/pkg/golinters/gochecksumtype.go b/pkg/golinters/gochecksumtype.go new file mode 100644 index 00000000..fcc0cad5 --- /dev/null +++ b/pkg/golinters/gochecksumtype.go @@ -0,0 +1,80 @@ +package golinters + +import ( + "strings" + "sync" + + gochecksumtype "github.com/alecthomas/go-check-sumtype" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/packages" + + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/result" +) + +const goCheckSumTypeName = "gochecksumtype" + +func NewGoCheckSumType() *goanalysis.Linter { + var mu sync.Mutex + var resIssues []goanalysis.Issue + + analyzer := &analysis.Analyzer{ + Name: goCheckSumTypeName, + Doc: goanalysis.TheOnlyanalyzerDoc, + Run: func(pass *analysis.Pass) (any, error) { + issues, err := runGoCheckSumType(pass) + if err != nil { + return nil, err + } + + if len(issues) == 0 { + return nil, nil + } + + mu.Lock() + resIssues = append(resIssues, issues...) + mu.Unlock() + + return nil, nil + }, + } + + return goanalysis.NewLinter( + goCheckSumTypeName, + `Run exhaustiveness checks on Go "sum types"`, + []*analysis.Analyzer{analyzer}, + nil, + ).WithIssuesReporter(func(ctx *linter.Context) []goanalysis.Issue { + return resIssues + }).WithLoadMode(goanalysis.LoadModeTypesInfo) +} + +func runGoCheckSumType(pass *analysis.Pass) ([]goanalysis.Issue, error) { + var resIssues []goanalysis.Issue + + pkg := &packages.Package{ + Fset: pass.Fset, + Syntax: pass.Files, + Types: pass.Pkg, + TypesInfo: pass.TypesInfo, + } + + var unknownError error + errors := gochecksumtype.Run([]*packages.Package{pkg}) + for _, err := range errors { + err, ok := err.(gochecksumtype.Error) + if !ok { + unknownError = err + continue + } + + resIssues = append(resIssues, goanalysis.NewIssue(&result.Issue{ + FromLinter: goCheckSumTypeName, + Text: strings.TrimPrefix(err.Error(), err.Pos().String()+": "), + Pos: err.Pos(), + }, pass)) + } + + return resIssues, unknownError +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index c0cb36ae..71dc3ceb 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -441,6 +441,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("v1.12.0"). WithPresets(linter.PresetStyle), + linter.NewConfig(golinters.NewGoCheckSumType()). + WithSince("v1.55.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/alecthomas/go-check-sumtype"), + linter.NewConfig(golinters.NewGocognit(gocognitCfg)). WithSince("v1.20.0"). WithPresets(linter.PresetComplexity). diff --git a/test/testdata/gochecksumtype.go b/test/testdata/gochecksumtype.go new file mode 100644 index 00000000..47ae3b9a --- /dev/null +++ b/test/testdata/gochecksumtype.go @@ -0,0 +1,38 @@ +//golangcitest:args -Egochecksumtype +package testdata + +import ( + "log" +) + +//sumtype:decl +type SumType interface{ isSumType() } + +//sumtype:decl +type One struct{} // want "type 'One' is not an interface" + +func (One) isSumType() {} + +type Two struct{} + +func (Two) isSumType() {} + +func sumTypeTest() { + var sum SumType = One{} + switch sum.(type) { // want "exhaustiveness check failed for sum type.*SumType.*missing cases for Two" + case One: + } + + switch sum.(type) { // want "exhaustiveness check failed for sum type.*SumType.*missing cases for Two" + case One: + default: + panic("??") + } + + log.Println("??") + + switch sum.(type) { + case One: + case Two: + } +}