diff --git a/.golangci.reference.yml b/.golangci.reference.yml index fda48cceec85..bc30cd626754 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -1209,6 +1209,17 @@ linters-settings: ignore-words: - someword + musttag: + # A set of custom functions to check in addition to the builtin ones. + # Default: json, xml, gopkg.in/yaml.v3, BurntSushi/toml, mitchellh/mapstructure + functions: + # The full name of the function, including the package. + - name: github.com/jmoiron/sqlx.Get + # The struct tag whose presence should be ensured. + tag: db + # The position of the argument to check. + arg-pos: 1 + nakedret: # Make an issue if func has more lines of code than this setting, and it has naked returns. # Default: 30 @@ -2037,6 +2048,7 @@ linters: - makezero - maligned - misspell + - musttag - nakedret - nestif - nilerr @@ -2145,6 +2157,7 @@ linters: - makezero - maligned - misspell + - musttag - nakedret - nestif - nilerr diff --git a/go.mod b/go.mod index 47dd406f7793..4848f9bc9ae8 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/jingyugao/rowserrcheck v1.1.1 github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af github.com/julz/importas v0.1.0 + github.com/junk1tm/musttag v0.4.1 github.com/kisielk/errcheck v1.6.3 github.com/kkHAIKE/contextcheck v1.1.3 github.com/kulti/thelper v0.6.3 diff --git a/go.sum b/go.sum index 6ee4c629f656..76332121c446 100644 --- a/go.sum +++ b/go.sum @@ -308,6 +308,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/junk1tm/musttag v0.4.1 h1:RepN7vE8xObm/AC3WiynRcpFBt8paQR7ztgi09xhTsU= +github.com/junk1tm/musttag v0.4.1/go.mod h1:XkcL/9O6RmD88JBXb+I15nYRl9W4ExhgQeCBEhfMC8U= github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index dfc0dc8b550d..76a16b87d610 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -179,6 +179,7 @@ type LintersSettings struct { Makezero MakezeroSettings Maligned MalignedSettings Misspell MisspellSettings + MustTag MustTagSettings Nakedret NakedretSettings Nestif NestifSettings NilNil NilNilSettings @@ -538,6 +539,14 @@ type MisspellSettings struct { IgnoreWords []string `mapstructure:"ignore-words"` } +type MustTagSettings struct { + Functions []struct { + Name string `mapstructure:"name"` + Tag string `mapstructure:"tag"` + ArgPos int `mapstructure:"arg-pos"` + } `mapstructure:"functions"` +} + type NakedretSettings struct { MaxFuncLines int `mapstructure:"max-func-lines"` } diff --git a/pkg/golinters/musttag.go b/pkg/golinters/musttag.go new file mode 100644 index 000000000000..75500b5aba44 --- /dev/null +++ b/pkg/golinters/musttag.go @@ -0,0 +1,29 @@ +package golinters + +import ( + "github.com/junk1tm/musttag" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewMustTag(setting *config.MustTagSettings) *goanalysis.Linter { + var funcs []musttag.Func + + if setting != nil { + for _, fn := range setting.Functions { + funcs = append(funcs, musttag.Func{ + Name: fn.Name, + Tag: fn.Tag, + ArgPos: fn.ArgPos, + }) + } + } + + a := musttag.New(funcs...) + + return goanalysis. + NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil). + WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index ab8f1835bd41..6ea531b8f817 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -147,6 +147,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { makezeroCfg *config.MakezeroSettings malignedCfg *config.MalignedSettings misspellCfg *config.MisspellSettings + musttagCfg *config.MustTagSettings nakedretCfg *config.NakedretSettings nestifCfg *config.NestifSettings nilNilCfg *config.NilNilSettings @@ -224,6 +225,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { makezeroCfg = &m.cfg.LintersSettings.Makezero malignedCfg = &m.cfg.LintersSettings.Maligned misspellCfg = &m.cfg.LintersSettings.Misspell + musttagCfg = &m.cfg.LintersSettings.MustTag nakedretCfg = &m.cfg.LintersSettings.Nakedret nestifCfg = &m.cfg.LintersSettings.Nestif nilNilCfg = &m.cfg.LintersSettings.NilNil @@ -632,6 +634,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithAutoFix(). WithURL("https://github.com/client9/misspell"), + linter.NewConfig(golinters.NewMustTag(musttagCfg)). + WithSince("v1.51.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle, linter.PresetBugs). + WithURL("https://github.com/junk1tm/musttag"), + linter.NewConfig(golinters.NewNakedret(nakedretCfg)). WithSince("v1.19.0"). WithPresets(linter.PresetStyle). diff --git a/test/testdata/configs/musttag.yml b/test/testdata/configs/musttag.yml new file mode 100644 index 000000000000..399e236bb57b --- /dev/null +++ b/test/testdata/configs/musttag.yml @@ -0,0 +1,9 @@ +linters-settings: + musttag: + functions: + - name: encoding/asn1.Marshal + tag: asn1 + arg-pos: 0 + - name: encoding/asn1.Unmarshal + tag: asn1 + arg-pos: 1 diff --git a/test/testdata/musttag.go b/test/testdata/musttag.go new file mode 100644 index 000000000000..daf84298fee0 --- /dev/null +++ b/test/testdata/musttag.go @@ -0,0 +1,27 @@ +//golangcitest:args -Emusttag +package testdata + +import ( + "encoding/asn1" + "encoding/json" +) + +// builtin functions: +func musttagJSON() { + var user struct { // want `exported fields should be annotated with the "json" tag` + Name string + Email string `json:"email"` + } + json.Marshal(user) + json.Unmarshal(nil, &user) +} + +// custom functions from config: +func musttagASN1() { + var user struct { + Name string + Email string `asn1:"email"` + } + asn1.Marshal(user) + asn1.Unmarshal(nil, &user) +} diff --git a/test/testdata/musttag_custom.go b/test/testdata/musttag_custom.go new file mode 100644 index 000000000000..42bdea3fb511 --- /dev/null +++ b/test/testdata/musttag_custom.go @@ -0,0 +1,28 @@ +//golangcitest:args -Emusttag +//golangcitest:config_path testdata/configs/musttag.yml +package testdata + +import ( + "encoding/asn1" + "encoding/json" +) + +// builtin functions: +func musttagJSONCustom() { + var user struct { // want `exported fields should be annotated with the "json" tag` + Name string + Email string `json:"email"` + } + json.Marshal(user) + json.Unmarshal(nil, &user) +} + +// custom functions from config: +func musttagASN1Custom() { + var user struct { // want `exported fields should be annotated with the "asn1" tag` + Name string + Email string `asn1:"email"` + } + asn1.Marshal(user) + asn1.Unmarshal(nil, &user) +}