Skip to content

Commit 380699a

Browse files
xobotyildez
andauthored
feat: add exhaustruct linter (#2667)
Co-authored-by: Fernandez Ludovic <[email protected]>
1 parent acceecf commit 380699a

13 files changed

+380
-107
lines changed

Diff for: .golangci.example.yml

+10
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,16 @@ linters-settings:
285285
- '*.Test'
286286
- 'example.com/package.ExampleStruct'
287287

288+
exhaustruct:
289+
# List of regular expressions to match struct packages and names.
290+
# If this list is empty, all structs are tested.
291+
include:
292+
- '.*\.Test'
293+
- 'example\.com/package\.ExampleStruct[\d]{1,2}'
294+
# List of regular expressions to exclude struct packages and names from check.
295+
exclude:
296+
- 'cobra\.Command$'
297+
288298
forbidigo:
289299
# Forbid the following identifiers (list of regexp).
290300
forbid:

Diff for: go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/Antonboom/nilnil v0.1.1
99
github.com/BurntSushi/toml v1.1.0
1010
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24
11+
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0
1112
github.com/OpenPeeDeeP/depguard v1.1.0
1213
github.com/alexkohler/prealloc v1.0.0
1314
github.com/ashanbrown/forbidigo v1.3.0

Diff for: go.sum

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pkg/config/linters_settings.go

+6
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ type LintersSettings struct {
123123
ErrorLint ErrorLintSettings
124124
Exhaustive ExhaustiveSettings
125125
ExhaustiveStruct ExhaustiveStructSettings
126+
Exhaustruct ExhaustructSettings
126127
Forbidigo ForbidigoSettings
127128
Funlen FunlenSettings
128129
Gci GciSettings
@@ -255,6 +256,11 @@ type ExhaustiveStructSettings struct {
255256
StructPatterns []string `mapstructure:"struct-patterns"`
256257
}
257258

259+
type ExhaustructSettings struct {
260+
Include []string `mapstructure:"include"`
261+
Exclude []string `mapstructure:"exclude"`
262+
}
263+
258264
type ForbidigoSettings struct {
259265
Forbid []string `mapstructure:"forbid"`
260266
ExcludeGodocExamples bool `mapstructure:"exclude-godoc-examples"`

Diff for: pkg/golinters/commons.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package golinters
2+
3+
import "github.com/golangci/golangci-lint/pkg/logutils"
4+
5+
// linterLogger must be use only when the context logger is not available.
6+
var linterLogger = logutils.NewStderrLog("linter")

Diff for: pkg/golinters/exhaustruct.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package golinters
2+
3+
import (
4+
"github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer"
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 NewExhaustruct(settings *config.ExhaustructSettings) *goanalysis.Linter {
12+
var include, exclude []string
13+
14+
if settings != nil {
15+
include = settings.Include
16+
exclude = settings.Exclude
17+
}
18+
19+
a, err := analyzer.NewAnalyzer(include, exclude)
20+
if err != nil {
21+
linterLogger.Fatalf("exhaustruct configuration: %v", err)
22+
}
23+
24+
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil).
25+
WithLoadMode(goanalysis.LoadModeTypesInfo)
26+
}

Diff for: pkg/lint/lintersdb/manager.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
107107
var errorlintCfg *config.ErrorLintSettings
108108
var exhaustiveCfg *config.ExhaustiveSettings
109109
var exhaustiveStructCfg *config.ExhaustiveStructSettings
110+
var exhaustructCfg *config.ExhaustructSettings
110111
var gciCfg *config.GciSettings
111112
var goModDirectivesCfg *config.GoModDirectivesSettings
112113
var goMndCfg *config.GoMndSettings
@@ -140,6 +141,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
140141
errorlintCfg = &m.cfg.LintersSettings.ErrorLint
141142
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
142143
exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct
144+
exhaustructCfg = &m.cfg.LintersSettings.Exhaustruct
143145
gciCfg = &m.cfg.LintersSettings.Gci
144146
goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives
145147
goMndCfg = &m.cfg.LintersSettings.Gomnd
@@ -281,7 +283,14 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
281283
WithSince("v1.32.0").
282284
WithPresets(linter.PresetStyle, linter.PresetTest).
283285
WithLoadForGoAnalysis().
284-
WithURL("https://github.com/mbilski/exhaustivestruct"),
286+
WithURL("https://github.com/mbilski/exhaustivestruct").
287+
Deprecated("The owner seems to have abandoned the linter.", "v1.46.0", "exhaustruct"),
288+
289+
linter.NewConfig(golinters.NewExhaustruct(exhaustructCfg)).
290+
WithSince("v1.46.0").
291+
WithPresets(linter.PresetStyle, linter.PresetTest).
292+
WithLoadForGoAnalysis().
293+
WithURL("https://github.com/GaijinEntertainment/go-exhaustruct"),
285294

286295
linter.NewConfig(golinters.NewExportLoopRef()).
287296
WithSince("v1.28.0").

Diff for: test/testdata/configs/exhaustivestruct.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
linters-settings:
2+
exhaustivestruct:
3+
struct-patterns:
4+
- '*.ExhaustiveStructCustom'
5+
- '*.ExhaustiveStructCustom2'

Diff for: test/testdata/configs/exhaustruct.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
linters-settings:
2+
exhaustruct:
3+
include:
4+
- .*\.ExhaustructCustom
5+
exclude:
6+
- .*\.ExhaustructCustom[\d]{1,2}

Diff for: test/testdata/exhaustivestruct.go

+31-25
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
1-
//args: -Eexhaustivestruct
1+
// args: -Eexhaustivestruct --internal-cmd-test
22
package testdata
33

44
import "time"
55

6-
type Test struct {
6+
type ExhaustiveStruct struct {
77
A string
88
B int
99
c bool // private field inside the same package are not ignored
1010
D float64
1111
E time.Time
1212
}
1313

14-
var pass = Test{
15-
A: "a",
16-
B: 0,
17-
c: false,
18-
D: 1.0,
19-
E: time.Now(),
20-
}
14+
func exhaustiveStruct() {
15+
// pass
16+
_ = ExhaustiveStruct{
17+
A: "a",
18+
B: 0,
19+
c: false,
20+
D: 1.0,
21+
E: time.Now(),
22+
}
2123

22-
var failPrivate = Test{ // ERROR "c is missing in Test"
23-
A: "a",
24-
B: 0,
25-
D: 1.0,
26-
E: time.Now(),
27-
}
24+
// failPrivate
25+
_ = ExhaustiveStruct{ // ERROR "c is missing in ExhaustiveStruct"
26+
A: "a",
27+
B: 0,
28+
D: 1.0,
29+
E: time.Now(),
30+
}
2831

29-
var fail = Test{ // ERROR "B is missing in Test"
30-
A: "a",
31-
c: false,
32-
D: 1.0,
33-
E: time.Now(),
34-
}
32+
// fail
33+
_ = ExhaustiveStruct{ // ERROR "B is missing in ExhaustiveStruct"
34+
A: "a",
35+
c: false,
36+
D: 1.0,
37+
E: time.Now(),
38+
}
3539

36-
var failMultiple = Test{ // ERROR "B, D are missing in Test"
37-
A: "a",
38-
c: false,
39-
E: time.Now(),
40+
// failMultiple
41+
_ = ExhaustiveStruct{ // ERROR "B, D are missing in ExhaustiveStruct"
42+
A: "a",
43+
c: false,
44+
E: time.Now(),
45+
}
4046
}

0 commit comments

Comments
 (0)