Help us shape the future of Go! Take the 2026 Go Developer Survey

Source file src/go/types/validate_test.go

     1  // Copyright 2026 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_test
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/parser"
    11  	"go/token"
    12  	"testing"
    13  
    14  	. "go/types"
    15  )
    16  
    17  // checkInvariants verifies structural well-formedness and consistency of
    18  // a type-checked package and its recorded type information:
    19  //  1. No Object has a nil Type().
    20  //  2. Every *Func object dynamically has a *Signature type.
    21  //  3. No compound type component (Pointer.Elem, Slice.Elem, Array.Elem,
    22  //     Map.Key, Map.Elem, Chan.Elem, Struct.Field, Tuple.At, Interface.Method,
    23  //     Interface.EmbeddedType, TypeParam.Constraint, Union.Term) is nil
    24  //     (empty Signature.Params and Results are canonically nil *Tuples).
    25  //  4. Accessor methods (Underlying, Rhs, Constraint, Unalias) never panic.
    26  //  5. Typ[Invalid] is never published for typed objects (*Var, *Const, *TypeName,
    27  //     *Func) or non-builtin expression types unless the package reported an error.
    28  //     (Non-typed objects like *PkgName, *Builtin, and *Label, and built-in
    29  //     expressions, have Typ[Invalid] by specification).
    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  	// Invariant 1: obj.Type() must never be nil.
   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  	// Invariant 2: Func.Type() must dynamically be a *Signature.
   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  	// PkgName, Builtin, and Label don't have a Go type; their Type()
   123  	// is defined as Typ[Invalid] by specification.
   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  	// Safe Underlying() call: must never panic.
   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  		// Invariant: Typ[Invalid] is never published without a reported error.
   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  		// Only check methods on origin types. On instantiated types (t.Origin() != t),
   266  		// methods are lazily expanded from the origin; eagerly traversing methods of
   267  		// instantiated types whose methods return further generic instances (e.g.
   268  		// func (List[E]) Map[F any](...) List[F]) leads to infinite recursive method
   269  		// instantiation.
   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) {}, // ignore expected errors
   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