diff --git a/.golangci.example.yml b/.golangci.example.yml index e39ecd062f92..a9ec5a75a17b 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -111,6 +111,12 @@ linters-settings: # 'default' case is present, even if all enum members aren't listed in the # switch default-signifies-exhaustive: false + exhaustivestruct: + struct-patterns: + - '*.Test' + - '*.Test2' + - '*.Embedded' + - '*.External' funlen: lines: 60 statements: 40 diff --git a/pkg/config/config.go b/pkg/config/config.go index 33f38b827676..923087ca2f9d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -249,31 +249,32 @@ type LintersSettings struct { } `mapstructure:"blocked"` } - WSL WSLSettings - Lll LllSettings - Unparam UnparamSettings - Nakedret NakedretSettings - Prealloc PreallocSettings - Errcheck ErrcheckSettings - Gocritic GocriticSettings - Godox GodoxSettings - Dogsled DogsledSettings - Gocognit GocognitSettings - Godot GodotSettings - Goheader GoHeaderSettings - Testpackage TestpackageSettings - Nestif NestifSettings - NoLintLint NoLintLintSettings - Exhaustive ExhaustiveSettings - Gofumpt GofumptSettings - ErrorLint ErrorLintSettings - Makezero MakezeroSettings - Revive ReviveSettings - Thelper ThelperSettings - Forbidigo ForbidigoSettings - Ifshort IfshortSettings - Predeclared PredeclaredSettings - Cyclop Cyclop + WSL WSLSettings + Lll LllSettings + Unparam UnparamSettings + Nakedret NakedretSettings + Prealloc PreallocSettings + Errcheck ErrcheckSettings + Gocritic GocriticSettings + Godox GodoxSettings + Dogsled DogsledSettings + Gocognit GocognitSettings + Godot GodotSettings + Goheader GoHeaderSettings + Testpackage TestpackageSettings + Nestif NestifSettings + NoLintLint NoLintLintSettings + Exhaustive ExhaustiveSettings + ExhaustiveStruct ExhaustiveStructSettings + Gofumpt GofumptSettings + ErrorLint ErrorLintSettings + Makezero MakezeroSettings + Revive ReviveSettings + Thelper ThelperSettings + Forbidigo ForbidigoSettings + Ifshort IfshortSettings + Predeclared PredeclaredSettings + Cyclop Cyclop Custom map[string]CustomLinterSettings } @@ -387,6 +388,10 @@ type ExhaustiveSettings struct { DefaultSignifiesExhaustive bool `mapstructure:"default-signifies-exhaustive"` } +type ExhaustiveStructSettings struct { + StructPatterns []string `mapstructure:"struct-patterns"` +} + type GofumptSettings struct { ExtraRules bool `mapstructure:"extra-rules"` } diff --git a/pkg/golinters/exhaustivestruct.go b/pkg/golinters/exhaustivestruct.go index d84746141de7..6a1dbd71c5a3 100644 --- a/pkg/golinters/exhaustivestruct.go +++ b/pkg/golinters/exhaustivestruct.go @@ -1,17 +1,31 @@ package golinters import ( + "strings" + "github.com/mbilski/exhaustivestruct/pkg/analyzer" "golang.org/x/tools/go/analysis" + "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" ) -func NewExhaustiveStruct() *goanalysis.Linter { +func NewExhaustiveStruct(settings *config.ExhaustiveStructSettings) *goanalysis.Linter { + a := analyzer.Analyzer + + var cfg map[string]map[string]interface{} + if settings != nil { + cfg = map[string]map[string]interface{}{ + a.Name: { + "struct_patterns": strings.Join(settings.StructPatterns, ","), + }, + } + } + return goanalysis.NewLinter( - "exhaustivestruct", - "Checks if all struct's fields are initialized", - []*analysis.Analyzer{analyzer.Analyzer}, - nil, + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + cfg, ).WithLoadMode(goanalysis.LoadModeTypesInfo) } diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 11407913dbc7..bdf7a870441b 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -92,6 +92,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var govetCfg *config.GovetSettings var testpackageCfg *config.TestpackageSettings var exhaustiveCfg *config.ExhaustiveSettings + var exhaustiveStructCfg *config.ExhaustiveStructSettings var errorlintCfg *config.ErrorLintSettings var thelperCfg *config.ThelperSettings var predeclaredCfg *config.PredeclaredSettings @@ -102,6 +103,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { govetCfg = &m.cfg.LintersSettings.Govet testpackageCfg = &m.cfg.LintersSettings.Testpackage exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive + exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct errorlintCfg = &m.cfg.LintersSettings.ErrorLint thelperCfg = &m.cfg.LintersSettings.Thelper predeclaredCfg = &m.cfg.LintersSettings.Predeclared @@ -339,7 +341,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetStyle). WithLoadForGoAnalysis(). WithURL("https://github.com/moricho/tparallel"), - linter.NewConfig(golinters.NewExhaustiveStruct()). + linter.NewConfig(golinters.NewExhaustiveStruct(exhaustiveStructCfg)). WithPresets(linter.PresetStyle). WithLoadForGoAnalysis(). WithURL("https://github.com/mbilski/exhaustivestruct"), diff --git a/test/testdata/exhaustivestruct_custom.go b/test/testdata/exhaustivestruct_custom.go new file mode 100644 index 000000000000..f3e1c7fa6fbf --- /dev/null +++ b/test/testdata/exhaustivestruct_custom.go @@ -0,0 +1,113 @@ +//args: -Eexhaustivestruct +//config: linters-settings.exhaustivestruct.struct-patterns=*.Test1,*.Test3 +package testdata + +import "time" + +type Test1 struct { + A string + B int + c bool // private field inside the same package are not ignored + D float64 + E time.Time +} + +var passTest1 = Test1{ + A: "a", + B: 0, + c: false, + D: 1.0, + E: time.Now(), +} + +var failTest1 = Test1{ // ERROR "B is missing in Test" + A: "a", + c: false, + D: 1.0, + E: time.Now(), +} + +var failMultipleTest1 = Test1{ // ERROR "B, D are missing in Test" + A: "a", + c: false, + E: time.Now(), +} + +var failPrivateTest1 = Test1{ // ERROR "c is missing in Test" + A: "a", + B: 0, + D: 1.0, + E: time.Now(), +} + +type Test2 struct { + A string + B int + c bool // private field inside the same package are not ignored + D float64 + E time.Time +} + +var passTest2 = Test1{ + A: "a", + B: 0, + c: false, + D: 1.0, + E: time.Now(), +} + +var failTest2 = Test2{ + A: "a", + c: false, + D: 1.0, + E: time.Now(), +} + +var failMultipleTest2 = Test2{ + A: "a", + c: false, + E: time.Now(), +} + +var failPrivateTest2 = Test2{ + A: "a", + B: 0, + D: 1.0, + E: time.Now(), +} + +type Test3 struct { + A string + B int + c bool // private field inside the same package are not ignored + D float64 + E time.Time +} + +var passTest3 = Test3{ + A: "a", + B: 0, + c: false, + D: 1.0, + E: time.Now(), +} + +var failTest3 = Test3{ // ERROR "B is missing in Test" + A: "a", + c: false, + D: 1.0, + E: time.Now(), +} + +var failMultipleTest3 = Test3{ // ERROR "B, D are missing in Test" + A: "a", + c: false, + E: time.Now(), +} + +var failPrivateTest3 = Test3{ // ERROR "c is missing in Test" + A: "a", + B: 0, + D: 1.0, + E: time.Now(), +}