github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/src/cmd/vet/structtag.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file contains the test for canonical struct tags. 6 7 package main 8 9 import ( 10 "errors" 11 "go/ast" 12 "reflect" 13 "strconv" 14 ) 15 16 func init() { 17 register("structtags", 18 "check that struct field tags have canonical format and apply to exported fields as needed", 19 checkCanonicalFieldTag, 20 field) 21 } 22 23 // checkCanonicalFieldTag checks a struct field tag. 24 func checkCanonicalFieldTag(f *File, node ast.Node) { 25 field := node.(*ast.Field) 26 if field.Tag == nil { 27 return 28 } 29 30 tag, err := strconv.Unquote(field.Tag.Value) 31 if err != nil { 32 f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) 33 return 34 } 35 36 if err := validateStructTag(tag); err != nil { 37 f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get: %s", field.Tag.Value, err) 38 } 39 40 // Check for use of json or xml tags with unexported fields. 41 42 // Embedded struct. Nothing to do for now, but that 43 // may change, depending on what happens with issue 7363. 44 if len(field.Names) == 0 { 45 return 46 } 47 48 if field.Names[0].IsExported() { 49 return 50 } 51 52 st := reflect.StructTag(tag) 53 for _, enc := range [...]string{"json", "xml"} { 54 if st.Get(enc) != "" { 55 f.Badf(field.Pos(), "struct field %s has %s tag but is not exported", field.Names[0].Name, enc) 56 return 57 } 58 } 59 } 60 61 var ( 62 errTagSyntax = errors.New("bad syntax for struct tag pair") 63 errTagKeySyntax = errors.New("bad syntax for struct tag key") 64 errTagValueSyntax = errors.New("bad syntax for struct tag value") 65 ) 66 67 // validateStructTag parses the struct tag and returns an error if it is not 68 // in the canonical format, which is a space-separated list of key:"value" 69 // settings. The value may contain spaces. 70 func validateStructTag(tag string) error { 71 // This code is based on the StructTag.Get code in package reflect. 72 73 for tag != "" { 74 // Skip leading space. 75 i := 0 76 for i < len(tag) && tag[i] == ' ' { 77 i++ 78 } 79 tag = tag[i:] 80 if tag == "" { 81 break 82 } 83 84 // Scan to colon. A space, a quote or a control character is a syntax error. 85 // Strictly speaking, control chars include the range [0x7f, 0x9f], not just 86 // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters 87 // as it is simpler to inspect the tag's bytes than the tag's runes. 88 i = 0 89 for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { 90 i++ 91 } 92 if i == 0 { 93 return errTagKeySyntax 94 } 95 if i+1 >= len(tag) || tag[i] != ':' { 96 return errTagSyntax 97 } 98 if tag[i+1] != '"' { 99 return errTagValueSyntax 100 } 101 tag = tag[i+1:] 102 103 // Scan quoted string to find value. 104 i = 1 105 for i < len(tag) && tag[i] != '"' { 106 if tag[i] == '\\' { 107 i++ 108 } 109 i++ 110 } 111 if i >= len(tag) { 112 return errTagValueSyntax 113 } 114 qvalue := string(tag[:i+1]) 115 tag = tag[i+1:] 116 117 if _, err := strconv.Unquote(qvalue); err != nil { 118 return errTagValueSyntax 119 } 120 } 121 return nil 122 }