From f593b74c24b562ada0f2690300b0e4e9bbe2dcab Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Sun, 11 Apr 2021 23:41:43 +0200 Subject: [PATCH] Add tagliatelle linter --- .golangci.example.yml | 14 ++++++++++++++ go.mod | 1 + go.sum | 4 ++++ pkg/config/config.go | 8 ++++++++ pkg/golinters/tagliatelle.go | 30 ++++++++++++++++++++++++++++++ pkg/lint/lintersdb/manager.go | 7 +++++++ test/testdata/tagliatelle.go | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 97 insertions(+) create mode 100644 pkg/golinters/tagliatelle.go create mode 100644 test/testdata/tagliatelle.go diff --git a/.golangci.example.yml b/.golangci.example.yml index f7485b088543..fe14fd094612 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -478,6 +478,20 @@ linters-settings: retract-allow-no-explanation: false # Forbid the use of the `exclude` directives. Default is false. exclude-forbidden: false + tagliatelle: + # check the struck tag name case + case: + # use the struct field name to check the name of the struct tag + use-field-name: true + rules: + # any struct tag type can be used. + # support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower` + json: camel + yaml: camel + xml: camel + bson: camel + avro: snake + mapstructure: kebab # The custom section can be used to define linter plugins to be loaded at runtime. See README doc # for more info. diff --git a/go.mod b/go.mod index 871919f15b5e..9b19ae3d2053 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/kunwardeep/paralleltest v1.0.2 github.com/kyoh86/exportloopref v0.1.8 github.com/ldez/gomoddirectives v0.2.1 + github.com/ldez/tagliatelle v0.2.0 github.com/maratori/testpackage v1.0.1 github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // v1.0 github.com/mattn/go-colorable v0.1.8 diff --git a/go.sum b/go.sum index e0b73ffc9c19..3be80d32ee17 100644 --- a/go.sum +++ b/go.sum @@ -132,6 +132,8 @@ github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.2 h1:K5s1W2fGfkoWXsFlxBNqT6J0ZCncPaKrGM5qe0bni68= github.com/esimonov/ifshort v1.0.2/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= +github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= +github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= @@ -378,6 +380,8 @@ github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77 github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/ldez/gomoddirectives v0.2.1 h1:9pAcW9KRZW7HQjFwbozNvFMcNVwdCBufU7os5QUwLIY= github.com/ldez/gomoddirectives v0.2.1/go.mod h1:sGicqkRgBOg//JfpXwkB9Hj0X5RyJ7mlACM5B9f6Me4= +github.com/ldez/tagliatelle v0.2.0 h1:693V8Bf1NdShJ8eu/s84QySA0J2VWBanVBa2WwXD/Wk= +github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= diff --git a/pkg/config/config.go b/pkg/config/config.go index 44d1ac431025..f2091c6d1b56 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -278,6 +278,7 @@ type LintersSettings struct { ImportAs ImportAsSettings GoModDirectives GoModDirectivesSettings Promlinter PromlinterSettings + Tagliatelle TagliatelleSettings Custom map[string]CustomLinterSettings } @@ -478,6 +479,13 @@ type GoModDirectivesSettings struct { RetractAllowNoExplanation bool `mapstructure:"retract-allow-no-explanation"` } +type TagliatelleSettings struct { + Case struct { + Rules map[string]string + UseFieldName bool `mapstructure:"use-field-name"` + } +} + var defaultLintersSettings = LintersSettings{ Lll: LllSettings{ LineLength: 120, diff --git a/pkg/golinters/tagliatelle.go b/pkg/golinters/tagliatelle.go new file mode 100644 index 000000000000..5f58fc1d3ba9 --- /dev/null +++ b/pkg/golinters/tagliatelle.go @@ -0,0 +1,30 @@ +package golinters + +import ( + "github.com/ldez/tagliatelle" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewTagliatelle(settings *config.TagliatelleSettings) *goanalysis.Linter { + cfg := tagliatelle.Config{ + Rules: map[string]string{ + "json": "camel", + "yaml": "camel", + }, + } + + if settings != nil { + for k, v := range settings.Case.Rules { + cfg.Rules[k] = v + } + cfg.UseFieldName = settings.Case.UseFieldName + } + + a := tagliatelle.New(cfg) + + return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil). + WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 1e62f2786925..ab9716e5e5e1 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -112,6 +112,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var cyclopCfg *config.Cyclop var importAsCfg *config.ImportAsSettings var goModDirectivesCfg *config.GoModDirectivesSettings + var tagliatelleCfg *config.TagliatelleSettings if m.cfg != nil { govetCfg = &m.cfg.LintersSettings.Govet @@ -126,6 +127,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { cyclopCfg = &m.cfg.LintersSettings.Cyclop importAsCfg = &m.cfg.LintersSettings.ImportAs goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives + tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle } const megacheckName = "megacheck" @@ -483,6 +485,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("v1.40.0"). WithPresets(linter.PresetStyle). WithURL("https://github.com/yeya24/promlinter"), + linter.NewConfig(golinters.NewTagliatelle(tagliatelleCfg)). + WithSince("v1.40.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/ldez/tagliatelle"), + // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives linter.NewConfig(golinters.NewNoLintLint()). WithSince("v1.26.0"). diff --git a/test/testdata/tagliatelle.go b/test/testdata/tagliatelle.go new file mode 100644 index 000000000000..7ecb9f4a48a7 --- /dev/null +++ b/test/testdata/tagliatelle.go @@ -0,0 +1,33 @@ +//args: -Etagliatelle +package testdata + +import "time" + +type TglFoo struct { + ID string `json:"ID"` // ERROR `json\(camel\): got 'ID' want 'id'` + UserID string `json:"UserID"` // ERROR `json\(camel\): got 'UserID' want 'userId'` + Name string `json:"name"` + Value time.Duration `json:"value,omitempty"` + Bar TglBar `json:"bar"` + Bur `json:"bur"` +} + +type TglBar struct { + Name string `json:"-"` + Value string `json:"value"` + CommonServiceFooItem *TglBir `json:"CommonServiceItem,omitempty"` // ERROR `json\(camel\): got 'CommonServiceItem' want 'commonServiceItem'` +} + +type TglBir struct { + Name string `json:"-"` + Value string `json:"value"` + ReplaceAllowList []string `mapstructure:"replace-allow-list"` +} + +type Bur struct { + Name string + Value string `yaml:"Value"` // ERROR `yaml\(camel\): got 'Value' want 'value'` + More string `json:"-"` + Also string `json:",omitempty"` + ReqPerS string `avro:"req_per_s"` +}