github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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  	"github.com/powerman/golang-tools/internal/testenv"
    15  	. "github.com/powerman/golang-tools/internal/typeparams"
    16  )
    17  
    18  func TestGetIndexExprData(t *testing.T) {
    19  	x := &ast.Ident{}
    20  	i := &ast.Ident{}
    21  
    22  	want := &IndexListExpr{X: x, Lbrack: 1, Indices: []ast.Expr{i}, Rbrack: 2}
    23  	tests := map[ast.Node]bool{
    24  		&ast.IndexExpr{X: x, Lbrack: 1, Index: i, Rbrack: 2}: true,
    25  		want:         true,
    26  		&ast.Ident{}: false,
    27  	}
    28  
    29  	for n, isIndexExpr := range tests {
    30  		X, lbrack, indices, rbrack := UnpackIndexExpr(n)
    31  		if got := X != nil; got != isIndexExpr {
    32  			t.Errorf("UnpackIndexExpr(%v) = %v, _, _, _; want nil: %t", n, x, !isIndexExpr)
    33  		}
    34  		if X == nil {
    35  			continue
    36  		}
    37  		if X != x || lbrack != 1 || indices[0] != i || rbrack != 2 {
    38  			t.Errorf("UnpackIndexExprData(%v) = %v, %v, %v, %v; want %+v", n, x, lbrack, indices, rbrack, want)
    39  		}
    40  	}
    41  }
    42  
    43  func TestOriginMethodRecursive(t *testing.T) {
    44  	testenv.NeedsGo1Point(t, 18)
    45  	src := `package p
    46  
    47  type N[A any] int
    48  
    49  func (r N[B]) m() { r.m(); r.n() }
    50  
    51  func (r *N[C]) n() { }
    52  `
    53  	fset := token.NewFileSet()
    54  	f, err := parser.ParseFile(fset, "p.go", src, 0)
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  	info := types.Info{
    59  		Defs: make(map[*ast.Ident]types.Object),
    60  		Uses: make(map[*ast.Ident]types.Object),
    61  	}
    62  	var conf types.Config
    63  	if _, err := conf.Check("p", fset, []*ast.File{f}, &info); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	// Collect objects from types.Info.
    68  	var m, n *types.Func   // the 'origin' methods in Info.Defs
    69  	var mm, mn *types.Func // the methods used in the body of m
    70  
    71  	for _, decl := range f.Decls {
    72  		fdecl, ok := decl.(*ast.FuncDecl)
    73  		if !ok {
    74  			continue
    75  		}
    76  		def := info.Defs[fdecl.Name].(*types.Func)
    77  		switch fdecl.Name.Name {
    78  		case "m":
    79  			m = def
    80  			ast.Inspect(fdecl.Body, func(n ast.Node) bool {
    81  				if call, ok := n.(*ast.CallExpr); ok {
    82  					sel := call.Fun.(*ast.SelectorExpr)
    83  					use := info.Uses[sel.Sel].(*types.Func)
    84  					switch sel.Sel.Name {
    85  					case "m":
    86  						mm = use
    87  					case "n":
    88  						mn = use
    89  					}
    90  				}
    91  				return true
    92  			})
    93  		case "n":
    94  			n = def
    95  		}
    96  	}
    97  
    98  	tests := []struct {
    99  		name        string
   100  		input, want *types.Func
   101  	}{
   102  		{"declared m", m, m},
   103  		{"declared n", n, n},
   104  		{"used m", mm, m},
   105  		{"used n", mn, n},
   106  	}
   107  
   108  	for _, test := range tests {
   109  		if got := OriginMethod(test.input); got != test.want {
   110  			t.Errorf("OriginMethod(%q) = %v, want %v", test.name, test.input, test.want)
   111  		}
   112  	}
   113  }
   114  
   115  func TestOriginMethodUses(t *testing.T) {
   116  	testenv.NeedsGo1Point(t, 18)
   117  
   118  	tests := []string{
   119  		`type T interface { m() }; func _(t T) { t.m() }`,
   120  		`type T[P any] interface { m() P }; func _[A any](t T[A]) { t.m() }`,
   121  		`type T[P any] interface { m() P }; func _(t T[int]) { t.m() }`,
   122  		`type T[P any] int; func (r T[A]) m() { r.m() }`,
   123  		`type T[P any] int; func (r *T[A]) m() { r.m() }`,
   124  		`type T[P any] int; func (r *T[A]) m() {}; func _(t T[int]) { t.m() }`,
   125  		`type T[P any] int; func (r *T[A]) m() {}; func _[A any](t T[A]) { t.m() }`,
   126  	}
   127  
   128  	for _, src := range tests {
   129  		fset := token.NewFileSet()
   130  		f, err := parser.ParseFile(fset, "p.go", "package p; "+src, 0)
   131  		if err != nil {
   132  			t.Fatal(err)
   133  		}
   134  		info := types.Info{
   135  			Uses: make(map[*ast.Ident]types.Object),
   136  		}
   137  		var conf types.Config
   138  		pkg, err := conf.Check("p", fset, []*ast.File{f}, &info)
   139  		if err != nil {
   140  			t.Fatal(err)
   141  		}
   142  
   143  		T := pkg.Scope().Lookup("T").Type()
   144  		obj, _, _ := types.LookupFieldOrMethod(T, true, pkg, "m")
   145  		m := obj.(*types.Func)
   146  
   147  		ast.Inspect(f, func(n ast.Node) bool {
   148  			if call, ok := n.(*ast.CallExpr); ok {
   149  				sel := call.Fun.(*ast.SelectorExpr)
   150  				use := info.Uses[sel.Sel].(*types.Func)
   151  				orig := OriginMethod(use)
   152  				if orig != m {
   153  					t.Errorf("%s:\nUses[%v] = %v, want %v", src, types.ExprString(sel), use, m)
   154  				}
   155  			}
   156  			return true
   157  		})
   158  	}
   159  }
   160  
   161  func TestGenericAssignableTo(t *testing.T) {
   162  	testenv.NeedsGo1Point(t, 18)
   163  
   164  	tests := []struct {
   165  		src  string
   166  		want bool
   167  	}{
   168  		// The inciting issue: golang/go#50887.
   169  		{`
   170  			type T[P any] interface {
   171  			        Accept(P)
   172  			}
   173  
   174  			type V[Q any] struct {
   175  			        Element Q
   176  			}
   177  
   178  			func (c V[Q]) Accept(q Q) { c.Element = q }
   179  			`, true},
   180  
   181  		// Various permutations on constraints and signatures.
   182  		{`type T[P ~int] interface{ A(P) }; type V[Q int] int; func (V[Q]) A(Q) {}`, true},
   183  		{`type T[P int] interface{ A(P) }; type V[Q ~int] int; func (V[Q]) A(Q) {}`, false},
   184  		{`type T[P int|string] interface{ A(P) }; type V[Q int] int; func (V[Q]) A(Q) {}`, true},
   185  		{`type T[P any] interface{ A(P) }; type V[Q any] int; func (V[Q]) A(Q, Q) {}`, false},
   186  		{`type T[P any] interface{ int; A(P) }; type V[Q any] int; func (V[Q]) A(Q) {}`, false},
   187  
   188  		// Various structural restrictions on T.
   189  		{`type T[P any] interface{ ~int; A(P) }; type V[Q any] int; func (V[Q]) A(Q) {}`, true},
   190  		{`type T[P any] interface{ ~int|string; A(P) }; type V[Q any] int; func (V[Q]) A(Q) {}`, true},
   191  		{`type T[P any] interface{ int; A(P) }; type V[Q int] int; func (V[Q]) A(Q) {}`, false},
   192  
   193  		// Various recursive constraints.
   194  		{`type T[P ~struct{ f *P }] interface{ A(P) }; type V[Q ~struct{ f *Q }] int; func (V[Q]) A(Q) {}`, true},
   195  		{`type T[P ~struct{ f *P }] interface{ A(P) }; type V[Q ~struct{ g *Q }] int; func (V[Q]) A(Q) {}`, false},
   196  		{`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},
   197  		{`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},
   198  		{`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},
   199  		{`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},
   200  		{`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},
   201  
   202  		// In this test case, we reverse the type parameters in the signature of V.A
   203  		{`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},
   204  		// It would be nice to return true here: V can only be instantiated with
   205  		// [int, int], so the identity of the type parameters should not matter.
   206  		{`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},
   207  	}
   208  
   209  	for _, test := range tests {
   210  		fset := token.NewFileSet()
   211  		f, err := parser.ParseFile(fset, "p.go", "package p; "+test.src, 0)
   212  		if err != nil {
   213  			t.Fatalf("%s:\n%v", test.src, err)
   214  		}
   215  		var conf types.Config
   216  		pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
   217  		if err != nil {
   218  			t.Fatalf("%s:\n%v", test.src, err)
   219  		}
   220  
   221  		V := pkg.Scope().Lookup("V").Type()
   222  		T := pkg.Scope().Lookup("T").Type()
   223  
   224  		if types.AssignableTo(V, T) {
   225  			t.Fatal("AssignableTo")
   226  		}
   227  
   228  		if got := GenericAssignableTo(nil, V, T); got != test.want {
   229  			t.Fatalf("%s:\nGenericAssignableTo(%v, %v) = %v, want %v", test.src, V, T, got, test.want)
   230  		}
   231  	}
   232  }