Skip to content

Commit 6791ae1

Browse files
committed
feat: add exhaustruct linter
This linter can be called a successor of `exhaustivestruct`, and: - it is at least **2.5+ times faster**, due to better algorithm; - can receive `include` and/or `exclude` patterns; - expects received patterns to be RegExp, therefore this package is not api-compatible with `exhaustivestruct`. Also: deprecate `exhaustivestruct` linter
1 parent bb2fb45 commit 6791ae1

File tree

8 files changed

+218
-1
lines changed

8 files changed

+218
-1
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'
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

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ require (
104104
mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5
105105
)
106106

107+
require github.com/GaijinEntertainment/go-exhaustruct v1.0.0
108+
107109
require (
108110
github.com/Masterminds/semver v1.5.0 // indirect
109111
github.com/beorn7/perks v1.0.1 // indirect

Diff for: go.sum

+4
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/exhaustruct.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package golinters
2+
3+
import (
4+
"strings"
5+
6+
"github.com/GaijinEntertainment/go-exhaustruct/pkg/analyzer"
7+
"golang.org/x/tools/go/analysis"
8+
9+
"github.com/golangci/golangci-lint/pkg/config"
10+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
11+
)
12+
13+
func NewExhaustruct(settings *config.ExhaustructSettings) *goanalysis.Linter {
14+
a := analyzer.Analyzer
15+
16+
var cfg map[string]map[string]interface{}
17+
if settings != nil {
18+
cfg = map[string]map[string]interface{}{
19+
a.Name: {
20+
"include": strings.Join(settings.Include, ","),
21+
"exclude": strings.Join(settings.Exclude, ","),
22+
},
23+
}
24+
}
25+
26+
return goanalysis.NewLinter(
27+
a.Name,
28+
a.Doc,
29+
[]*analysis.Analyzer{a},
30+
cfg,
31+
).WithLoadMode(goanalysis.LoadModeTypesInfo)
32+
}

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("Owner seems to abandon 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/exhaustruct.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//args: -Eexhaustruct
2+
package testdata
3+
4+
import "time"
5+
6+
type Test struct {
7+
A string
8+
B int
9+
c bool // private field inside the same package are not ignored
10+
D float64
11+
E time.Time
12+
}
13+
14+
var pass = Test{
15+
A: "a",
16+
B: 0,
17+
c: false,
18+
D: 1.0,
19+
E: time.Now(),
20+
}
21+
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+
}
28+
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+
}
35+
36+
var failMultiple = Test{ // ERROR "B, D are missing in Test"
37+
A: "a",
38+
c: false,
39+
E: time.Now(),
40+
}

Diff for: test/testdata/exhaustruct_custom.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//args: -Eexhaustruct
2+
//config: linters-settings.exhaustruct.include=.*\.Test1$
3+
//config: linters-settings.exhaustruct.exclude=.*\.Test3$
4+
package testdata
5+
6+
import "time"
7+
8+
type Test1 struct {
9+
A string
10+
B int
11+
c bool // private field inside the same package are not ignored
12+
D float64
13+
E time.Time
14+
}
15+
16+
var passTest1 = Test1{
17+
A: "a",
18+
B: 0,
19+
c: false,
20+
D: 1.0,
21+
E: time.Now(),
22+
}
23+
24+
var failTest1 = Test1{ // ERROR "B is missing in Test"
25+
A: "a",
26+
c: false,
27+
D: 1.0,
28+
E: time.Now(),
29+
}
30+
31+
var failMultipleTest1 = Test1{ // ERROR "B, D are missing in Test"
32+
A: "a",
33+
c: false,
34+
E: time.Now(),
35+
}
36+
37+
var failPrivateTest1 = Test1{ // ERROR "c is missing in Test"
38+
A: "a",
39+
B: 0,
40+
D: 1.0,
41+
E: time.Now(),
42+
}
43+
44+
type Test2 struct {
45+
A string
46+
B int
47+
c bool // private field inside the same package are not ignored
48+
D float64
49+
E time.Time
50+
}
51+
52+
var passTest2 = Test1{
53+
A: "a",
54+
B: 0,
55+
c: false,
56+
D: 1.0,
57+
E: time.Now(),
58+
}
59+
60+
var failTest2 = Test2{
61+
A: "a",
62+
c: false,
63+
D: 1.0,
64+
E: time.Now(),
65+
}
66+
67+
var failMultipleTest2 = Test2{
68+
A: "a",
69+
c: false,
70+
E: time.Now(),
71+
}
72+
73+
var failPrivateTest2 = Test2{
74+
A: "a",
75+
B: 0,
76+
D: 1.0,
77+
E: time.Now(),
78+
}
79+
80+
type Test3 struct {
81+
A string
82+
B int
83+
c bool // private field inside the same package are not ignored
84+
D float64
85+
E time.Time
86+
}
87+
88+
var passTest3 = Test3{
89+
A: "a",
90+
B: 0,
91+
c: false,
92+
D: 1.0,
93+
E: time.Now(),
94+
}
95+
96+
var failTest3 = Test3{
97+
A: "a",
98+
c: false,
99+
D: 1.0,
100+
E: time.Now(),
101+
}
102+
103+
var failMultipleTest3 = Test3{
104+
A: "a",
105+
c: false,
106+
E: time.Now(),
107+
}
108+
109+
var failPrivateTest3 = Test3{
110+
A: "a",
111+
B: 0,
112+
D: 1.0,
113+
E: time.Now(),
114+
}

0 commit comments

Comments
 (0)