1 // Copyright 2021 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package types
6 7 import (
8 "go/ast"
9 "go/token"
10 . "internal/types/errors"
11 "strconv"
12 )
13 14 // ----------------------------------------------------------------------------
15 // API
16 17 // A Struct represents a struct type.
18 type Struct struct {
19 fields []*Var // fields != nil indicates the struct is set up (possibly with len(fields) == 0)
20 tags []string // field tags; nil if there are no tags
21 }
22 23 // NewStruct returns a new struct with the given fields and corresponding field tags.
24 // If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
25 // only as long as required to hold the tag with the largest index i. Consequently,
26 // if no field has a tag, tags may be nil.
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 // NumFields returns the number of fields in the struct (including blank and embedded fields).
43 func (s *Struct) NumFields() int { return len(s.fields) }
44 45 // Field returns the i'th field for 0 <= i < NumFields().
46 func (s *Struct) Field(i int) *Var { return s.fields[i] }
47 48 // Tag returns the i'th field tag for 0 <= i < NumFields().
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 // Implementation
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 // struct fields and tags
76 var fields []*Var
77 var tags []string
78 79 // for double-declaration checks
80 var fset objset
81 82 // current field typ and tag
83 var typ Type
84 var tag string
85 add := func(ident *ast.Ident, embedded bool) {
86 if tag != "" && tags == nil {
87 tags = make([]string, len(fields))
88 }
89 if tags != nil {
90 tags = append(tags, tag)
91 }
92 93 pos := ident.Pos()
94 name := ident.Name
95 fld := NewField(pos, check.pkg, name, typ, embedded)
96 // spec: "Within a struct, non-blank field names must be unique."
97 if name == "_" || check.declareInSet(&fset, pos, fld) {
98 fields = append(fields, fld)
99 check.recordDef(ident, fld)
100 }
101 }
102 103 // addInvalid adds an embedded field of invalid type to the struct for
104 // fields with errors; this keeps the number of struct fields in sync
105 // with the source as long as the fields are _ or have different names
106 // (go.dev/issue/25627).
107 addInvalid := func(ident *ast.Ident) {
108 typ = Typ[Invalid]
109 tag = ""
110 add(ident, true)
111 }
112 113 for _, f := range list.List {
114 typ = check.varType(f.Type)
115 tag = check.tag(f.Tag)
116 if len(f.Names) > 0 {
117 // named fields
118 for _, name := range f.Names {
119 add(name, false)
120 }
121 } else {
122 // embedded field
123 // spec: "An embedded type must be specified as a type name T or as a
124 // pointer to a non-interface type name *T, and T itself may not be a
125 // pointer type."
126 pos := f.Type.Pos() // position of type, for errors
127 name := embeddedFieldIdent(f.Type)
128 if name == nil {
129 check.errorf(f.Type, InvalidSyntaxTree, "embedded field type %s has no name", f.Type)
130 name = ast.NewIdent("_")
131 name.NamePos = pos
132 addInvalid(name)
133 continue
134 }
135 add(name, true) // struct{p.T} field has position of T
136 137 // Because we have a name, typ must be of the form T or *T, where T is the name
138 // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
139 // We must delay this check to the end because we don't want to instantiate
140 // (via under(t)) a possibly incomplete type.
141 142 // for use in the closure below
143 embeddedTyp := typ
144 embeddedPos := f.Type
145 146 check.later(func() {
147 t, isPtr := deref(embeddedTyp)
148 switch u := under(t).(type) {
149 case *Basic:
150 if !isValid(t) {
151 // error was reported before
152 return
153 }
154 // unsafe.Pointer is treated like a regular pointer
155 if u.kind == UnsafePointer {
156 check.error(embeddedPos, InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
157 }
158 case *Pointer:
159 check.error(embeddedPos, InvalidPtrEmbed, "embedded field type cannot be a pointer")
160 case *Interface:
161 if isTypeParam(t) {
162 // The error code here is inconsistent with other error codes for
163 // invalid embedding, because this restriction may be relaxed in the
164 // future, and so it did not warrant a new error code.
165 check.error(embeddedPos, MisplacedTypeParam, "embedded field type cannot be a (pointer to a) type parameter")
166 break
167 }
168 if isPtr {
169 check.error(embeddedPos, InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
170 }
171 }
172 }).describef(embeddedPos, "check embedded type %s", embeddedTyp)
173 }
174 }
175 176 styp.fields = fields
177 styp.tags = tags
178 styp.markComplete()
179 }
180 181 func embeddedFieldIdent(e ast.Expr) *ast.Ident {
182 switch e := e.(type) {
183 case *ast.Ident:
184 return e
185 case *ast.StarExpr:
186 // *T is valid, but **T is not
187 if _, ok := e.X.(*ast.StarExpr); !ok {
188 return embeddedFieldIdent(e.X)
189 }
190 case *ast.SelectorExpr:
191 return e.Sel
192 case *ast.IndexExpr:
193 return embeddedFieldIdent(e.X)
194 case *ast.IndexListExpr:
195 return embeddedFieldIdent(e.X)
196 }
197 return nil // invalid embedded field
198 }
199 200 func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
201 if alt := oset.insert(obj); alt != nil {
202 err := check.newError(DuplicateDecl)
203 err.addf(atPos(pos), "%s redeclared", obj.Name())
204 err.addAltDecl(alt)
205 err.report()
206 return false
207 }
208 return true
209 }
210 211 func (check *Checker) tag(t *ast.BasicLit) string {
212 if t != nil {
213 if t.Kind == token.STRING {
214 if val, err := strconv.Unquote(t.Value); err == nil {
215 return val
216 }
217 }
218 check.errorf(t, InvalidSyntaxTree, "incorrect tag syntax: %q", t.Value)
219 }
220 return ""
221 }
222