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 == "" {
 | 
				
			||||||
 | 
								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
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if i+1 >= len(tag) || tag[i] != ':' {
 | 
				
			||||||
			return errTagSyntax
 | 
								return errTagSyntax
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		key, value := fields[0], fields[1]
 | 
							if tag[i+1] != '"' {
 | 
				
			||||||
		if len(key) == 0 || len(value) < 2 {
 | 
					 | 
				
			||||||
			return errTagSyntax
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Key must not contain control characters or quotes.
 | 
					 | 
				
			||||||
		for _, r := range key {
 | 
					 | 
				
			||||||
			if r == '"' || unicode.IsControl(r) {
 | 
					 | 
				
			||||||
				return errTagKeySyntax
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if value[0] != '"' || value[len(value)-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