| 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