]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] go/types: import api_test.go changes from dev.go2go
authorRob Findley <rfindley@google.com>
Tue, 19 Jan 2021 22:30:09 +0000 (17:30 -0500)
committerRobert Findley <rfindley@google.com>
Fri, 22 Jan 2021 18:23:29 +0000 (18:23 +0000)
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>
src/go/types/api_test.go

index 75cebc98261124891abf04af07bf933f868ad6c8..014cd5282e6a1f8c21b91b2108a3d8881c9aa102 100644 (file)
@@ -22,7 +22,8 @@ import (
 
 func pkgFor(path, source string, info *Info) (*Package, error) {
        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 {
                return nil, err
        }
@@ -42,9 +43,21 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string {
        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()
-       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
                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) {},
                Importer: importer.Default(),
        }
-       pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
-       return pkg.Name()
+       pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+       return pkg.Name(), err
 }
 
 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
                {`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 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 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`},
+
+               // 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 {
                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
                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) {
        testenv.MustHaveGoBuild(t)