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