diff options
Diffstat (limited to 'refactor/satisfy/find_test.go')
-rw-r--r-- | refactor/satisfy/find_test.go | 238 |
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 +} |