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