Skip to content

Commit 25733a9

Browse files
committedJun 29, 2011
reflect: support for struct tag use by multiple packages
Each package using struct field tags assumes that it is the only package storing data in the tag. This CL adds support in package reflect for sharing tags between multiple packages. In this scheme, the tags must be of the form key:"value" key2:"value2" (raw strings help when writing that tag in Go source). reflect.StructField's Tag field now has type StructTag (a string type), which has method Get(key string) string that returns the associated value. Clients of json and xml will need to be updated. Code that says type T struct { X int "name" } should become type T struct { X int `json:"name"` // or `xml:"name"` } Use govet to identify struct tags that need to be changed to use the new syntax. R=r, r, dsymonds, bradfitz, kevlar, fvbommel, n13m3y3r CC=golang-dev https://golang.org/cl/4645069
1 parent f83609f commit 25733a9

File tree

24 files changed

+293
-184
lines changed

24 files changed

+293
-184
lines changed
 

‎src/cmd/godoc/codewalk.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
7474

7575
// A Codewalk represents a single codewalk read from an XML file.
7676
type Codewalk struct {
77-
Title string "attr"
77+
Title string `xml:"attr"`
7878
File []string
7979
Step []*Codestep
8080
}
@@ -83,9 +83,9 @@ type Codewalk struct {
8383
// A Codestep is a single step in a codewalk.
8484
type Codestep struct {
8585
// Filled in from XML
86-
Src string "attr"
87-
Title string "attr"
88-
XML string "innerxml"
86+
Src string `xml:"attr"`
87+
Title string `xml:"attr"`
88+
XML string `xml:"innerxml"`
8989

9090
// Derived from Src; not in XML.
9191
Err os.Error

‎src/cmd/govet/govet.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"go/token"
1616
"os"
1717
"path/filepath"
18+
"reflect"
1819
"strconv"
1920
"strings"
2021
"utf8"
@@ -59,7 +60,7 @@ func main() {
5960
var err os.Error
6061
skip, err = strconv.Atoi(name[colon+1:])
6162
if err != nil {
62-
error(`illegal format for "Func:N" argument %q; %s`, name, err)
63+
errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
6364
}
6465
name = name[:colon]
6566
}
@@ -93,7 +94,7 @@ func doFile(name string, reader io.Reader) {
9394
fs := token.NewFileSet()
9495
parsedFile, err := parser.ParseFile(fs, name, reader, 0)
9596
if err != nil {
96-
error("%s: %s", name, err)
97+
errorf("%s: %s", name, err)
9798
return
9899
}
99100
file := &File{fs.File(parsedFile.Pos())}
@@ -121,7 +122,7 @@ func walkDir(root string) {
121122
done := make(chan bool)
122123
go func() {
123124
for e := range errors {
124-
error("walk error: %s", e)
125+
errorf("walk error: %s", e)
125126
}
126127
done <- true
127128
}()
@@ -132,7 +133,7 @@ func walkDir(root string) {
132133

133134
// error formats the error to standard error, adding program
134135
// identification and a newline
135-
func error(format string, args ...interface{}) {
136+
func errorf(format string, args ...interface{}) {
136137
fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...)
137138
setExit(2)
138139
}
@@ -185,15 +186,35 @@ func (f *File) checkFile(name string, file *ast.File) {
185186

186187
// Visit implements the ast.Visitor interface.
187188
func (f *File) Visit(node ast.Node) ast.Visitor {
188-
// TODO: could return nil for nodes that cannot contain a CallExpr -
189-
// will shortcut traversal. Worthwhile?
190189
switch n := node.(type) {
191190
case *ast.CallExpr:
192191
f.checkCallExpr(n)
192+
case *ast.Field:
193+
f.checkFieldTag(n)
193194
}
194195
return f
195196
}
196197

198+
// checkField checks a struct field tag.
199+
func (f *File) checkFieldTag(field *ast.Field) {
200+
if field.Tag == nil {
201+
return
202+
}
203+
204+
tag, err := strconv.Unquote(field.Tag.Value)
205+
if err != nil {
206+
f.Warnf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
207+
return
208+
}
209+
210+
// Check tag for validity by appending
211+
// new key:value to end and checking that
212+
// the tag parsing code can find it.
213+
if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" {
214+
f.Warnf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
215+
return
216+
}
217+
}
197218

198219
// checkCallExpr checks a call expression.
199220
func (f *File) checkCallExpr(call *ast.CallExpr) {
@@ -373,6 +394,10 @@ func BadFunctionUsedInTests() {
373394
f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function
374395
}
375396

397+
type BadTypeUsedInTests struct {
398+
X int "hello" // struct field not well-formed
399+
}
400+
376401
// printf is used by the test.
377402
func printf(format string, args ...interface{}) {
378403
panic("don't call - testing only")

‎src/pkg/asn1/asn1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
707707
if i == 0 && field.Type == rawContentsType {
708708
continue
709709
}
710-
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag))
710+
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
711711
if err != nil {
712712
return
713713
}

‎src/pkg/asn1/asn1_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,11 @@ type TestObjectIdentifierStruct struct {
299299
}
300300

301301
type TestContextSpecificTags struct {
302-
A int "tag:1"
302+
A int `asn1:"tag:1"`
303303
}
304304

305305
type TestContextSpecificTags2 struct {
306-
A int "explicit,tag:1"
306+
A int `asn1:"explicit,tag:1"`
307307
B int
308308
}
309309

@@ -353,7 +353,7 @@ type Certificate struct {
353353
}
354354

355355
type TBSCertificate struct {
356-
Version int "optional,explicit,default:0,tag:0"
356+
Version int `asn1:"optional,explicit,default:0,tag:0"`
357357
SerialNumber RawValue
358358
SignatureAlgorithm AlgorithmIdentifier
359359
Issuer RDNSequence

‎src/pkg/asn1/marshal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
413413
for i := startingField; i < t.NumField(); i++ {
414414
var pre *forkableWriter
415415
pre, out = out.fork()
416-
err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag))
416+
err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
417417
if err != nil {
418418
return
419419
}

‎src/pkg/asn1/marshal_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ type rawContentsStruct struct {
3030
}
3131

3232
type implicitTagTest struct {
33-
A int "implicit,tag:5"
33+
A int `asn1:"implicit,tag:5"`
3434
}
3535

3636
type explicitTagTest struct {
37-
A int "explicit,tag:5"
37+
A int `asn1:"explicit,tag:5"`
3838
}
3939

4040
type ia5StringTest struct {
41-
A string "ia5"
41+
A string `asn1:"ia5"`
4242
}
4343

4444
type printableStringTest struct {
45-
A string "printable"
45+
A string `asn1:"printable"`
4646
}
4747

4848
type optionalRawValueTest struct {
49-
A RawValue "optional"
49+
A RawValue `asn1:"optional"`
5050
}
5151

5252
type testSET []int

‎src/pkg/crypto/ocsp/ocsp.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type certID struct {
4343

4444
type responseASN1 struct {
4545
Status asn1.Enumerated
46-
Response responseBytes "explicit,tag:0"
46+
Response responseBytes `asn1:"explicit,tag:0"`
4747
}
4848

4949
type responseBytes struct {
@@ -55,30 +55,30 @@ type basicResponse struct {
5555
TBSResponseData responseData
5656
SignatureAlgorithm pkix.AlgorithmIdentifier
5757
Signature asn1.BitString
58-
Certificates []asn1.RawValue "explicit,tag:0,optional"
58+
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
5959
}
6060

6161
type responseData struct {
6262
Raw asn1.RawContent
63-
Version int "optional,default:1,explicit,tag:0"
64-
RequestorName pkix.RDNSequence "optional,explicit,tag:1"
65-
KeyHash []byte "optional,explicit,tag:2"
63+
Version int `asn1:"optional,default:1,explicit,tag:0"`
64+
RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"`
65+
KeyHash []byte `asn1:"optional,explicit,tag:2"`
6666
ProducedAt *time.Time
6767
Responses []singleResponse
6868
}
6969

7070
type singleResponse struct {
7171
CertID certID
72-
Good asn1.Flag "explicit,tag:0,optional"
73-
Revoked revokedInfo "explicit,tag:1,optional"
74-
Unknown asn1.Flag "explicit,tag:2,optional"
72+
Good asn1.Flag `asn1:"explicit,tag:0,optional"`
73+
Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
74+
Unknown asn1.Flag `asn1:"explicit,tag:2,optional"`
7575
ThisUpdate *time.Time
76-
NextUpdate *time.Time "explicit,tag:0,optional"
76+
NextUpdate *time.Time `asn1:"explicit,tag:0,optional"`
7777
}
7878

7979
type revokedInfo struct {
8080
RevocationTime *time.Time
81-
Reason int "explicit,tag:0,optional"
81+
Reason int `asn1:"explicit,tag:0,optional"`
8282
}
8383

8484
// This is the exposed reflection of the internal OCSP structures.

‎src/pkg/crypto/x509/pkix/pkix.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
// 5280, section 4.1.1.2.
1717
type AlgorithmIdentifier struct {
1818
Algorithm asn1.ObjectIdentifier
19-
Parameters asn1.RawValue "optional"
19+
Parameters asn1.RawValue `asn1:"optional"`
2020
}
2121

2222
type RDNSequence []RelativeDistinguishedNameSET
@@ -32,7 +32,7 @@ type AttributeTypeAndValue struct {
3232
// 5280, section 4.2.
3333
type Extension struct {
3434
Id asn1.ObjectIdentifier
35-
Critical bool "optional"
35+
Critical bool `asn1:"optional"`
3636
Value []byte
3737
}
3838

@@ -149,19 +149,19 @@ func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
149149
// 5280, section 5.1.
150150
type TBSCertificateList struct {
151151
Raw asn1.RawContent
152-
Version int "optional,default:2"
152+
Version int `asn1:"optional,default:2"`
153153
Signature AlgorithmIdentifier
154154
Issuer RDNSequence
155155
ThisUpdate *time.Time
156156
NextUpdate *time.Time
157-
RevokedCertificates []RevokedCertificate "optional"
158-
Extensions []Extension "tag:0,optional,explicit"
157+
RevokedCertificates []RevokedCertificate `asn1:"optional"`
158+
Extensions []Extension `asn1:"tag:0,optional,explicit"`
159159
}
160160

161161
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
162162
// 5280, section 5.1.
163163
type RevokedCertificate struct {
164164
SerialNumber *big.Int
165165
RevocationTime *time.Time
166-
Extensions []Extension "optional"
166+
Extensions []Extension `asn1:"optional"`
167167
}

‎src/pkg/crypto/x509/x509.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ type pkcs1PrivateKey struct {
3030
P *big.Int
3131
Q *big.Int
3232
// We ignore these values, if present, because rsa will calculate them.
33-
Dp *big.Int "optional"
34-
Dq *big.Int "optional"
35-
Qinv *big.Int "optional"
33+
Dp *big.Int `asn1:"optional"`
34+
Dq *big.Int `asn1:"optional"`
35+
Qinv *big.Int `asn1:"optional"`
3636

37-
AdditionalPrimes []pkcs1AdditionalRSAPrime "optional"
37+
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
3838
}
3939

4040
type pkcs1AdditionalRSAPrime struct {
@@ -136,16 +136,16 @@ type certificate struct {
136136

137137
type tbsCertificate struct {
138138
Raw asn1.RawContent
139-
Version int "optional,explicit,default:1,tag:0"
139+
Version int `asn1:"optional,explicit,default:1,tag:0"`
140140
SerialNumber *big.Int
141141
SignatureAlgorithm pkix.AlgorithmIdentifier
142142
Issuer pkix.RDNSequence
143143
Validity validity
144144
Subject pkix.RDNSequence
145145
PublicKey publicKeyInfo
146-
UniqueId asn1.BitString "optional,tag:1"
147-
SubjectUniqueId asn1.BitString "optional,tag:2"
148-
Extensions []pkix.Extension "optional,explicit,tag:3"
146+
UniqueId asn1.BitString `asn1:"optional,tag:1"`
147+
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
148+
Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
149149
}
150150

151151
type dsaAlgorithmParameters struct {
@@ -168,7 +168,7 @@ type publicKeyInfo struct {
168168

169169
// RFC 5280, 4.2.1.1
170170
type authKeyId struct {
171-
Id []byte "optional,tag:0"
171+
Id []byte `asn1:"optional,tag:0"`
172172
}
173173

174174
type SignatureAlgorithm int
@@ -480,8 +480,8 @@ func (h UnhandledCriticalExtension) String() string {
480480
}
481481

482482
type basicConstraints struct {
483-
IsCA bool "optional"
484-
MaxPathLen int "optional"
483+
IsCA bool `asn1:"optional"`
484+
MaxPathLen int `asn1:"optional"`
485485
}
486486

487487
type rsaPublicKey struct {
@@ -497,14 +497,14 @@ type policyInformation struct {
497497

498498
// RFC 5280, 4.2.1.10
499499
type nameConstraints struct {
500-
Permitted []generalSubtree "optional,tag:0"
501-
Excluded []generalSubtree "optional,tag:1"
500+
Permitted []generalSubtree `asn1:"optional,tag:0"`
501+
Excluded []generalSubtree `asn1:"optional,tag:1"`
502502
}
503503

504504
type generalSubtree struct {
505-
Name string "tag:2,optional,ia5"
506-
Min int "optional,tag:0"
507-
Max int "optional,tag:1"
505+
Name string `asn1:"tag:2,optional,ia5"`
506+
Min int `asn1:"optional,tag:0"`
507+
Max int `asn1:"optional,tag:1"`
508508
}
509509

510510
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) {

‎src/pkg/go/types/testdata/exports.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type (
3838
T9 struct {
3939
a int
4040
b, c float32
41-
d []string "tag"
41+
d []string `go:"tag"`
4242
}
4343
T10 struct {
4444
T8

‎src/pkg/json/decode.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ func (d *decodeState) object(v reflect.Value) {
482482
if isValidTag(key) {
483483
for i := 0; i < sv.NumField(); i++ {
484484
f = st.Field(i)
485-
if f.Tag == key {
485+
if f.Tag.Get("json") == key {
486486
ok = true
487487
break
488488
}

0 commit comments

Comments
 (0)
Please sign in to comment.