Skip to content

Commit

Permalink
feat: add exhaustruct linter (golangci#2667)
Browse files Browse the repository at this point in the history
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
  • Loading branch information
2 people authored and SeigeC committed Apr 4, 2023
1 parent 4364b08 commit 6976786
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 107 deletions.
10 changes: 10 additions & 0 deletions .golangci.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ linters-settings:
- '*.Test'
- 'example.com/package.ExampleStruct'

exhaustruct:
# List of regular expressions to match struct packages and names.
# If this list is empty, all structs are tested.
include:
- '.*\.Test'
- 'example\.com/package\.ExampleStruct[\d]{1,2}'
# List of regular expressions to exclude struct packages and names from check.
exclude:
- 'cobra\.Command$'

forbidigo:
# Forbid the following identifiers (list of regexp).
forbid:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/Antonboom/nilnil v0.1.1
github.com/BurntSushi/toml v1.1.0
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0
github.com/OpenPeeDeeP/depguard v1.1.0
github.com/alexkohler/prealloc v1.0.0
github.com/ashanbrown/forbidigo v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions pkg/config/linters_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ type LintersSettings struct {
ErrorLint ErrorLintSettings
Exhaustive ExhaustiveSettings
ExhaustiveStruct ExhaustiveStructSettings
Exhaustruct ExhaustructSettings
Forbidigo ForbidigoSettings
Funlen FunlenSettings
Gci GciSettings
Expand Down Expand Up @@ -255,6 +256,11 @@ type ExhaustiveStructSettings struct {
StructPatterns []string `mapstructure:"struct-patterns"`
}

type ExhaustructSettings struct {
Include []string `mapstructure:"include"`
Exclude []string `mapstructure:"exclude"`
}

type ForbidigoSettings struct {
Forbid []string `mapstructure:"forbid"`
ExcludeGodocExamples bool `mapstructure:"exclude-godoc-examples"`
Expand Down
6 changes: 6 additions & 0 deletions pkg/golinters/commons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package golinters

import "github.com/golangci/golangci-lint/pkg/logutils"

// linterLogger must be use only when the context logger is not available.
var linterLogger = logutils.NewStderrLog("linter")
26 changes: 26 additions & 0 deletions pkg/golinters/exhaustruct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package golinters

import (
"github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer"
"golang.org/x/tools/go/analysis"

"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
)

func NewExhaustruct(settings *config.ExhaustructSettings) *goanalysis.Linter {
var include, exclude []string

if settings != nil {
include = settings.Include
exclude = settings.Exclude
}

a, err := analyzer.NewAnalyzer(include, exclude)
if err != nil {
linterLogger.Fatalf("exhaustruct configuration: %v", err)
}

return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil).
WithLoadMode(goanalysis.LoadModeTypesInfo)
}
11 changes: 10 additions & 1 deletion pkg/lint/lintersdb/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
var errorlintCfg *config.ErrorLintSettings
var exhaustiveCfg *config.ExhaustiveSettings
var exhaustiveStructCfg *config.ExhaustiveStructSettings
var exhaustructCfg *config.ExhaustructSettings
var gciCfg *config.GciSettings
var goModDirectivesCfg *config.GoModDirectivesSettings
var goMndCfg *config.GoMndSettings
Expand Down Expand Up @@ -140,6 +141,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
errorlintCfg = &m.cfg.LintersSettings.ErrorLint
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct
exhaustructCfg = &m.cfg.LintersSettings.Exhaustruct
gciCfg = &m.cfg.LintersSettings.Gci
goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives
goMndCfg = &m.cfg.LintersSettings.Gomnd
Expand Down Expand Up @@ -281,7 +283,14 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithSince("v1.32.0").
WithPresets(linter.PresetStyle, linter.PresetTest).
WithLoadForGoAnalysis().
WithURL("https://github.com/mbilski/exhaustivestruct"),
WithURL("https://github.com/mbilski/exhaustivestruct").
Deprecated("The owner seems to have abandoned the linter.", "v1.46.0", "exhaustruct"),

linter.NewConfig(golinters.NewExhaustruct(exhaustructCfg)).
WithSince("v1.46.0").
WithPresets(linter.PresetStyle, linter.PresetTest).
WithLoadForGoAnalysis().
WithURL("https://github.com/GaijinEntertainment/go-exhaustruct"),

linter.NewConfig(golinters.NewExportLoopRef()).
WithSince("v1.28.0").
Expand Down
5 changes: 5 additions & 0 deletions test/testdata/configs/exhaustivestruct.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
linters-settings:
exhaustivestruct:
struct-patterns:
- '*.ExhaustiveStructCustom'
- '*.ExhaustiveStructCustom2'
6 changes: 6 additions & 0 deletions test/testdata/configs/exhaustruct.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
linters-settings:
exhaustruct:
include:
- .*\.ExhaustructCustom
exclude:
- .*\.ExhaustructCustom[\d]{1,2}
56 changes: 31 additions & 25 deletions test/testdata/exhaustivestruct.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
//args: -Eexhaustivestruct
// args: -Eexhaustivestruct --internal-cmd-test
package testdata

import "time"

type Test struct {
type ExhaustiveStruct struct {
A string
B int
c bool // private field inside the same package are not ignored
D float64
E time.Time
}

var pass = Test{
A: "a",
B: 0,
c: false,
D: 1.0,
E: time.Now(),
}
func exhaustiveStruct() {
// pass
_ = ExhaustiveStruct{
A: "a",
B: 0,
c: false,
D: 1.0,
E: time.Now(),
}

var failPrivate = Test{ // ERROR "c is missing in Test"
A: "a",
B: 0,
D: 1.0,
E: time.Now(),
}
// failPrivate
_ = ExhaustiveStruct{ // ERROR "c is missing in ExhaustiveStruct"
A: "a",
B: 0,
D: 1.0,
E: time.Now(),
}

var fail = Test{ // ERROR "B is missing in Test"
A: "a",
c: false,
D: 1.0,
E: time.Now(),
}
// fail
_ = ExhaustiveStruct{ // ERROR "B is missing in ExhaustiveStruct"
A: "a",
c: false,
D: 1.0,
E: time.Now(),
}

var failMultiple = Test{ // ERROR "B, D are missing in Test"
A: "a",
c: false,
E: time.Now(),
// failMultiple
_ = ExhaustiveStruct{ // ERROR "B, D are missing in ExhaustiveStruct"
A: "a",
c: false,
E: time.Now(),
}
}
Loading

0 comments on commit 6976786

Please sign in to comment.