diff options
author | Nigel Tao <nigeltao@golang.org> | 2015-02-05 15:42:17 +1100 |
---|---|---|
committer | Nigel Tao <nigeltao@golang.org> | 2015-02-06 02:26:15 +0000 |
commit | 9622599500adce60e13ea72b2ac73fd516d57493 (patch) | |
tree | b0ae72fa10cf5dec8c039a52637870158bbc46da | |
parent | 913f41fc5f1b7afd8f7c66c011c1ad368fa23fb8 (diff) | |
download | tools-9622599500adce60e13ea72b2ac73fd516d57493.tar.gz |
cmd/vet: allow spaces in struct tag values.
The validateStructTag code now closely mimics the StructTag.Get code in
package reflect.
This addresses the comment raised on issue #9500:
https://github.com/golang/go/issues/9500#issuecomment-70218780
See also https://go-review.googlesource.com/3953
Change-Id: I583f7447dbc5a2d7ecbb393d9bb6b1515cb10b18
Reviewed-on: https://go-review.googlesource.com/3952
Reviewed-by: Rob Pike <r@golang.org>
-rw-r--r-- | cmd/vet/structtag.go | 60 | ||||
-rw-r--r-- | cmd/vet/testdata/structtag.go | 6 |
2 files changed, 46 insertions, 20 deletions
diff --git a/cmd/vet/structtag.go b/cmd/vet/structtag.go index a2b7d30..e8164a4 100644 --- a/cmd/vet/structtag.go +++ b/cmd/vet/structtag.go @@ -11,8 +11,6 @@ import ( "go/ast" "reflect" "strconv" - "strings" - "unicode" ) func init() { @@ -68,33 +66,55 @@ var ( // validateStructTag parses the struct tag and returns an error if it is not // in the canonical format, which is a space-separated list of key:"value" -// settings. +// settings. The value may contain spaces. func validateStructTag(tag string) error { - elems := strings.Split(tag, " ") - for _, elem := range elems { - if elem == "" { - continue + // This code is based on the StructTag.Get code in package reflect. + + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ } - fields := strings.SplitN(elem, ":", 2) - if len(fields) != 2 { - return errTagSyntax + tag = tag[i:] + if tag == "" { + break + } + + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + if i == 0 { + return errTagKeySyntax } - key, value := fields[0], fields[1] - if len(key) == 0 || len(value) < 2 { + if i+1 >= len(tag) || tag[i] != ':' { return errTagSyntax } - // Key must not contain control characters or quotes. - for _, r := range key { - if r == '"' || unicode.IsControl(r) { - return errTagKeySyntax + if tag[i+1] != '"' { + return errTagValueSyntax + } + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ } + i++ } - if value[0] != '"' || value[len(value)-1] != '"' { + if i >= len(tag) { return errTagValueSyntax } - // Value must be quoted string - _, err := strconv.Unquote(value) - if err != nil { + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if _, err := strconv.Unquote(qvalue); err != nil { return errTagValueSyntax } } diff --git a/cmd/vet/testdata/structtag.go b/cmd/vet/testdata/structtag.go index 1e1ebf8..6878f56 100644 --- a/cmd/vet/testdata/structtag.go +++ b/cmd/vet/testdata/structtag.go @@ -11,8 +11,14 @@ type StructTagTest struct { B int "\tx:\"y\"" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key" C int "x:\"y\"\tx:\"y\"" // ERROR "not compatible with reflect.StructTag.Get" D int "x:`y`" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value" + E int "ct\brl:\"char\"" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag pair" + F int `:"emptykey"` // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key" + G int `x:"noEndQuote` // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value" + H int `x:"trunc\x0"` // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value" OK0 int `x:"y" u:"v" w:""` OK1 int `x:"y:z" u:"v" w:""` // note multiple colons. + OK2 int "k0:\"values contain spaces\" k1:\"literal\ttabs\" k2:\"and\\tescaped\\tabs\"" + OK3 int `under_scores:"and" CAPS:"ARE_OK"` } type UnexportedEncodingTagTest struct { |