| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package satisfy_test |
| |
| import ( |
| "fmt" |
| "go/ast" |
| "go/importer" |
| "go/parser" |
| "go/token" |
| "go/types" |
| "reflect" |
| "sort" |
| "testing" |
| |
| "golang.org/x/tools/internal/typeparams" |
| "golang.org/x/tools/refactor/satisfy" |
| ) |
| |
| // This test exercises various operations on core types of type parameters. |
| // (It also provides pretty decent coverage of the non-generic operations.) |
| func TestGenericCoreOperations(t *testing.T) { |
| if !typeparams.Enabled { |
| t.Skip("!typeparams.Enabled") |
| } |
| |
| const src = `package foo |
| |
| import "unsafe" |
| |
| type I interface { f() } |
| |
| type impl struct{} |
| func (impl) f() {} |
| |
| // A big pile of single-serving types that implement I. |
| type A struct{impl} |
| type B struct{impl} |
| type C struct{impl} |
| type D struct{impl} |
| type E struct{impl} |
| type F struct{impl} |
| type G struct{impl} |
| type H struct{impl} |
| type J struct{impl} |
| type K struct{impl} |
| type L struct{impl} |
| type M struct{impl} |
| type N struct{impl} |
| type O struct{impl} |
| type P struct{impl} |
| type Q struct{impl} |
| type R struct{impl} |
| type S struct{impl} |
| type T struct{impl} |
| type U struct{impl} |
| type V struct{impl} |
| |
| type Generic[T any] struct{impl} |
| func (Generic[T]) g(T) {} |
| |
| type GI[T any] interface{ |
| g(T) |
| } |
| |
| func _[Slice interface{ []I }](s Slice) Slice { |
| s[0] = L{} // I <- L |
| return append(s, A{}) // I <- A |
| } |
| |
| func _[Func interface{ func(I) B }](fn Func) { |
| b := fn(C{}) // I <- C |
| var _ I = b // I <- B |
| } |
| |
| func _[Chan interface{ chan D }](ch Chan) { |
| var i I |
| for i = range ch {} // I <- D |
| _ = i |
| } |
| |
| func _[Chan interface{ chan E }](ch Chan) { |
| var _ I = <-ch // I <- E |
| } |
| |
| func _[Chan interface{ chan I }](ch Chan) { |
| ch <- F{} // I <- F |
| } |
| |
| func _[Map interface{ map[G]H }](m Map) { |
| var k, v I |
| for k, v = range m {} // I <- G, I <- H |
| _, _ = k, v |
| } |
| |
| func _[Map interface{ map[I]K }](m Map) { |
| var _ I = m[J{}] // I <- J, I <- K |
| delete(m, R{}) // I <- R |
| _, _ = m[J{}] |
| } |
| |
| func _[Array interface{ [1]I }](a Array) { |
| a[0] = M{} // I <- M |
| } |
| |
| func _[Array interface{ [1]N }](a Array) { |
| var _ I = a[0] // I <- N |
| } |
| |
| func _[Array interface{ [1]O }](a Array) { |
| var v I |
| for _, v = range a {} // I <- O |
| _ = v |
| } |
| |
| func _[ArrayPtr interface{ *[1]P }](a ArrayPtr) { |
| var v I |
| for _, v = range a {} // I <- P |
| _ = v |
| } |
| |
| func _[Slice interface{ []Q }](s Slice) { |
| var v I |
| for _, v = range s {} // I <- Q |
| _ = v |
| } |
| |
| func _[Func interface{ func() (S, bool) }](fn Func) { |
| var i I |
| i, _ = fn() // I <- S |
| _ = i |
| } |
| |
| func _() I { |
| var _ I = T{} // I <- T |
| var _ I = Generic[T]{} // I <- Generic[T] |
| var _ I = Generic[string]{} // I <- Generic[string] |
| return U{} // I <- U |
| } |
| |
| var _ GI[string] = Generic[string]{} // GI[string] <- Generic[string] |
| |
| // universally quantified constraints: |
| // the type parameter may appear on the left, the right, or both sides. |
| |
| func _[T any](g Generic[T]) GI[T] { |
| return g // GI[T] <- Generic[T] |
| } |
| |
| func _[T any]() { |
| type GI2[T any] interface{ g(string) } |
| var _ GI2[T] = Generic[string]{} // GI2[T] <- Generic[string] |
| } |
| |
| type Gen2[T any] struct{} |
| func (f Gen2[T]) g(string) { global = f } // GI[string] <- Gen2[T] |
| |
| var global GI[string] |
| |
| func _() { |
| var x [3]V |
| // golang/go#56227: the finder should visit calls in the unsafe package. |
| _ = unsafe.Slice(&x[0], func() int { var _ I = x[0]; return 3 }()) // I <- V |
| } |
| ` |
| got := constraints(t, src) |
| want := []string{ |
| "p.GI2[T] <- p.Generic[string]", // implicitly "forall T" quantified |
| "p.GI[T] <- p.Generic[T]", // implicitly "forall T" quantified |
| "p.GI[string] <- p.Gen2[T]", // implicitly "forall T" quantified |
| "p.GI[string] <- p.Generic[string]", |
| "p.I <- p.A", |
| "p.I <- p.B", |
| "p.I <- p.C", |
| "p.I <- p.D", |
| "p.I <- p.E", |
| "p.I <- p.F", |
| "p.I <- p.G", |
| "p.I <- p.Generic[p.T]", |
| "p.I <- p.Generic[string]", |
| "p.I <- p.H", |
| "p.I <- p.J", |
| "p.I <- p.K", |
| "p.I <- p.L", |
| "p.I <- p.M", |
| "p.I <- p.N", |
| "p.I <- p.O", |
| "p.I <- p.P", |
| "p.I <- p.Q", |
| "p.I <- p.R", |
| "p.I <- p.S", |
| "p.I <- p.T", |
| "p.I <- p.U", |
| "p.I <- p.V", |
| } |
| if !reflect.DeepEqual(got, want) { |
| t.Fatalf("found unexpected constraints: got %s, want %s", got, want) |
| } |
| } |
| |
| func constraints(t *testing.T, src string) []string { |
| // parse |
| fset := token.NewFileSet() |
| f, err := parser.ParseFile(fset, "p.go", src, 0) |
| if err != nil { |
| t.Fatal(err) // parse error |
| } |
| files := []*ast.File{f} |
| |
| // type-check |
| info := &types.Info{ |
| Types: make(map[ast.Expr]types.TypeAndValue), |
| Defs: make(map[*ast.Ident]types.Object), |
| Uses: make(map[*ast.Ident]types.Object), |
| Implicits: make(map[ast.Node]types.Object), |
| Scopes: make(map[ast.Node]*types.Scope), |
| Selections: make(map[*ast.SelectorExpr]*types.Selection), |
| } |
| typeparams.InitInstanceInfo(info) |
| conf := types.Config{ |
| Importer: importer.Default(), |
| } |
| if _, err := conf.Check("p", fset, files, info); err != nil { |
| t.Fatal(err) // type error |
| } |
| |
| // gather constraints |
| var finder satisfy.Finder |
| finder.Find(info, files) |
| var constraints []string |
| for c := range finder.Result { |
| constraints = append(constraints, fmt.Sprintf("%v <- %v", c.LHS, c.RHS)) |
| } |
| sort.Strings(constraints) |
| return constraints |
| } |