golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/typeparams/common_test.go (about)

     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 typeparams_test
     6  
     7  import (
     8  	"go/ast"
     9  	"go/parser"
    10  	"go/token"
    11  	"go/types"
    12  	"testing"
    13  
    14  	. "golang.org/x/tools/internal/typeparams"
    15  )
    16  
    17  func TestGetIndexExprData(t *testing.T) {
    18  	x := &ast.Ident{}
    19  	i := &ast.Ident{}
    20  
    21  	want := &ast.IndexListExpr{X: x, Lbrack: 1, Indices: []ast.Expr{i}, Rbrack: 2}
    22  	tests := map[ast.Node]bool{
    23  		&ast.IndexExpr{X: x, Lbrack: 1, Index: i, Rbrack: 2}: true,
    24  		want:         true,
    25  		&ast.Ident{}: false,
    26  	}
    27  
    28  	for n, isIndexExpr := range tests {
    29  		X, lbrack, indices, rbrack := UnpackIndexExpr(n)
    30  		if got := X != nil; got != isIndexExpr {
    31  			t.Errorf("UnpackIndexExpr(%v) = %v, _, _, _; want nil: %t", n, x, !isIndexExpr)
    32  		}
    33  		if X == nil {
    34  			continue
    35  		}
    36  		if X != x || lbrack != 1 || indices[0] != i || rbrack != 2 {
    37  			t.Errorf("UnpackIndexExprData(%v) = %v, %v, %v, %v; want %+v", n, x, lbrack, indices, rbrack, want)
    38  		}
    39  	}
    40  }
    41  
    42  func TestFuncOriginRecursive(t *testing.T) {
    43  	src := `package p
    44  
    45  type N[A any] int
    46  
    47  func (r N[B]) m() { r.m(); r.n() }
    48  
    49  func (r *N[C]) n() { }
    50  `
    51  	fset := token.NewFileSet()
    52  	f, err := parser.ParseFile(fset, "p.go", src, 0)
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	info := types.Info{
    57  		Defs: make(map[*ast.Ident]types.Object),
    58  		Uses: make(map[*ast.Ident]types.Object),
    59  	}
    60  	var conf types.Config
    61  	if _, err := conf.Check("p", fset, []*ast.File{f}, &info); err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	// Collect objects from types.Info.
    66  	var m, n *types.Func   // the 'origin' methods in Info.Defs
    67  	var mm, mn *types.Func // the methods used in the body of m
    68  
    69  	for _, decl := range f.Decls {
    70  		fdecl, ok := decl.(*ast.FuncDecl)
    71  		if !ok {
    72  			continue
    73  		}
    74  		def := info.Defs[fdecl.Name].(*types.Func)
    75  		switch fdecl.Name.Name {
    76  		case "m":
    77  			m = def
    78  			ast.Inspect(fdecl.Body, func(n ast.Node) bool {
    79  				if call, ok := n.(*ast.CallExpr); ok {
    80  					sel := call.Fun.(*ast.SelectorExpr)
    81  					use := info.Uses[sel.Sel].(*types.Func)
    82  					switch sel.Sel.Name {
    83  					case "m":
    84  						mm = use
    85  					case "n":
    86  						mn = use
    87  					}
    88  				}
    89  				return true
    90  			})
    91  		case "n":
    92  			n = def
    93  		}
    94  	}
    95  
    96  	tests := []struct {
    97  		name        string
    98  		input, want *types.Func
    99  	}{
   100  		{"declared m", m, m},
   101  		{"declared n", n, n},
   102  		{"used m", mm, m},
   103  		{"used n", mn, n},
   104  	}
   105  
   106  	for _, test := range tests {
   107  		if got := test.input.Origin(); got != test.want {
   108  			t.Errorf("Origin(%q) = %v, want %v", test.name, test.input, test.want)
   109  		}
   110  	}
   111  }
   112  
   113  func TestFuncOriginUses(t *testing.T) {
   114  
   115  	tests := []string{
   116  		`type T interface { m() }; func _(t T) { t.m() }`,
   117  		`type T[P any] interface { m() P }; func _[A any](t T[A]) { t.m() }`,
   118  		`type T[P any] interface { m() P }; func _(t T[int]) { t.m() }`,
   119  		`type T[P any] int; func (r T[A]) m() { r.m() }`,
   120  		`type T[P any] int; func (r *T[A]) m() { r.m() }`,
   121  		`type T[P any] int; func (r *T[A]) m() {}; func _(t T[int]) { t.m() }`,
   122  		`type T[P any] int; func (r *T[A]) m() {}; func _[A any](t T[A]) { t.m() }`,
   123  	}
   124  
   125  	for _, src := range tests {
   126  		fset := token.NewFileSet()
   127  		f, err := parser.ParseFile(fset, "p.go", "package p; "+src, 0)
   128  		if err != nil {
   129  			t.Fatal(err)
   130  		}
   131  		info := types.Info{
   132  			Uses: make(map[*ast.Ident]types.Object),
   133  		}
   134  		var conf types.Config
   135  		pkg, err := conf.Check("p", fset, []*ast.File{f}, &info)
   136  		if err != nil {
   137  			t.Fatal(err)
   138  		}
   139  
   140  		// Look up func T.m.
   141  		T := pkg.Scope().Lookup("T").Type()
   142  		obj, _, _ := types.LookupFieldOrMethod(T, true, pkg, "m")
   143  		m := obj.(*types.Func)
   144  
   145  		// Assert that the origin of each t.m() call is p.T.m.
   146  		ast.Inspect(f, func(n ast.Node) bool {
   147  			if call, ok := n.(*ast.CallExpr); ok {
   148  				sel := call.Fun.(*ast.SelectorExpr)
   149  				use := info.Uses[sel.Sel].(*types.Func)
   150  				orig := use.Origin()
   151  				if orig != m {
   152  					t.Errorf("%s:\nUses[%v] = %v, want %v", src, types.ExprString(sel), use, m)
   153  				}
   154  			}
   155  			return true
   156  		})
   157  	}
   158  }
   159  
   160  // Issue #60628 was a crash in gopls caused by inconsistency (#60634) between
   161  // LookupFieldOrMethod and NewFileSet for methods with an illegal
   162  // *T receiver type, where T itself is a pointer.
   163  // This is a regression test for the workaround in the (now deleted) OriginMethod.
   164  func TestFuncOrigin60628(t *testing.T) {
   165  	const src = `package p; type T[P any] *int; func (r *T[A]) f() {}`
   166  	fset := token.NewFileSet()
   167  	f, err := parser.ParseFile(fset, "p.go", src, 0)
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  
   172  	// Expect type error: "invalid receiver type T[A] (pointer or interface type)".
   173  	info := types.Info{
   174  		Uses: make(map[*ast.Ident]types.Object),
   175  	}
   176  	var conf types.Config
   177  	pkg, _ := conf.Check("p", fset, []*ast.File{f}, &info) // error expected
   178  	if pkg == nil {
   179  		t.Fatal("no package")
   180  	}
   181  
   182  	// Look up methodset of *T.
   183  	T := pkg.Scope().Lookup("T").Type()
   184  	mset := types.NewMethodSet(types.NewPointer(T))
   185  	if mset.Len() == 0 {
   186  		t.Errorf("NewMethodSet(*T) is empty")
   187  	}
   188  	for i := 0; i < mset.Len(); i++ {
   189  		sel := mset.At(i)
   190  		m := sel.Obj().(*types.Func)
   191  
   192  		// TODO(adonovan): check the consistency property required to fix #60634.
   193  		if false {
   194  			m2, _, _ := types.LookupFieldOrMethod(T, true, m.Pkg(), m.Name())
   195  			if m2 != m {
   196  				t.Errorf("LookupFieldOrMethod(%v, indirect=true, %v) = %v, want %v",
   197  					T, m, m2, m)
   198  			}
   199  		}
   200  
   201  		// Check the workaround.
   202  		if m.Origin() == nil {
   203  			t.Errorf("Origin(%v) = nil", m)
   204  		}
   205  	}
   206  }
   207  
   208  func TestGenericAssignableTo(t *testing.T) {
   209  	tests := []struct {
   210  		src  string
   211  		want bool
   212  	}{
   213  		// The inciting issue: golang/go#50887.
   214  		{`
   215  			type T[P any] interface {
   216  			        Accept(P)
   217  			}
   218  
   219  			type V[Q any] struct {
   220  			        Element Q
   221  			}
   222  
   223  			func (c V[Q]) Accept(q Q) { c.Element = q }
   224  			`, true},
   225  
   226  		// Various permutations on constraints and signatures.
   227  		{`type T[P ~int] interface{ A(P) }; type V[Q int] int; func (V[Q]) A(Q) {}`, true},
   228  		{`type T[P int] interface{ A(P) }; type V[Q ~int] int; func (V[Q]) A(Q) {}`, false},
   229  		{`type T[P int|string] interface{ A(P) }; type V[Q int] int; func (V[Q]) A(Q) {}`, true},
   230  		{`type T[P any] interface{ A(P) }; type V[Q any] int; func (V[Q]) A(Q, Q) {}`, false},
   231  		{`type T[P any] interface{ int; A(P) }; type V[Q any] int; func (V[Q]) A(Q) {}`, false},
   232  
   233  		// Various structural restrictions on T.
   234  		{`type T[P any] interface{ ~int; A(P) }; type V[Q any] int; func (V[Q]) A(Q) {}`, true},
   235  		{`type T[P any] interface{ ~int|string; A(P) }; type V[Q any] int; func (V[Q]) A(Q) {}`, true},
   236  		{`type T[P any] interface{ int; A(P) }; type V[Q int] int; func (V[Q]) A(Q) {}`, false},
   237  
   238  		// Various recursive constraints.
   239  		{`type T[P ~struct{ f *P }] interface{ A(P) }; type V[Q ~struct{ f *Q }] int; func (V[Q]) A(Q) {}`, true},
   240  		{`type T[P ~struct{ f *P }] interface{ A(P) }; type V[Q ~struct{ g *Q }] int; func (V[Q]) A(Q) {}`, false},
   241  		{`type T[P ~*X, X any] interface{ A(P) X }; type V[Q ~*Y, Y any] int; func (V[Q, Y]) A(Q) (y Y) { return }`, true},
   242  		{`type T[P ~*X, X any] interface{ A(P) X }; type V[Q ~**Y, Y any] int; func (V[Q, Y]) A(Q) (y Y) { return }`, false},
   243  		{`type T[P, X any] interface{ A(P) X }; type V[Q ~*Y, Y any] int; func (V[Q, Y]) A(Q) (y Y) { return }`, true},
   244  		{`type T[P ~*X, X any] interface{ A(P) X }; type V[Q, Y any] int; func (V[Q, Y]) A(Q) (y Y) { return }`, false},
   245  		{`type T[P, X any] interface{ A(P) X }; type V[Q, Y any] int; func (V[Q, Y]) A(Q) (y Y) { return }`, true},
   246  
   247  		// In this test case, we reverse the type parameters in the signature of V.A
   248  		{`type T[P, X any] interface{ A(P) X }; type V[Q, Y any] int; func (V[Q, Y]) A(Y) (y Q) { return }`, false},
   249  		// It would be nice to return true here: V can only be instantiated with
   250  		// [int, int], so the identity of the type parameters should not matter.
   251  		{`type T[P, X any] interface{ A(P) X }; type V[Q, Y int] int; func (V[Q, Y]) A(Y) (y Q) { return }`, false},
   252  	}
   253  
   254  	for _, test := range tests {
   255  		fset := token.NewFileSet()
   256  		f, err := parser.ParseFile(fset, "p.go", "package p; "+test.src, 0)
   257  		if err != nil {
   258  			t.Fatalf("%s:\n%v", test.src, err)
   259  		}
   260  		var conf types.Config
   261  		pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
   262  		if err != nil {
   263  			t.Fatalf("%s:\n%v", test.src, err)
   264  		}
   265  
   266  		V := pkg.Scope().Lookup("V").Type()
   267  		T := pkg.Scope().Lookup("T").Type()
   268  
   269  		if types.AssignableTo(V, T) {
   270  			t.Fatal("AssignableTo")
   271  		}
   272  
   273  		if got := GenericAssignableTo(nil, V, T); got != test.want {
   274  			t.Fatalf("%s:\nGenericAssignableTo(%v, %v) = %v, want %v", test.src, V, T, got, test.want)
   275  		}
   276  	}
   277  }