[dev.typeparams] go/types: import api_test.go changes from dev.go2go

This CL imports tests for the go/types API from the dev.go2go branch.
Only parse type parameters for packages with a magic prefix, with the
rationale that while generics are in preview, we want existing
(non-generic) tests to exercise the default mode.

Change-Id: I8ae0d8769b997a8a93b708453a1afaecb262244d
Reviewed-on: https://go-review.googlesource.com/c/go/+/284693
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Findley 2021-01-19 17:30:09 -05:00 committed by Robert Findley
parent 12cd9cf7e0
commit 626406b703

View file

@ -22,7 +22,8 @@ import (
func pkgFor(path, source string, info *Info) (*Package, error) { func pkgFor(path, source string, info *Info) (*Package, error) {
fset := token.NewFileSet() fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, source, 0) mode := modeForSource(source)
f, err := parser.ParseFile(fset, path, source, mode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -42,9 +43,21 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string {
return pkg.Name() return pkg.Name()
} }
func mayTypecheck(t *testing.T, path, source string, info *Info) string { // genericPkg is a prefix for packages that should be type checked with
// generics.
const genericPkg = "package generic_"
func modeForSource(src string) parser.Mode {
if strings.HasPrefix(src, genericPkg) {
return parser.ParseTypeParams
}
return 0
}
func mayTypecheck(t *testing.T, path, source string, info *Info) (string, error) {
fset := token.NewFileSet() fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, source, 0) mode := modeForSource(source)
f, err := parser.ParseFile(fset, path, source, mode)
if f == nil { // ignore errors unless f is nil if f == nil { // ignore errors unless f is nil
t.Fatalf("%s: unable to parse: %s", path, err) t.Fatalf("%s: unable to parse: %s", path, err)
} }
@ -52,8 +65,8 @@ func mayTypecheck(t *testing.T, path, source string, info *Info) string {
Error: func(err error) {}, Error: func(err error) {},
Importer: importer.Default(), Importer: importer.Default(),
} }
pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, info) pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
return pkg.Name() return pkg.Name(), err
} }
func TestValuesInfo(t *testing.T) { func TestValuesInfo(t *testing.T) {
@ -270,15 +283,31 @@ func TestTypesInfo(t *testing.T) {
// tests for broken code that doesn't parse or type-check // tests for broken code that doesn't parse or type-check
{`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`}, {`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
{`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`}, {`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
{`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`}, {`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a, f: b,}}`, `b`, `string`},
{`package x3; var x = panic("");`, `panic`, `func(interface{})`}, {`package x3; var x = panic("");`, `panic`, `func(interface{})`},
{`package x4; func _() { panic("") }`, `panic`, `func(interface{})`}, {`package x4; func _() { panic("") }`, `panic`, `func(interface{})`},
{`package x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`}, {`package x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
// parameterized functions
{genericPkg + `p0; func f[T any](T); var _ = f(int)`, `f`, `func[T₁ any](T₁)`},
{genericPkg + `p1; func f[T any](T); var _ = f(int)`, `f(int)`, `func(int)`},
{genericPkg + `p2; func f[T any](T); var _ = f(42)`, `f`, `func[T₁ any](T₁)`},
{genericPkg + `p2; func f[T any](T); var _ = f(42)`, `f(42)`, `()`},
// type parameters
{genericPkg + `t0; type t[] int; var _ t`, `t`, `generic_t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
{genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P₁ any]`},
{genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P₁ interface{}]`},
{genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P₁, Q₂ interface{}]`},
{genericPkg + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `generic_t4.t[P₁, Q₂ interface{m()}]`},
// instantiated types must be sanitized
{genericPkg + `g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `generic_g0.t[int]`},
} }
for _, test := range tests { for _, test := range tests {
info := Info{Types: make(map[ast.Expr]TypeAndValue)} info := Info{Types: make(map[ast.Expr]TypeAndValue)}
name := mayTypecheck(t, "TypesInfo", test.src, &info) name, _ := mayTypecheck(t, "TypesInfo", test.src, &info)
// look for expression type // look for expression type
var typ Type var typ Type
@ -300,6 +329,218 @@ func TestTypesInfo(t *testing.T) {
} }
} }
func TestInferredInfo(t *testing.T) {
var tests = []struct {
src string
fun string
targs []string
sig string
}{
{genericPkg + `p0; func f[T any](T); func _() { f(42) }`,
`f`,
[]string{`int`},
`func(int)`,
},
{genericPkg + `p1; func f[T any](T) T; func _() { f('@') }`,
`f`,
[]string{`rune`},
`func(rune) rune`,
},
{genericPkg + `p2; func f[T any](...T) T; func _() { f(0i) }`,
`f`,
[]string{`complex128`},
`func(...complex128) complex128`,
},
{genericPkg + `p3; func f[A, B, C any](A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`,
`f`,
[]string{`float64`, `string`, `byte`},
`func(float64, *string, []byte)`,
},
{genericPkg + `p4; func f[A, B any](A, *B, ...[]B); func _() { f(1.2, new(byte)) }`,
`f`,
[]string{`float64`, `byte`},
`func(float64, *byte, ...[]byte)`,
},
{genericPkg + `s1; func f[T any, P interface{type *T}](x T); func _(x string) { f(x) }`,
`f`,
[]string{`string`, `*string`},
`func(x string)`,
},
{genericPkg + `s2; func f[T any, P interface{type *T}](x []T); func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `*int`},
`func(x []int)`,
},
{genericPkg + `s3; type C[T any] interface{type chan<- T}; func f[T any, P C[T]](x []T); func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`},
`func(x []int)`,
},
{genericPkg + `s4; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T); func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func(x []int)`,
},
{genericPkg + `t1; func f[T any, P interface{type *T}]() T; func _() { _ = f[string] }`,
`f`,
[]string{`string`, `*string`},
`func() string`,
},
{genericPkg + `t2; type C[T any] interface{type chan<- T}; func f[T any, P C[T]]() []T; func _() { _ = f[int] }`,
`f`,
[]string{`int`, `chan<- int`},
`func() []int`,
},
{genericPkg + `t3; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T; func _() { _ = f[int] }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func() []int`,
},
}
for _, test := range tests {
info := Info{Inferred: make(map[ast.Expr]Inferred)}
name, err := mayTypecheck(t, "InferredInfo", test.src, &info)
if err != nil {
t.Errorf("package %s: %v", name, err)
continue
}
// look for inferred type arguments and signature
var targs []Type
var sig *Signature
for call, inf := range info.Inferred {
var fun ast.Expr
switch x := call.(type) {
case *ast.CallExpr:
fun = x.Fun
case *ast.IndexExpr:
fun = x.X
default:
panic(fmt.Sprintf("unexpected call expression type %T", call))
}
if ExprString(fun) == test.fun {
targs = inf.Targs
sig = inf.Sig
break
}
}
if targs == nil {
t.Errorf("package %s: no inferred information found for %s", name, test.fun)
continue
}
// check that type arguments are correct
if len(targs) != len(test.targs) {
t.Errorf("package %s: got %d type arguments; want %d", name, len(targs), len(test.targs))
continue
}
for i, targ := range targs {
if got := targ.String(); got != test.targs[i] {
t.Errorf("package %s, %d. type argument: got %s; want %s", name, i, got, test.targs[i])
continue
}
}
// check that signature is correct
if got := sig.String(); got != test.sig {
t.Errorf("package %s: got %s; want %s", name, got, test.sig)
}
}
}
func TestDefsInfo(t *testing.T) {
var tests = []struct {
src string
obj string
want string
}{
{`package p0; const x = 42`, `x`, `const p0.x untyped int`},
{`package p1; const x int = 42`, `x`, `const p1.x int`},
{`package p2; var x int`, `x`, `var p2.x int`},
{`package p3; type x int`, `x`, `type p3.x int`},
{`package p4; func f()`, `f`, `func p4.f()`},
// generic types must be sanitized
// (need to use sufficiently nested types to provoke unexpanded types)
{genericPkg + `g0; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
{genericPkg + `g1; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
{genericPkg + `g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
{genericPkg + `g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var generic_g3.g func(x struct{f generic_g3.t[string]})`},
}
for _, test := range tests {
info := Info{
Defs: make(map[*ast.Ident]Object),
}
name := mustTypecheck(t, "DefsInfo", test.src, &info)
// find object
var def Object
for id, obj := range info.Defs {
if id.Name == test.obj {
def = obj
break
}
}
if def == nil {
t.Errorf("package %s: %s not found", name, test.obj)
continue
}
if got := def.String(); got != test.want {
t.Errorf("package %s: got %s; want %s", name, got, test.want)
}
}
}
func TestUsesInfo(t *testing.T) {
var tests = []struct {
src string
obj string
want string
}{
{`package p0; func _() { _ = x }; const x = 42`, `x`, `const p0.x untyped int`},
{`package p1; func _() { _ = x }; const x int = 42`, `x`, `const p1.x int`},
{`package p2; func _() { _ = x }; var x int`, `x`, `var p2.x int`},
{`package p3; func _() { type _ x }; type x int`, `x`, `type p3.x int`},
{`package p4; func _() { _ = f }; func f()`, `f`, `func p4.f()`},
// generic types must be sanitized
// (need to use sufficiently nested types to provoke unexpanded types)
{genericPkg + `g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
{genericPkg + `g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
{genericPkg + `g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
{genericPkg + `g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func generic_g3.f(x struct{f generic_g3.t[string]})`},
}
for _, test := range tests {
info := Info{
Uses: make(map[*ast.Ident]Object),
}
name := mustTypecheck(t, "UsesInfo", test.src, &info)
// find object
var use Object
for id, obj := range info.Uses {
if id.Name == test.obj {
use = obj
break
}
}
if use == nil {
t.Errorf("package %s: %s not found", name, test.obj)
continue
}
if got := use.String(); got != test.want {
t.Errorf("package %s: got %s; want %s", name, got, test.want)
}
}
}
func TestImplicitsInfo(t *testing.T) { func TestImplicitsInfo(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)