add tagalign linter (#3709)

This commit is contained in:
Yifei Liu 2023-03-26 22:35:06 +08:00 committed by GitHub
parent 69f929b227
commit 134f2e0491
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 218 additions and 0 deletions

View File

@ -1739,6 +1739,39 @@ linters-settings:
# Default: ["200", "400", "404", "500"]
http-status-code-whitelist: [ "200", "400", "404", "500" ]
tagalign:
# Align and sort can be used together or separately.
#
# Whether enable align. If true, the struct tags will be aligned.
# eg:
# type FooBar struct {
# Bar string `json:"bar" validate:"required"`
# FooFoo int8 `json:"foo_foo" validate:"required"`
# }
# will be formatted to:
# type FooBar struct {
# Bar string `json:"bar" validate:"required"`
# FooFoo int8 `json:"foo_foo" validate:"required"`
# }
# Default: true.
align: false
# Whether enable tags sort.
# If true, the tags will be sorted by name in ascending order.
# eg: `xml:"bar" json:"bar" validate:"required"` -> `json:"bar" validate:"required" xml:"bar"`
# Default: true
sort: false
# Specify the order of tags, the other tags will be sorted by name.
# This option will be ignored if `sort` is false.
# Default: []
order:
- json
- yaml
- yml
- toml
- mapstructure
- binding
- validate
tagliatelle:
# Check the struct tag name case.
case:
@ -2118,6 +2151,7 @@ linters:
- staticcheck
- structcheck
- stylecheck
- tagalign
- tagliatelle
- tenv
- testableexamples
@ -2229,6 +2263,7 @@ linters:
- staticcheck
- structcheck
- stylecheck
- tagalign
- tagliatelle
- tenv
- testableexamples

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.19
require (
4d63.com/gocheckcompilerdirectives v1.2.1
4d63.com/gochecknoglobals v0.2.1
github.com/4meepo/tagalign v1.2.2
github.com/Abirdcfly/dupword v0.0.11
github.com/Antonboom/errname v0.1.9
github.com/Antonboom/nilnil v0.1.3

2
go.sum generated
View File

@ -40,6 +40,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/4meepo/tagalign v1.2.2 h1:kQeUTkFTaBRtd/7jm8OKJl9iHk0gAO+TDFPHGSna0aw=
github.com/4meepo/tagalign v1.2.2/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE=
github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16wgU=
github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA=
github.com/Antonboom/errname v0.1.9 h1:BZDX4r3l4TBZxZ2o2LNrlGxSHran4d1u4veZdoORTT4=

View File

@ -110,6 +110,11 @@ var defaultLintersSettings = LintersSettings{
Ignore: "",
Qualified: false,
},
TagAlign: TagAlignSettings{
Align: true,
Sort: true,
Order: nil,
},
Testpackage: TestpackageSettings{
SkipRegexp: `(export|internal)_test\.go`,
AllowPackages: []string{"main"},
@ -203,6 +208,7 @@ type LintersSettings struct {
Staticcheck StaticCheckSettings
Structcheck StructCheckSettings
Stylecheck StaticCheckSettings
TagAlign TagAlignSettings
Tagliatelle TagliatelleSettings
Tenv TenvSettings
Testpackage TestpackageSettings
@ -655,6 +661,12 @@ type StructCheckSettings struct {
CheckExportedFields bool `mapstructure:"exported-fields"`
}
type TagAlignSettings struct {
Align bool `mapstructure:"align"`
Sort bool `mapstructure:"sort"`
Order []string `mapstructure:"order"`
}
type TagliatelleSettings struct {
Case struct {
Rules map[string]string

70
pkg/golinters/tagalign.go Normal file
View File

@ -0,0 +1,70 @@
package golinters
import (
"sync"
"github.com/4meepo/tagalign"
"golang.org/x/tools/go/analysis"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
func NewTagAlign(settings *config.TagAlignSettings) *goanalysis.Linter {
var mu sync.Mutex
var resIssues []goanalysis.Issue
options := []tagalign.Option{tagalign.WithMode(tagalign.GolangciLintMode)}
if settings != nil {
options = append(options, tagalign.WithAlign(settings.Align))
if settings.Sort || len(settings.Order) > 0 {
options = append(options, tagalign.WithSort(settings.Order...))
}
}
analyzer := tagalign.NewAnalyzer(options...)
analyzer.Run = func(pass *analysis.Pass) (any, error) {
taIssues := tagalign.Run(pass, options...)
issues := make([]goanalysis.Issue, len(taIssues))
for i, issue := range taIssues {
report := &result.Issue{
FromLinter: analyzer.Name,
Pos: issue.Pos,
Text: issue.Message,
Replacement: &result.Replacement{
Inline: &result.InlineFix{
StartCol: issue.InlineFix.StartCol,
Length: issue.InlineFix.Length,
NewString: issue.InlineFix.NewString,
},
},
}
issues[i] = goanalysis.NewIssue(report, pass)
}
if len(issues) == 0 {
return nil, nil
}
mu.Lock()
resIssues = append(resIssues, issues...)
mu.Unlock()
return nil, nil
}
return goanalysis.NewLinter(
analyzer.Name,
analyzer.Doc,
[]*analysis.Analyzer{analyzer},
nil,
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
return resIssues
}).WithLoadMode(goanalysis.LoadModeSyntax)
}

View File

@ -165,6 +165,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
staticcheckCfg *config.StaticCheckSettings
structcheckCfg *config.StructCheckSettings
stylecheckCfg *config.StaticCheckSettings
tagalignCfg *config.TagAlignSettings
tagliatelleCfg *config.TagliatelleSettings
tenvCfg *config.TenvSettings
testpackageCfg *config.TestpackageSettings
@ -244,6 +245,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
staticcheckCfg = &m.cfg.LintersSettings.Staticcheck
structcheckCfg = &m.cfg.LintersSettings.Structcheck
stylecheckCfg = &m.cfg.LintersSettings.Stylecheck
tagalignCfg = &m.cfg.LintersSettings.TagAlign
tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle
tenvCfg = &m.cfg.LintersSettings.Tenv
testpackageCfg = &m.cfg.LintersSettings.Testpackage
@ -777,6 +779,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithPresets(linter.PresetStyle).
WithURL("https://github.com/dominikh/go-tools/tree/master/stylecheck"),
linter.NewConfig(golinters.NewTagAlign(tagalignCfg)).
WithSince("v1.53.0").
WithPresets(linter.PresetStyle, linter.PresetFormatting).
WithAutoFix().
WithURL("https://github.com/4meepo/tagalign"),
linter.NewConfig(golinters.NewTagliatelle(tagliatelleCfg)).
WithSince("v1.40.0").
WithPresets(linter.PresetStyle).

View File

@ -0,0 +1,3 @@
linters-settings:
tagalign:
sort: false

View File

@ -0,0 +1,7 @@
linters-settings:
tagalign:
align: false
order:
- "xml"
- "json"
- "yaml"

View File

@ -0,0 +1,4 @@
linters-settings:
tagalign:
align: false
sort: true

15
test/testdata/tagalign.go vendored Normal file
View File

@ -0,0 +1,15 @@
//golangcitest:args -Etagalign
package testdata
import "time"
type TagAlignExampleAlignSort struct {
Foo time.Duration `json:"foo,omitempty" yaml:"foo" xml:"foo" binding:"required" gorm:"column:foo" zip:"foo" validate:"required"` // want `binding:"required" gorm:"column:foo" json:"foo,omitempty" validate:"required" xml:"foo" yaml:"foo" zip:"foo"`
Bar int `validate:"required" yaml:"bar" xml:"bar" binding:"required" json:"bar,omitempty" gorm:"column:bar" zip:"bar" ` // want `binding:"required" gorm:"column:bar" json:"bar,omitempty" validate:"required" xml:"bar" yaml:"bar" zip:"bar"`
FooBar int `gorm:"column:fooBar" validate:"required" xml:"fooBar" binding:"required" json:"fooBar,omitempty" zip:"fooBar" yaml:"fooBar"` // want `binding:"required" gorm:"column:fooBar" json:"fooBar,omitempty" validate:"required" xml:"fooBar" yaml:"fooBar" zip:"fooBar"`
}
type TagAlignExampleAlignSort2 struct {
Foo int ` xml:"foo" json:"foo,omitempty" yaml:"foo" zip:"foo" binding:"required" gorm:"column:foo" validate:"required"` // want `binding:"required" gorm:"column:foo" json:"foo,omitempty" validate:"required" xml:"foo" yaml:"foo" zip:"foo"`
Bar int `validate:"required" gorm:"column:bar" yaml:"bar" xml:"bar" binding:"required" json:"bar" zip:"bar" ` // want `binding:"required" gorm:"column:bar" json:"bar" validate:"required" xml:"bar" yaml:"bar" zip:"bar"`
}

31
test/testdata/tagalign_align_only.go vendored Normal file
View File

@ -0,0 +1,31 @@
//golangcitest:args -Etagalign
//golangcitest:config_path testdata/configs/tagalign_align_only.yml
package testdata
import "time"
type TagAlignExampleAlignOnlyKO struct {
Foo time.Time `gorm:"column:foo" json:"foo,omitempty" xml:"foo" yaml:"foo" zip:"foo"` // want `gorm:"column:foo" json:"foo,omitempty" xml:"foo" yaml:"foo" zip:"foo"`
FooBar struct{} `gorm:"column:fooBar" zip:"fooBar" json:"fooBar,omitempty" xml:"fooBar" yaml:"fooBar"` // want `gorm:"column:fooBar" zip:"fooBar" json:"fooBar,omitempty" xml:"fooBar" yaml:"fooBar"`
FooFoo struct {
Foo int `json:"foo" yaml:"foo"` // want `json:"foo" yaml:"foo"`
Bar int `yaml:"bar" json:"bar"` // want `yaml:"bar" json:"bar"`
BarBar string `json:"barBar" yaml:"barBar"`
} `xml:"fooFoo" json:"fooFoo"`
NoTag struct{}
BarBar struct{} `json:"barBar,omitempty" gorm:"column:barBar" yaml:"barBar" xml:"barBar" zip:"barBar"`
Boo struct{} `gorm:"column:boo" json:"boo,omitempty" xml:"boo" yaml:"boo" zip:"boo"` // want `gorm:"column:boo" json:"boo,omitempty" xml:"boo" yaml:"boo" zip:"boo"`
}
type TagAlignExampleAlignOnlyOK struct {
Foo time.Time `gorm:"column:foo" json:"foo,omitempty" xml:"foo" yaml:"foo" zip:"foo"`
FooBar struct{} `gorm:"column:fooBar" zip:"fooBar" json:"fooBar,omitempty" xml:"fooBar" yaml:"fooBar"`
FooFoo struct {
Foo int `json:"foo" yaml:"foo"`
Bar int `yaml:"bar" json:"bar"`
BarBar string `json:"barBar" yaml:"barBar"`
} `xml:"fooFoo" json:"fooFoo"`
NoTag struct{}
BarBar struct{} `json:"barBar,omitempty" gorm:"column:barBar" yaml:"barBar" xml:"barBar" zip:"barBar"`
Boo struct{} `gorm:"column:boo" json:"boo,omitempty" xml:"boo" yaml:"boo" zip:"boo"`
}

15
test/testdata/tagalign_order_only.go vendored Normal file
View File

@ -0,0 +1,15 @@
//golangcitest:args -Etagalign
//golangcitest:config_path testdata/configs/tagalign_order_only.yml
package testdata
import "time"
type TagAlignExampleOrderOnlyKO struct {
Foo time.Time `xml:"foo" json:"foo,omitempty" yaml:"foo" zip:"foo" gorm:"column:foo" validate:"required"` // want `xml:"foo" json:"foo,omitempty" yaml:"foo" gorm:"column:foo" validate:"required" zip:"foo"`
FooBar struct{} `gorm:"column:fooBar" validate:"required" zip:"fooBar" xml:"fooBar" json:"fooBar,omitempty" yaml:"fooBar"` // want `xml:"fooBar" json:"fooBar,omitempty" yaml:"fooBar" gorm:"column:fooBar" validate:"required" zip:"fooBar"`
}
type TagAlignExampleOrderOnlyOK struct {
Foo time.Time `xml:"foo" json:"foo,omitempty" yaml:"foo" gorm:"column:foo" validate:"required" zip:"foo"`
FooBar struct{} `xml:"fooBar" json:"fooBar,omitempty" yaml:"fooBar" gorm:"column:fooBar" validate:"required" zip:"fooBar"`
}

15
test/testdata/tagalign_sort_only.go vendored Normal file
View File

@ -0,0 +1,15 @@
//golangcitest:args -Etagalign
//golangcitest:config_path testdata/configs/tagalign_sort_only.yml
package testdata
import "time"
type TagAlignExampleSortOnlyKO struct {
Foo time.Time `xml:"foo" json:"foo,omitempty" yaml:"foo" gorm:"column:foo" validate:"required" zip:"foo"` // want `gorm:"column:foo" json:"foo,omitempty" validate:"required" xml:"foo" yaml:"foo" zip:"foo"`
FooBar struct{} `gorm:"column:fooBar" validate:"required" zip:"fooBar" xml:"fooBar" json:"fooBar,omitempty" yaml:"fooBar"` // want `gorm:"column:fooBar" json:"fooBar,omitempty" validate:"required" xml:"fooBar" yaml:"fooBar" zip:"fooBar"`
}
type TagAlignExampleSortOnlyOK struct {
Foo time.Time `gorm:"column:foo" json:"foo,omitempty" validate:"required" xml:"foo" yaml:"foo" zip:"foo"`
FooBar struct{} `gorm:"column:fooBar" json:"fooBar,omitempty" validate:"required" xml:"fooBar" yaml:"fooBar" zip:"fooBar"`
}