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