Source file src/go/types/methodset_test.go

     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 types_test
     6  
     7  import (
     8  	"slices"
     9  	"strings"
    10  	"testing"
    11  
    12  	"go/ast"
    13  	"go/parser"
    14  	"go/token"
    15  	. "go/types"
    16  )
    17  
    18  func TestNewMethodSet(t *testing.T) {
    19  	type method struct {
    20  		name     string
    21  		index    []int
    22  		indirect bool
    23  	}
    24  
    25  	// Tests are expressed src -> methods, for simplifying the composite literal.
    26  	// Should be kept in sync with TestLookupFieldOrMethod.
    27  	tests := map[string][]method{
    28  		// Named types
    29  		"var a T; type T struct{}; func (T) f() {}":   {{"f", []int{0}, false}},
    30  		"var a *T; type T struct{}; func (T) f() {}":  {{"f", []int{0}, true}},
    31  		"var a T; type T struct{}; func (*T) f() {}":  {},
    32  		"var a *T; type T struct{}; func (*T) f() {}": {{"f", []int{0}, true}},
    33  
    34  		// Generic named types
    35  		"var a T[int]; type T[P any] struct{}; func (T[P]) f() {}":   {{"f", []int{0}, false}},
    36  		"var a *T[int]; type T[P any] struct{}; func (T[P]) f() {}":  {{"f", []int{0}, true}},
    37  		"var a T[int]; type T[P any] struct{}; func (*T[P]) f() {}":  {},
    38  		"var a *T[int]; type T[P any] struct{}; func (*T[P]) f() {}": {{"f", []int{0}, true}},
    39  
    40  		// Interfaces
    41  		"var a T; type T interface{ f() }":                           {{"f", []int{0}, true}},
    42  		"var a T1; type ( T1 T2; T2 interface{ f() } )":              {{"f", []int{0}, true}},
    43  		"var a T1; type ( T1 interface{ T2 }; T2 interface{ f() } )": {{"f", []int{0}, true}},
    44  
    45  		// Generic interfaces
    46  		"var a T[int]; type T[P any] interface{ f() }":                                     {{"f", []int{0}, true}},
    47  		"var a T1[int]; type ( T1[P any] T2[P]; T2[P any] interface{ f() } )":              {{"f", []int{0}, true}},
    48  		"var a T1[int]; type ( T1[P any] interface{ T2[P] }; T2[P any] interface{ f() } )": {{"f", []int{0}, true}},
    49  
    50  		// Embedding
    51  		"var a struct{ E }; type E interface{ f() }":            {{"f", []int{0, 0}, true}},
    52  		"var a *struct{ E }; type E interface{ f() }":           {{"f", []int{0, 0}, true}},
    53  		"var a struct{ E }; type E struct{}; func (E) f() {}":   {{"f", []int{0, 0}, false}},
    54  		"var a struct{ *E }; type E struct{}; func (E) f() {}":  {{"f", []int{0, 0}, true}},
    55  		"var a struct{ E }; type E struct{}; func (*E) f() {}":  {},
    56  		"var a struct{ *E }; type E struct{}; func (*E) f() {}": {{"f", []int{0, 0}, true}},
    57  
    58  		// Embedding of generic types
    59  		"var a struct{ E[int] }; type E[P any] interface{ f() }":               {{"f", []int{0, 0}, true}},
    60  		"var a *struct{ E[int] }; type E[P any] interface{ f() }":              {{"f", []int{0, 0}, true}},
    61  		"var a struct{ E[int] }; type E[P any] struct{}; func (E[P]) f() {}":   {{"f", []int{0, 0}, false}},
    62  		"var a struct{ *E[int] }; type E[P any] struct{}; func (E[P]) f() {}":  {{"f", []int{0, 0}, true}},
    63  		"var a struct{ E[int] }; type E[P any] struct{}; func (*E[P]) f() {}":  {},
    64  		"var a struct{ *E[int] }; type E[P any] struct{}; func (*E[P]) f() {}": {{"f", []int{0, 0}, true}},
    65  
    66  		// collisions
    67  		"var a struct{ E1; *E2 }; type ( E1 interface{ f() }; E2 struct{ f int })":            {},
    68  		"var a struct{ E1; *E2 }; type ( E1 struct{ f int }; E2 struct{} ); func (E2) f() {}": {},
    69  
    70  		// recursive generic types; see go.dev/issue/52715
    71  		"var a T[int]; type ( T[P any] struct { *N[P] }; N[P any] struct { *T[P] } ); func (N[P]) m() {}": {{"m", []int{0, 0}, true}},
    72  		"var a T[int]; type ( T[P any] struct { *N[P] }; N[P any] struct { *T[P] } ); func (T[P]) m() {}": {{"m", []int{0}, false}},
    73  	}
    74  
    75  	tParamTests := map[string][]method{
    76  		// By convention, look up a in the scope of "g"
    77  		"type C interface{ f() }; func g[T C](a T){}":               {{"f", []int{0}, true}},
    78  		"type C interface{ f() }; func g[T C]() { var a T; _ = a }": {{"f", []int{0}, true}},
    79  
    80  		// go.dev/issue/43621: We don't allow this anymore. Keep this code in case we
    81  		// decide to revisit this decision.
    82  		// "type C interface{ f() }; func g[T C]() { var a struct{T}; _ = a }": {{"f", []int{0, 0}, true}},
    83  
    84  		// go.dev/issue/45639: We also don't allow this anymore.
    85  		// "type C interface{ f() }; func g[T C]() { type Y T; var a Y; _ = a }": {},
    86  	}
    87  
    88  	check := func(src string, methods []method, generic bool) {
    89  		pkg := mustTypecheck("package p;"+src, nil, nil)
    90  
    91  		scope := pkg.Scope()
    92  		if generic {
    93  			fn := pkg.Scope().Lookup("g").(*Func)
    94  			scope = fn.Scope()
    95  		}
    96  		obj := scope.Lookup("a")
    97  		if obj == nil {
    98  			t.Errorf("%s: incorrect test case - no object a", src)
    99  			return
   100  		}
   101  
   102  		ms := NewMethodSet(obj.Type())
   103  		if got, want := ms.Len(), len(methods); got != want {
   104  			t.Errorf("%s: got %d methods, want %d", src, got, want)
   105  			return
   106  		}
   107  		for i, m := range methods {
   108  			sel := ms.At(i)
   109  			if got, want := sel.Obj().Name(), m.name; got != want {
   110  				t.Errorf("%s [method %d]: got name = %q at, want %q", src, i, got, want)
   111  			}
   112  			if got, want := sel.Index(), m.index; !slices.Equal(got, want) {
   113  				t.Errorf("%s [method %d]: got index = %v, want %v", src, i, got, want)
   114  			}
   115  			if got, want := sel.Indirect(), m.indirect; got != want {
   116  				t.Errorf("%s [method %d]: got indirect = %v, want %v", src, i, got, want)
   117  			}
   118  		}
   119  	}
   120  
   121  	for src, methods := range tests {
   122  		check(src, methods, false)
   123  	}
   124  
   125  	for src, methods := range tParamTests {
   126  		check(src, methods, true)
   127  	}
   128  }
   129  
   130  // Test for go.dev/issue/52715
   131  func TestNewMethodSet_RecursiveGeneric(t *testing.T) {
   132  	const src = `
   133  package pkg
   134  
   135  type Tree[T any] struct {
   136  	*Node[T]
   137  }
   138  
   139  type Node[T any] struct {
   140  	*Tree[T]
   141  }
   142  
   143  type Instance = *Tree[int]
   144  `
   145  
   146  	fset := token.NewFileSet()
   147  	f, err := parser.ParseFile(fset, "foo.go", src, 0)
   148  	if err != nil {
   149  		panic(err)
   150  	}
   151  	pkg := NewPackage("pkg", f.Name.Name)
   152  	if err := NewChecker(nil, fset, pkg, nil).Files([]*ast.File{f}); err != nil {
   153  		panic(err)
   154  	}
   155  
   156  	T := pkg.Scope().Lookup("Instance").Type()
   157  	_ = NewMethodSet(T) // verify that NewMethodSet terminates
   158  }
   159  
   160  func TestIssue60634(t *testing.T) {
   161  	const src = `
   162  package p
   163  type T *int
   164  func (T) m() {} // expected error: invalid receiver type
   165  `
   166  
   167  	fset := token.NewFileSet()
   168  	f, err := parser.ParseFile(fset, "p.go", src, 0)
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	var conf Config
   174  	pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
   175  	if err == nil || !strings.Contains(err.Error(), "invalid receiver type") {
   176  		t.Fatalf("missing or unexpected error: %v", err)
   177  	}
   178  
   179  	// look up T.m and (*T).m
   180  	T := pkg.Scope().Lookup("T").Type()
   181  	name := "m"
   182  	for _, recv := range []Type{T, NewPointer(T)} {
   183  		// LookupFieldOrMethod and NewMethodSet must match:
   184  		// either both find m or neither finds it.
   185  		obj1, _, _ := LookupFieldOrMethod(recv, false, pkg, name)
   186  		mset := NewMethodSet(recv)
   187  		if (obj1 != nil) != (mset.Len() == 1) {
   188  			t.Fatalf("lookup(%v.%s): got obj = %v, mset = %v", recv, name, obj1, mset)
   189  		}
   190  		// If the method exists, both must return the same object.
   191  		if obj1 != nil {
   192  			obj2 := mset.At(0).Obj()
   193  			if obj1 != obj2 {
   194  				t.Fatalf("%v != %v", obj1, obj2)
   195  			}
   196  		}
   197  	}
   198  }
   199  

View as plain text