aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNigel Tao <nigeltao@golang.org>2015-02-05 15:42:17 +1100
committerNigel Tao <nigeltao@golang.org>2015-02-06 02:26:15 +0000
commit9622599500adce60e13ea72b2ac73fd516d57493 (patch)
treeb0ae72fa10cf5dec8c039a52637870158bbc46da
parent913f41fc5f1b7afd8f7c66c011c1ad368fa23fb8 (diff)
downloadtools-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.go60
-rw-r--r--cmd/vet/testdata/structtag.go6
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 {