1
2
3
4
5 package types2_test
6
7 import (
8 "cmd/compile/internal/syntax"
9 "fmt"
10 "strings"
11 "testing"
12
13 . "cmd/compile/internal/types2"
14 )
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 func checkInvariants(pkg *Package, info *Info, hasErrors bool) []error {
30 v := &validator{
31 pkg: pkg,
32 info: info,
33 hasErrors: hasErrors,
34 seenTypes: make(map[Type]bool),
35 seenObjs: make(map[Object]bool),
36 }
37 if pkg != nil {
38 v.checkScope(pkg.Scope(), "pkg.Scope()")
39 }
40 v.checkInfo(info)
41 return v.errs
42 }
43
44 type validator struct {
45 pkg *Package
46 info *Info
47 hasErrors bool
48 seenTypes map[Type]bool
49 seenObjs map[Object]bool
50 errs []error
51 }
52
53 func (v *validator) errorf(format string, args ...any) {
54 v.errs = append(v.errs, fmt.Errorf(format, args...))
55 }
56
57 func (v *validator) checkScope(s *Scope, where string) {
58 if s == nil {
59 return
60 }
61 for _, name := range s.Names() {
62 obj := s.Lookup(name)
63 v.checkObject(obj, fmt.Sprintf("%s.Lookup(%q)", where, name))
64 }
65 for i := 0; i < s.NumChildren(); i++ {
66 v.checkScope(s.Child(i), fmt.Sprintf("%s.Child(%d)", where, i))
67 }
68 }
69
70 func (v *validator) checkInfo(info *Info) {
71 if info == nil {
72 return
73 }
74 for ident, obj := range info.Defs {
75 if obj != nil {
76 v.checkObject(obj, fmt.Sprintf("Info.Defs[%s]", ident.Value))
77 }
78 }
79 for ident, obj := range info.Uses {
80 if obj != nil {
81 v.checkObject(obj, fmt.Sprintf("Info.Uses[%s]", ident.Value))
82 }
83 }
84 for node, obj := range info.Implicits {
85 if obj != nil {
86 v.checkObject(obj, fmt.Sprintf("Info.Implicits[%T]", node))
87 }
88 }
89 for _, s := range info.Scopes {
90 v.checkScope(s, "Info.Scopes")
91 }
92 for expr, tv := range info.Types {
93 if tv.Type == nil {
94 v.errorf("Info.Types[%v] has nil Type", expr)
95 } else if !tv.IsBuiltin() {
96 v.checkType(tv.Type, fmt.Sprintf("Info.Types[%v]", expr))
97 }
98 }
99 }
100
101 func (v *validator) checkObject(obj Object, where string) {
102 if obj == nil || v.seenObjs[obj] {
103 return
104 }
105 v.seenObjs[obj] = true
106
107
108 typ := obj.Type()
109 if typ == nil {
110 v.errorf("object %q (%T) at %v has nil Type() in %s", obj.Name(), obj, obj.Pos(), where)
111 return
112 }
113
114
115 if fn, ok := obj.(*Func); ok {
116 if _, ok := typ.(*Signature); !ok {
117 v.errorf("func %s has type %T, want *Signature in %s", fn.Name(), typ, where)
118 }
119 }
120
121
122
123 switch obj.(type) {
124 case *PkgName, *Builtin, *Label:
125 return
126 }
127
128 v.checkType(typ, fmt.Sprintf("object %s", obj.Name()))
129 }
130
131 func (v *validator) checkType(typ Type, where string) {
132 if typ == nil {
133 v.errorf("nil Type encountered at %s", where)
134 return
135 }
136 if v.seenTypes[typ] {
137 return
138 }
139 v.seenTypes[typ] = true
140
141
142 var under Type
143 func() {
144 defer func() {
145 if r := recover(); r != nil {
146 v.errorf("panic calling Underlying() on %T at %s: %v", typ, where, r)
147 }
148 }()
149 under = typ.Underlying()
150 }()
151 if under == nil {
152 v.errorf("type %T at %s has nil Underlying()", typ, where)
153 }
154
155 switch t := typ.(type) {
156 case *Basic:
157
158 if t == Typ[Invalid] && !v.hasErrors {
159 v.errorf("Typ[Invalid] published at %s without reported package errors", where)
160 }
161
162 case *Pointer:
163 if t.Elem() == nil {
164 v.errorf("Pointer at %s has nil Elem()", where)
165 } else {
166 v.checkType(t.Elem(), where+".Elem()")
167 }
168
169 case *Array:
170 if t.Elem() == nil {
171 v.errorf("Array at %s has nil Elem()", where)
172 } else {
173 v.checkType(t.Elem(), where+".Elem()")
174 }
175
176 case *Slice:
177 if t.Elem() == nil {
178 v.errorf("Slice at %s has nil Elem()", where)
179 } else {
180 v.checkType(t.Elem(), where+".Elem()")
181 }
182
183 case *Map:
184 if t.Key() == nil {
185 v.errorf("Map at %s has nil Key()", where)
186 } else {
187 v.checkType(t.Key(), where+".Key()")
188 }
189 if t.Elem() == nil {
190 v.errorf("Map at %s has nil Elem()", where)
191 } else {
192 v.checkType(t.Elem(), where+".Elem()")
193 }
194
195 case *Chan:
196 if t.Elem() == nil {
197 v.errorf("Chan at %s has nil Elem()", where)
198 } else {
199 v.checkType(t.Elem(), where+".Elem()")
200 }
201
202 case *Struct:
203 for i := 0; i < t.NumFields(); i++ {
204 f := t.Field(i)
205 if f == nil {
206 v.errorf("Struct at %s has nil Field(%d)", where, i)
207 } else {
208 v.checkObject(f, fmt.Sprintf("%s.Field(%d:%s)", where, i, f.Name()))
209 }
210 }
211
212 case *Tuple:
213 for i := 0; i < t.Len(); i++ {
214 elem := t.At(i)
215 if elem == nil {
216 v.errorf("Tuple at %s has nil At(%d)", where, i)
217 } else {
218 v.checkObject(elem, fmt.Sprintf("%s.At(%d)", where, i))
219 }
220 }
221
222 case *Signature:
223 if recv := t.Recv(); recv != nil {
224 v.checkObject(recv, where+".Recv()")
225 }
226 if params := t.Params(); params != nil {
227 v.checkType(params, where+".Params()")
228 }
229 if results := t.Results(); results != nil {
230 v.checkType(results, where+".Results()")
231 }
232 v.checkTypeParamList(t.TypeParams(), where+".TypeParams()")
233 v.checkTypeParamList(t.RecvTypeParams(), where+".RecvTypeParams()")
234
235 case *Interface:
236 for i := 0; i < t.NumMethods(); i++ {
237 m := t.Method(i)
238 if m == nil {
239 v.errorf("Interface at %s has nil Method(%d)", where, i)
240 } else {
241 v.checkObject(m, fmt.Sprintf("%s.Method(%s)", where, m.Name()))
242 }
243 }
244 for i := 0; i < t.NumEmbeddeds(); i++ {
245 emb := t.EmbeddedType(i)
246 if emb == nil {
247 v.errorf("Interface at %s has nil EmbeddedType(%d)", where, i)
248 } else {
249 v.checkType(emb, fmt.Sprintf("%s.EmbeddedType(%d)", where, i))
250 }
251 }
252
253 case *Named:
254 if t.Obj() == nil {
255 v.errorf("Named at %s has nil Obj()", where)
256 } else {
257 v.checkObject(t.Obj(), where+".Obj()")
258 }
259 if targs := t.TypeArgs(); targs != nil {
260 for i := 0; i < targs.Len(); i++ {
261 v.checkType(targs.At(i), fmt.Sprintf("%s.TypeArgs(%d)", where, i))
262 }
263 }
264
265
266
267
268
269 if t.Origin() == t {
270 for i := 0; i < t.NumMethods(); i++ {
271 m := t.Method(i)
272 if m == nil {
273 v.errorf("Named at %s has nil Method(%d)", where, i)
274 } else {
275 v.checkObject(m, fmt.Sprintf("%s.Method(%s)", where, m.Name()))
276 }
277 }
278 }
279
280 case *Alias:
281 func() {
282 defer func() {
283 if r := recover(); r != nil {
284 v.errorf("panic calling Rhs() on Alias at %s: %v", where, r)
285 }
286 }()
287 rhs := t.Rhs()
288 if rhs == nil {
289 v.errorf("Alias at %s has nil Rhs()", where)
290 } else {
291 v.checkType(rhs, where+".Rhs()")
292 }
293 }()
294 func() {
295 defer func() {
296 if r := recover(); r != nil {
297 v.errorf("panic calling Unalias() on Alias at %s: %v", where, r)
298 }
299 }()
300 if actual := Unalias(t); actual == nil {
301 v.errorf("Unalias() returned nil on Alias at %s", where)
302 }
303 }()
304
305 case *TypeParam:
306 func() {
307 defer func() {
308 if r := recover(); r != nil {
309 v.errorf("panic calling Constraint() on TypeParam at %s: %v", where, r)
310 }
311 }()
312 c := t.Constraint()
313 if c == nil {
314 v.errorf("TypeParam at %s has nil Constraint()", where)
315 } else {
316 v.checkType(c, where+".Constraint()")
317 }
318 }()
319 if t.Obj() == nil {
320 v.errorf("TypeParam at %s has nil Obj()", where)
321 } else {
322 v.checkObject(t.Obj(), where+".Obj()")
323 }
324
325 case *Union:
326 for i := 0; i < t.Len(); i++ {
327 term := t.Term(i)
328 if term == nil || term.Type() == nil {
329 v.errorf("Union at %s has nil term at %d", where, i)
330 } else {
331 v.checkType(term.Type(), fmt.Sprintf("%s.Term(%d)", where, i))
332 }
333 }
334 }
335 }
336
337 func (v *validator) checkTypeParamList(tparams *TypeParamList, where string) {
338 if tparams == nil {
339 return
340 }
341 for i := 0; i < tparams.Len(); i++ {
342 tp := tparams.At(i)
343 if tp == nil {
344 v.errorf("%s at index %d is nil", where, i)
345 } else {
346 v.checkType(tp, fmt.Sprintf("%s.At(%d)", where, i))
347 }
348 }
349 }
350
351 func TestBrokenCodeInvariants(t *testing.T) {
352 tests := []struct {
353 name string
354 src string
355 }{
356 {
357 name: "cyclic aliases",
358 src: `package p
359 type A = B
360 type B = A`,
361 },
362 {
363 name: "cyclic defined types",
364 src: `package p
365 type A B
366 type B A`,
367 },
368 {
369 name: "mutually recursive alias and struct",
370 src: `package p
371 type A = struct { b B }
372 type B = struct { a A }`,
373 },
374 {
375 name: "broken method receiver",
376 src: `package p
377 func (r Undeclared) M() {}`,
378 },
379 {
380 name: "generic receiver with wrong param count",
381 src: `package p
382 type T[A, B any] struct{}
383 func (r T[A]) M() {}`,
384 },
385 {
386 name: "broken signature parameters and results",
387 src: `package p
388 func f(a undeclared) (b undeclared) { return }`,
389 },
390 {
391 name: "broken short var decl in func body",
392 src: `package p
393 func f() {
394 x := undeclared
395 _ = x
396 }`,
397 },
398 {
399 name: "broken struct fields",
400 src: `package p
401 type S struct {
402 f undeclared
403 }`,
404 },
405 }
406
407 for _, tt := range tests {
408 t.Run(tt.name, func(t *testing.T) {
409 f, err := syntax.Parse(syntax.NewFileBase("test.go"), strings.NewReader(tt.src), func(error) {}, nil, 0)
410 if err != nil {
411 t.Fatal(err)
412 }
413 conf := Config{
414 Error: func(error) {},
415 }
416 info := Info{
417 Types: make(map[syntax.Expr]TypeAndValue),
418 Defs: make(map[*syntax.Name]Object),
419 Uses: make(map[*syntax.Name]Object),
420 Implicits: make(map[syntax.Node]Object),
421 Scopes: make(map[syntax.Node]*Scope),
422 }
423 pkg, _ := conf.Check("p", []*syntax.File{f}, &info)
424 if errs := checkInvariants(pkg, &info, true); len(errs) > 0 {
425 for _, err := range errs {
426 t.Errorf("invariant violated: %v", err)
427 }
428 }
429 })
430 }
431 }
432
View as plain text