Source file
src/go/types/struct.go
1
2
3
4
5 package types
6
7 import (
8 "go/ast"
9 "go/token"
10 "strconv"
11 )
12
13
14
15
16
17 type Struct struct {
18 fields []*Var
19 tags []string
20 }
21
22
23
24
25
26 func NewStruct(fields []*Var, tags []string) *Struct {
27 var fset objset
28 for _, f := range fields {
29 if f.name != "_" && fset.insert(f) != nil {
30 panic("multiple fields with the same name")
31 }
32 }
33 if len(tags) > len(fields) {
34 panic("more tags than fields")
35 }
36 s := &Struct{fields: fields, tags: tags}
37 s.markComplete()
38 return s
39 }
40
41
42 func (s *Struct) NumFields() int { return len(s.fields) }
43
44
45 func (s *Struct) Field(i int) *Var { return s.fields[i] }
46
47
48 func (s *Struct) Tag(i int) string {
49 if i < len(s.tags) {
50 return s.tags[i]
51 }
52 return ""
53 }
54
55 func (t *Struct) Underlying() Type { return t }
56 func (t *Struct) String() string { return TypeString(t, nil) }
57
58
59
60
61 func (s *Struct) markComplete() {
62 if s.fields == nil {
63 s.fields = make([]*Var, 0)
64 }
65 }
66
67 func (check *Checker) structType(styp *Struct, e *ast.StructType) {
68 list := e.Fields
69 if list == nil {
70 styp.markComplete()
71 return
72 }
73
74
75 var fields []*Var
76 var tags []string
77
78
79 var fset objset
80
81
82 var typ Type
83 var tag string
84 add := func(ident *ast.Ident, embedded bool, pos token.Pos) {
85 if tag != "" && tags == nil {
86 tags = make([]string, len(fields))
87 }
88 if tags != nil {
89 tags = append(tags, tag)
90 }
91
92 name := ident.Name
93 fld := NewField(pos, check.pkg, name, typ, embedded)
94
95 if name == "_" || check.declareInSet(&fset, pos, fld) {
96 fields = append(fields, fld)
97 check.recordDef(ident, fld)
98 }
99 }
100
101
102
103
104
105 addInvalid := func(ident *ast.Ident, pos token.Pos) {
106 typ = Typ[Invalid]
107 tag = ""
108 add(ident, true, pos)
109 }
110
111 for _, f := range list.List {
112 typ = check.varType(f.Type)
113 tag = check.tag(f.Tag)
114 if len(f.Names) > 0 {
115
116 for _, name := range f.Names {
117 add(name, false, name.Pos())
118 }
119 } else {
120
121
122
123
124 pos := f.Type.Pos()
125 name := embeddedFieldIdent(f.Type)
126 if name == nil {
127 check.invalidAST(f.Type, "embedded field type %s has no name", f.Type)
128 name = ast.NewIdent("_")
129 name.NamePos = pos
130 addInvalid(name, pos)
131 continue
132 }
133 add(name, true, pos)
134
135
136
137
138
139
140
141 embeddedTyp := typ
142 embeddedPos := f.Type
143
144 check.later(func() {
145 t, isPtr := deref(embeddedTyp)
146 switch u := under(t).(type) {
147 case *Basic:
148 if t == Typ[Invalid] {
149
150 return
151 }
152
153 if u.kind == UnsafePointer {
154 check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
155 }
156 case *Pointer:
157 check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
158 case *Interface:
159 if isTypeParam(t) {
160
161
162
163 check.error(embeddedPos, _MisplacedTypeParam, "embedded field type cannot be a (pointer to a) type parameter")
164 break
165 }
166 if isPtr {
167 check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
168 }
169 }
170 }).describef(embeddedPos, "check embedded type %s", embeddedTyp)
171 }
172 }
173
174 styp.fields = fields
175 styp.tags = tags
176 styp.markComplete()
177 }
178
179 func embeddedFieldIdent(e ast.Expr) *ast.Ident {
180 switch e := e.(type) {
181 case *ast.Ident:
182 return e
183 case *ast.StarExpr:
184
185 if _, ok := e.X.(*ast.StarExpr); !ok {
186 return embeddedFieldIdent(e.X)
187 }
188 case *ast.SelectorExpr:
189 return e.Sel
190 case *ast.IndexExpr:
191 return embeddedFieldIdent(e.X)
192 case *ast.IndexListExpr:
193 return embeddedFieldIdent(e.X)
194 }
195 return nil
196 }
197
198 func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
199 if alt := oset.insert(obj); alt != nil {
200 check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
201 check.reportAltDecl(alt)
202 return false
203 }
204 return true
205 }
206
207 func (check *Checker) tag(t *ast.BasicLit) string {
208 if t != nil {
209 if t.Kind == token.STRING {
210 if val, err := strconv.Unquote(t.Value); err == nil {
211 return val
212 }
213 }
214 check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
215 }
216 return ""
217 }
218
View as plain text