blob: 35a1e87caf441bbf7946c5adefc1448dcff8586d [file] [log] [blame]
// 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
}