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

View as plain text