From 6fa4622fc72506afdf2b7e2c63b6acc627156062 Mon Sep 17 00:00:00 2001 From: chavacava Date: Sat, 15 Feb 2025 11:47:02 +0100 Subject: [PATCH] feature: add suport of mapstructure struct tags in struct-tag rule --- rule/struct_tag.go | 41 ++++++++++++++++++++++------- test/struct_tag_test.go | 1 + testdata/struct_tag.go | 5 ++++ testdata/struct_tag_user_options.go | 5 ++++ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/rule/struct_tag.go b/rule/struct_tag.go index 216ed8061..520709dc6 100644 --- a/rule/struct_tag.go +++ b/rule/struct_tag.go @@ -98,16 +98,17 @@ func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { } const ( - keyASN1 = "asn1" - keyBSON = "bson" - keyDatastore = "datastore" - keyDefault = "default" - keyJSON = "json" - keyProtobuf = "protobuf" - keyRequired = "required" - keyURL = "url" - keyXML = "xml" - keyYAML = "yaml" + keyASN1 = "asn1" + keyBSON = "bson" + keyDatastore = "datastore" + keyDefault = "default" + keyJSON = "json" + keyMapstructure = "mapstructure" + keyProtobuf = "protobuf" + keyRequired = "required" + keyURL = "url" + keyXML = "xml" + keyYAML = "yaml" ) func (w lintStructTagRule) checkTagNameIfNeed(tag *structtag.Tag) (string, bool) { @@ -196,6 +197,11 @@ func (w lintStructTagRule) checkTaggedField(f *ast.Field) { if !ok { w.addFailure(f.Tag, msg) } + case keyMapstructure: + msg, ok := w.checkMapstructureTag(tag.Options) + if !ok { + w.addFailure(f.Tag, msg) + } case keyProtobuf: msg, ok := w.checkProtobufTag(tag) if !ok { @@ -379,6 +385,21 @@ func (w lintStructTagRule) checkDatastoreTag(options []string) (string, bool) { return "", true } +func (w lintStructTagRule) checkMapstructureTag(options []string) (string, bool) { + for _, opt := range options { + switch opt { + case "omitempty", "reminder", "squash": + default: + if w.isUserDefined(keyMapstructure, opt) { + continue + } + return fmt.Sprintf("unknown option '%s' in Mapstructure tag", opt), false + } + } + + return "", true +} + func (lintStructTagRule) typeValueMatch(t ast.Expr, val string) bool { tID, ok := t.(*ast.Ident) if !ok { diff --git a/test/struct_tag_test.go b/test/struct_tag_test.go index 3c4eab115..5386f0e40 100644 --- a/test/struct_tag_test.go +++ b/test/struct_tag_test.go @@ -18,6 +18,7 @@ func TestStructTagWithUserOptions(t *testing.T) { "bson,gnu", "url,myURLOption", "datastore,myDatastoreOption", + "mapstructure,myMapstructureOption", }, }) } diff --git a/testdata/struct_tag.go b/testdata/struct_tag.go index ec5e42e53..3d4ec6b04 100644 --- a/testdata/struct_tag.go +++ b/testdata/struct_tag.go @@ -141,3 +141,8 @@ type Fields struct { Field string `datastore:",noindex,flatten,omitempty"` OtherField string `datastore:",unknownOption"` // MATCH /unknown option 'unknownOption' in Datastore tag/ } + +type MapStruct struct { + Field1 string `mapstructure:",squash,reminder,omitempty"` + OtherField string `mapstructure:",unknownOption"` // MATCH /unknown option 'unknownOption' in Mapstructure tag/ +} diff --git a/testdata/struct_tag_user_options.go b/testdata/struct_tag_user_options.go index bbdabc397..cd07373b5 100644 --- a/testdata/struct_tag_user_options.go +++ b/testdata/struct_tag_user_options.go @@ -24,3 +24,8 @@ type Fields struct { Field string `datastore:",noindex,flatten,omitempty,myDatastoreOption"` OtherField string `datastore:",unknownOption"` // MATCH /unknown option 'unknownOption' in Datastore tag/ } + +type MapStruct struct { + Field1 string `mapstructure:",squash,reminder,omitempty,myMapstructureOption"` + OtherField string `mapstructure:",unknownOption"` // MATCH /unknown option 'unknownOption' in Mapstructure tag/ +}