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>
This commit is contained in:
parent
913f41fc5f
commit
9622599500
|
@ -11,8 +11,6 @@ import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -68,33 +66,55 @@ var (
|
||||||
|
|
||||||
// validateStructTag parses the struct tag and returns an error if it is not
|
// 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"
|
// 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 {
|
func validateStructTag(tag string) error {
|
||||||
elems := strings.Split(tag, " ")
|
// This code is based on the StructTag.Get code in package reflect.
|
||||||
for _, elem := range elems {
|
|
||||||
if elem == "" {
|
for tag != "" {
|
||||||
continue
|
// Skip leading space.
|
||||||
|
i := 0
|
||||||
|
for i < len(tag) && tag[i] == ' ' {
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
fields := strings.SplitN(elem, ":", 2)
|
tag = tag[i:]
|
||||||
if len(fields) != 2 {
|
if tag == "" {
|
||||||
return errTagSyntax
|
break
|
||||||
}
|
}
|
||||||
key, value := fields[0], fields[1]
|
|
||||||
if len(key) == 0 || len(value) < 2 {
|
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||||
return errTagSyntax
|
// 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++
|
||||||
}
|
}
|
||||||
// Key must not contain control characters or quotes.
|
if i == 0 {
|
||||||
for _, r := range key {
|
|
||||||
if r == '"' || unicode.IsControl(r) {
|
|
||||||
return errTagKeySyntax
|
return errTagKeySyntax
|
||||||
}
|
}
|
||||||
|
if i+1 >= len(tag) || tag[i] != ':' {
|
||||||
|
return errTagSyntax
|
||||||
}
|
}
|
||||||
if value[0] != '"' || value[len(value)-1] != '"' {
|
if tag[i+1] != '"' {
|
||||||
return errTagValueSyntax
|
return errTagValueSyntax
|
||||||
}
|
}
|
||||||
// Value must be quoted string
|
tag = tag[i+1:]
|
||||||
_, err := strconv.Unquote(value)
|
|
||||||
if err != nil {
|
// Scan quoted string to find value.
|
||||||
|
i = 1
|
||||||
|
for i < len(tag) && tag[i] != '"' {
|
||||||
|
if tag[i] == '\\' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(tag) {
|
||||||
|
return errTagValueSyntax
|
||||||
|
}
|
||||||
|
qvalue := string(tag[:i+1])
|
||||||
|
tag = tag[i+1:]
|
||||||
|
|
||||||
|
if _, err := strconv.Unquote(qvalue); err != nil {
|
||||||
return errTagValueSyntax
|
return errTagValueSyntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,14 @@ type StructTagTest struct {
|
||||||
B int "\tx:\"y\"" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key"
|
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"
|
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"
|
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:""`
|
OK0 int `x:"y" u:"v" w:""`
|
||||||
OK1 int `x:"y:z" u:"v" w:""` // note multiple colons.
|
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 {
|
type UnexportedEncodingTagTest struct {
|
||||||
|
|
Loading…
Reference in New Issue