aboutsummaryrefslogtreecommitdiff
path: root/refactor/satisfy/find_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'refactor/satisfy/find_test.go')
-rw-r--r--refactor/satisfy/find_test.go238
1 files changed, 238 insertions, 0 deletions
diff --git a/refactor/satisfy/find_test.go b/refactor/satisfy/find_test.go
new file mode 100644
index 000000000..35a1e87ca
--- /dev/null
+++ b/refactor/satisfy/find_test.go
@@ -0,0 +1,238 @@
+// 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
+}