| 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 | //go:build go1.18 |
| 6 | // +build go1.18 |
| 7 | |
| 8 | package objectpath_test |
| 9 | |
| 10 | import ( |
| 11 | "go/types" |
| 12 | "testing" |
| 13 | |
| 14 | "golang.org/x/tools/go/buildutil" |
| 15 | "golang.org/x/tools/go/loader" |
| 16 | "golang.org/x/tools/go/types/objectpath" |
| 17 | ) |
| 18 | |
| 19 | func TestGenericPaths(t *testing.T) { |
| 20 | pkgs := map[string]map[string]string{ |
| 21 | "b": {"b.go": ` |
| 22 | package b |
| 23 | |
| 24 | const C int = 1 |
| 25 | |
| 26 | type T[TP0 any, TP1 interface{ M0(); M1() }] struct{} |
| 27 | |
| 28 | func (T[RP0, RP1]) M() {} |
| 29 | |
| 30 | type N int |
| 31 | |
| 32 | func (N) M0() |
| 33 | func (N) M1() |
| 34 | |
| 35 | type A = T[int, N] |
| 36 | |
| 37 | func F[FP0 any, FP1 interface{ M() }](FP0, FP1) {} |
| 38 | `}, |
| 39 | } |
| 40 | paths := []pathTest{ |
| 41 | // Good paths |
| 42 | {"b", "T", "type b.T[TP0 any, TP1 interface{M0(); M1()}] struct{}", ""}, |
| 43 | {"b", "T.O", "type b.T[TP0 any, TP1 interface{M0(); M1()}] struct{}", ""}, |
| 44 | {"b", "T.M0", "func (b.T[RP0, RP1]).M()", ""}, |
| 45 | {"b", "T.T0O", "type parameter TP0 any", ""}, |
| 46 | {"b", "T.T1O", "type parameter TP1 interface{M0(); M1()}", ""}, |
| 47 | {"b", "T.T1CM0", "func (interface).M0()", ""}, |
| 48 | {"b", "F.T0O", "type parameter FP0 any", ""}, |
| 49 | {"b", "F.T1CM0", "func (interface).M()", ""}, |
| 50 | // Obj of an instance is the generic declaration. |
| 51 | {"b", "A.O", "type b.T[TP0 any, TP1 interface{M0(); M1()}] struct{}", ""}, |
| 52 | {"b", "A.M0", "func (b.T[int, b.N]).M()", ""}, |
| 53 | |
| 54 | // Bad paths |
| 55 | {"b", "N.C", "", "invalid path: ends with 'C', want [AFMO]"}, |
| 56 | {"b", "N.CO", "", "cannot apply 'C' to b.N (got *types.Named, want type parameter)"}, |
| 57 | {"b", "N.T", "", `invalid path: bad numeric operand "" for code 'T'`}, |
| 58 | {"b", "N.T0", "", "tuple index 0 out of range [0-0)"}, |
| 59 | {"b", "T.T2O", "", "tuple index 2 out of range [0-2)"}, |
| 60 | {"b", "T.T1M0", "", "cannot apply 'M' to TP1 (got *types.TypeParam, want interface or named)"}, |
| 61 | {"b", "C.T0", "", "cannot apply 'T' to int (got *types.Basic, want named or signature)"}, |
| 62 | } |
| 63 | |
| 64 | conf := loader.Config{Build: buildutil.FakeContext(pkgs)} |
| 65 | conf.Import("b") |
| 66 | prog, err := conf.Load() |
| 67 | if err != nil { |
| 68 | t.Fatal(err) |
| 69 | } |
| 70 | |
| 71 | for _, test := range paths { |
| 72 | if err := testPath(prog, test); err != nil { |
| 73 | t.Error(err) |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // bad objects |
| 78 | for _, test := range []struct { |
| 79 | obj types.Object |
| 80 | wantErr string |
| 81 | }{ |
| 82 | {types.Universe.Lookup("any"), "predeclared type any = interface{} has no path"}, |
| 83 | {types.Universe.Lookup("comparable"), "predeclared type comparable interface{comparable} has no path"}, |
| 84 | } { |
| 85 | path, err := objectpath.For(test.obj) |
| 86 | if err == nil { |
| 87 | t.Errorf("Object(%s) = %q, want error", test.obj, path) |
| 88 | continue |
| 89 | } |
| 90 | if err.Error() != test.wantErr { |
| 91 | t.Errorf("Object(%s) error was %q, want %q", test.obj, err, test.wantErr) |
| 92 | continue |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | func TestGenericPaths_Issue51717(t *testing.T) { |
| 98 | pkgs := map[string]map[string]string{ |
| 99 | "p": {"p.go": ` |
| 100 | package p |
| 101 | |
| 102 | type S struct{} |
| 103 | |
| 104 | func (_ S) M() { |
| 105 | // The go vet stackoverflow crash disappears when the following line is removed |
| 106 | panic("") |
| 107 | } |
| 108 | |
| 109 | func F[WL interface{ N(item W) WL }, W any]() { |
| 110 | } |
| 111 | |
| 112 | func main() {} |
| 113 | `}, |
| 114 | } |
| 115 | paths := []pathTest{ |
| 116 | {"p", "F.T0CM0.RA0", "var WL", ""}, |
| 117 | {"p", "F.T0CM0.RA0.CM0", "func (interface).N(item W) WL", ""}, |
| 118 | |
| 119 | // Finding S.M0 reproduced the infinite recursion reported in #51717, |
| 120 | // because F is searched before S. |
| 121 | {"p", "S.M0", "func (p.S).M()", ""}, |
| 122 | } |
| 123 | |
| 124 | conf := loader.Config{Build: buildutil.FakeContext(pkgs)} |
| 125 | conf.Import("p") |
| 126 | prog, err := conf.Load() |
| 127 | if err != nil { |
| 128 | t.Fatal(err) |
| 129 | } |
| 130 | |
| 131 | for _, test := range paths { |
| 132 | if err := testPath(prog, test); err != nil { |
| 133 | t.Error(err) |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 |
Members