1 | // Copyright 2022 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 satisfy_test |
6 | |
7 | import ( |
8 | "fmt" |
9 | "go/ast" |
10 | "go/importer" |
11 | "go/parser" |
12 | "go/token" |
13 | "go/types" |
14 | "reflect" |
15 | "sort" |
16 | "testing" |
17 | |
18 | "golang.org/x/tools/internal/typeparams" |
19 | "golang.org/x/tools/refactor/satisfy" |
20 | ) |
21 | |
22 | // This test exercises various operations on core types of type parameters. |
23 | // (It also provides pretty decent coverage of the non-generic operations.) |
24 | func TestGenericCoreOperations(t *testing.T) { |
25 | if !typeparams.Enabled { |
26 | t.Skip("!typeparams.Enabled") |
27 | } |
28 | |
29 | const src = `package foo |
30 | |
31 | import "unsafe" |
32 | |
33 | type I interface { f() } |
34 | |
35 | type impl struct{} |
36 | func (impl) f() {} |
37 | |
38 | // A big pile of single-serving types that implement I. |
39 | type A struct{impl} |
40 | type B struct{impl} |
41 | type C struct{impl} |
42 | type D struct{impl} |
43 | type E struct{impl} |
44 | type F struct{impl} |
45 | type G struct{impl} |
46 | type H struct{impl} |
47 | type J struct{impl} |
48 | type K struct{impl} |
49 | type L struct{impl} |
50 | type M struct{impl} |
51 | type N struct{impl} |
52 | type O struct{impl} |
53 | type P struct{impl} |
54 | type Q struct{impl} |
55 | type R struct{impl} |
56 | type S struct{impl} |
57 | type T struct{impl} |
58 | type U struct{impl} |
59 | type V struct{impl} |
60 | |
61 | type Generic[T any] struct{impl} |
62 | func (Generic[T]) g(T) {} |
63 | |
64 | type GI[T any] interface{ |
65 | g(T) |
66 | } |
67 | |
68 | func _[Slice interface{ []I }](s Slice) Slice { |
69 | s[0] = L{} // I <- L |
70 | return append(s, A{}) // I <- A |
71 | } |
72 | |
73 | func _[Func interface{ func(I) B }](fn Func) { |
74 | b := fn(C{}) // I <- C |
75 | var _ I = b // I <- B |
76 | } |
77 | |
78 | func _[Chan interface{ chan D }](ch Chan) { |
79 | var i I |
80 | for i = range ch {} // I <- D |
81 | _ = i |
82 | } |
83 | |
84 | func _[Chan interface{ chan E }](ch Chan) { |
85 | var _ I = <-ch // I <- E |
86 | } |
87 | |
88 | func _[Chan interface{ chan I }](ch Chan) { |
89 | ch <- F{} // I <- F |
90 | } |
91 | |
92 | func _[Map interface{ map[G]H }](m Map) { |
93 | var k, v I |
94 | for k, v = range m {} // I <- G, I <- H |
95 | _, _ = k, v |
96 | } |
97 | |
98 | func _[Map interface{ map[I]K }](m Map) { |
99 | var _ I = m[J{}] // I <- J, I <- K |
100 | delete(m, R{}) // I <- R |
101 | _, _ = m[J{}] |
102 | } |
103 | |
104 | func _[Array interface{ [1]I }](a Array) { |
105 | a[0] = M{} // I <- M |
106 | } |
107 | |
108 | func _[Array interface{ [1]N }](a Array) { |
109 | var _ I = a[0] // I <- N |
110 | } |
111 | |
112 | func _[Array interface{ [1]O }](a Array) { |
113 | var v I |
114 | for _, v = range a {} // I <- O |
115 | _ = v |
116 | } |
117 | |
118 | func _[ArrayPtr interface{ *[1]P }](a ArrayPtr) { |
119 | var v I |
120 | for _, v = range a {} // I <- P |
121 | _ = v |
122 | } |
123 | |
124 | func _[Slice interface{ []Q }](s Slice) { |
125 | var v I |
126 | for _, v = range s {} // I <- Q |
127 | _ = v |
128 | } |
129 | |
130 | func _[Func interface{ func() (S, bool) }](fn Func) { |
131 | var i I |
132 | i, _ = fn() // I <- S |
133 | _ = i |
134 | } |
135 | |
136 | func _() I { |
137 | var _ I = T{} // I <- T |
138 | var _ I = Generic[T]{} // I <- Generic[T] |
139 | var _ I = Generic[string]{} // I <- Generic[string] |
140 | return U{} // I <- U |
141 | } |
142 | |
143 | var _ GI[string] = Generic[string]{} // GI[string] <- Generic[string] |
144 | |
145 | // universally quantified constraints: |
146 | // the type parameter may appear on the left, the right, or both sides. |
147 | |
148 | func _[T any](g Generic[T]) GI[T] { |
149 | return g // GI[T] <- Generic[T] |
150 | } |
151 | |
152 | func _[T any]() { |
153 | type GI2[T any] interface{ g(string) } |
154 | var _ GI2[T] = Generic[string]{} // GI2[T] <- Generic[string] |
155 | } |
156 | |
157 | type Gen2[T any] struct{} |
158 | func (f Gen2[T]) g(string) { global = f } // GI[string] <- Gen2[T] |
159 | |
160 | var global GI[string] |
161 | |
162 | func _() { |
163 | var x [3]V |
164 | // golang/go#56227: the finder should visit calls in the unsafe package. |
165 | _ = unsafe.Slice(&x[0], func() int { var _ I = x[0]; return 3 }()) // I <- V |
166 | } |
167 | ` |
168 | got := constraints(t, src) |
169 | want := []string{ |
170 | "p.GI2[T] <- p.Generic[string]", // implicitly "forall T" quantified |
171 | "p.GI[T] <- p.Generic[T]", // implicitly "forall T" quantified |
172 | "p.GI[string] <- p.Gen2[T]", // implicitly "forall T" quantified |
173 | "p.GI[string] <- p.Generic[string]", |
174 | "p.I <- p.A", |
175 | "p.I <- p.B", |
176 | "p.I <- p.C", |
177 | "p.I <- p.D", |
178 | "p.I <- p.E", |
179 | "p.I <- p.F", |
180 | "p.I <- p.G", |
181 | "p.I <- p.Generic[p.T]", |
182 | "p.I <- p.Generic[string]", |
183 | "p.I <- p.H", |
184 | "p.I <- p.J", |
185 | "p.I <- p.K", |
186 | "p.I <- p.L", |
187 | "p.I <- p.M", |
188 | "p.I <- p.N", |
189 | "p.I <- p.O", |
190 | "p.I <- p.P", |
191 | "p.I <- p.Q", |
192 | "p.I <- p.R", |
193 | "p.I <- p.S", |
194 | "p.I <- p.T", |
195 | "p.I <- p.U", |
196 | "p.I <- p.V", |
197 | } |
198 | if !reflect.DeepEqual(got, want) { |
199 | t.Fatalf("found unexpected constraints: got %s, want %s", got, want) |
200 | } |
201 | } |
202 | |
203 | func constraints(t *testing.T, src string) []string { |
204 | // parse |
205 | fset := token.NewFileSet() |
206 | f, err := parser.ParseFile(fset, "p.go", src, 0) |
207 | if err != nil { |
208 | t.Fatal(err) // parse error |
209 | } |
210 | files := []*ast.File{f} |
211 | |
212 | // type-check |
213 | info := &types.Info{ |
214 | Types: make(map[ast.Expr]types.TypeAndValue), |
215 | Defs: make(map[*ast.Ident]types.Object), |
216 | Uses: make(map[*ast.Ident]types.Object), |
217 | Implicits: make(map[ast.Node]types.Object), |
218 | Scopes: make(map[ast.Node]*types.Scope), |
219 | Selections: make(map[*ast.SelectorExpr]*types.Selection), |
220 | } |
221 | typeparams.InitInstanceInfo(info) |
222 | conf := types.Config{ |
223 | Importer: importer.Default(), |
224 | } |
225 | if _, err := conf.Check("p", fset, files, info); err != nil { |
226 | t.Fatal(err) // type error |
227 | } |
228 | |
229 | // gather constraints |
230 | var finder satisfy.Finder |
231 | finder.Find(info, files) |
232 | var constraints []string |
233 | for c := range finder.Result { |
234 | constraints = append(constraints, fmt.Sprintf("%v <- %v", c.LHS, c.RHS)) |
235 | } |
236 | sort.Strings(constraints) |
237 | return constraints |
238 | } |
239 |
Members