From 8ea88db68f73999b430b166ee6ca49b58e1c14bd Mon Sep 17 00:00:00 2001 From: SalvadorC Date: Tue, 7 Aug 2018 20:28:45 +0200 Subject: [PATCH] struct-tag: add support for asn1 and bson (#49) --- fixtures/struct-tag.go | 18 +++++++++++- rule/struct-tag.go | 67 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/fixtures/struct-tag.go b/fixtures/struct-tag.go index ca7891e..86f2447 100644 --- a/fixtures/struct-tag.go +++ b/fixtures/struct-tag.go @@ -26,6 +26,22 @@ type RangeAllocation struct { Data []byte `json:"data,inline"` // MATCH /unknown option 'inline' in JSON tag/ } +type RangeAllocation struct { + metav1.TypeMeta `bson:",minsize"` + metav1.ObjectMeta `bson:"metadata,omitempty"` + Range string `bson:"range,flow"` // MATCH /unknown option 'flow' in BSON tag/ + Data []byte `bson:"data,inline"` +} + +type TestContextSpecificTags2 struct { + A int `asn1:"explicit,tag:1"` + B int `asn1:"tag:2"` + S string `asn1:"tag:0,utf8"` + Ints []int `asn1:"set"` + Version int `asn1:"optional,explicit,default:0,tag:0"` // MATCH /duplicated tag number 0/ + Time time.Time `asn1:"explicit,tag:4,other"` // MATCH /unknown option 'other' in ASN1 tag/ +} + type VirtualMachineRelocateSpecDiskLocator struct { DynamicData @@ -33,5 +49,5 @@ type VirtualMachineRelocateSpecDiskLocator struct { Datastore ManagedObjectReference `xml:"datastore,chardata,innerxml"` DiskMoveType string `xml:"diskMoveType,omitempty,comment"` DiskBackingInfo BaseVirtualDeviceBackingInfo `xml:"diskBackingInfo,omitempty,any"` - Profile []BaseVirtualMachineProfileSpec `xml:"profile,omitempty,typeattr"` // MATCH /unknown option 'typeattr' in XML tag/ + Profile []BaseVirtualMachineProfileSpec `xml:"profile,omitempty,other"` // MATCH /unknown option 'other' in XML tag/ } diff --git a/rule/struct-tag.go b/rule/struct-tag.go index 9e35c05..2ed1410 100644 --- a/rule/struct-tag.go +++ b/rule/struct-tag.go @@ -33,7 +33,8 @@ func (r *StructTagRule) Name() string { } type lintStructTagRule struct { - onFailure func(lint.Failure) + onFailure func(lint.Failure) + usedTagNbr map[string]bool // list of used tag numbers } func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { @@ -42,7 +43,7 @@ func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { if n.Fields == nil || n.Fields.NumFields() < 1 { return nil // skip empty structs } - + w.usedTagNbr = map[string]bool{} // init for _, f := range n.Fields.List { if f.Tag != nil { w.checkTaggedField(f) @@ -59,13 +60,22 @@ func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { func (w lintStructTagRule) checkTaggedField(f *ast.Field) { tags, err := structtag.Parse(strings.Trim(f.Tag.Value, "`")) if err != nil || tags == nil { + w.addFailure(f.Tag, "malformed tag") return } for _, tag := range tags.Tags() { switch key := tag.Key; key { case "asn1": - // Not implemented yet + msg, ok := w.checkASN1Tag(f.Type, tag) + if !ok { + w.addFailure(f.Tag, msg) + } + case "bson": + msg, ok := w.checkBSONTag(tag.Options) + if !ok { + w.addFailure(f.Tag, msg) + } case "default": if !w.typeValueMatch(f.Type, tag.Name) { w.addFailure(f.Tag, "field's type and default value's type mismatch") @@ -97,6 +107,55 @@ func (w lintStructTagRule) checkTaggedField(f *ast.Field) { } } +func (w lintStructTagRule) checkASN1Tag(t ast.Expr, tag *structtag.Tag) (string, bool) { + checkList := append(tag.Options, tag.Name) + for _, opt := range checkList { + switch opt { + case "application", "explicit", "generalized", "ia5", "omitempty", "optional", "set", "utf8": + + default: + if strings.HasPrefix(opt, "tag:") { + parts := strings.Split(opt, ":") + tagNumber := parts[1] + if w.usedTagNbr[tagNumber] { + return fmt.Sprintf("duplicated tag number %s", tagNumber), false + } + w.usedTagNbr[tagNumber] = true + + continue + } + + if strings.HasPrefix(opt, "default:") { + parts := strings.Split(opt, ":") + if len(parts) < 2 { + return "malformed default for ASN1 tag", false + } + if !w.typeValueMatch(t, parts[1]) { + return "field's type and default value's type mismatch", false + } + + continue + } + + return fmt.Sprintf("unknown option '%s' in ASN1 tag", opt), false + } + } + + return "", true +} + +func (w lintStructTagRule) checkBSONTag(options []string) (string, bool) { + for _, opt := range options { + switch opt { + case "inline", "minsize", "omitempty": + default: + return fmt.Sprintf("unknown option '%s' in BSON tag", opt), false + } + } + + return "", true +} + func (w lintStructTagRule) checkJSONTag(options []string) (string, bool) { for _, opt := range options { switch opt { @@ -112,7 +171,7 @@ func (w lintStructTagRule) checkJSONTag(options []string) (string, bool) { func (w lintStructTagRule) checkXMLTag(options []string) (string, bool) { for _, opt := range options { switch opt { - case "attr", "cdata", "chardata", "innerxml", "comment", "any", "omitempty": + case "any", "attr", "cdata", "chardata", "comment", "innerxml", "omitempty", "typeattr": default: return fmt.Sprintf("unknown option '%s' in XML tag", opt), false }