go.tools/go/types: record comma-ok results as tuples
- added corresponding api tests - support tuple comparison with IsIdentical This CL will require some adjustments to SSA. R=adonovan CC=golang-dev https://golang.org/cl/12024046
This commit is contained in:
parent
eae0511b70
commit
e5cfd92deb
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// TODO(gri) This file needs to be expanded significantly.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pkgFor(path string, source string, info *Info) (*Package, error) {
|
||||||
|
fset = token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fset, path, source, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
pkg, err := conf.Check(path, fset, []*ast.File{f}, info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommaOkTypes(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
src string
|
||||||
|
expr string // comma-ok expression string
|
||||||
|
typ Type // type of first comma-ok value
|
||||||
|
}{
|
||||||
|
{`package p; var x interface{}; var _, _ = x.(int)`,
|
||||||
|
`x.(int)`,
|
||||||
|
Typ[Int],
|
||||||
|
},
|
||||||
|
{`package p; var m map[string]complex128; var _, _ = m["foo"]`,
|
||||||
|
`m["foo"]`,
|
||||||
|
Typ[Complex128],
|
||||||
|
},
|
||||||
|
{`package p; var c chan string; var _, _ = <-c`,
|
||||||
|
`<-c`,
|
||||||
|
Typ[String],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
path := fmt.Sprintf("CommaOk%d", i)
|
||||||
|
|
||||||
|
// type-check
|
||||||
|
info := Info{Types: make(map[ast.Expr]Type)}
|
||||||
|
_, err := pkgFor(path, test.src, &info)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for comma-ok expression type
|
||||||
|
var typ Type
|
||||||
|
for e, t := range info.Types {
|
||||||
|
if exprString(e) == test.expr {
|
||||||
|
typ = t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ == nil {
|
||||||
|
t.Errorf("%s: no type found for %s", path, test.expr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that type is correct
|
||||||
|
got, _ := typ.(*Tuple)
|
||||||
|
want := NewTuple(
|
||||||
|
NewVar(token.NoPos, nil, "", test.typ),
|
||||||
|
NewVar(token.NoPos, nil, "", Typ[UntypedBool]),
|
||||||
|
)
|
||||||
|
if got == nil || !identicalTuples(got, want) {
|
||||||
|
t.Errorf("%s: got %s; want %s", path, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -159,7 +159,8 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||||
// Start with rhs so we have expression types
|
// Start with rhs so we have expression types
|
||||||
// for declarations with implicit types.
|
// for declarations with implicit types.
|
||||||
var x operand
|
var x operand
|
||||||
check.expr(&x, rhs[0])
|
rhs := rhs[0]
|
||||||
|
check.expr(&x, rhs)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
invalidateVars(lhs)
|
invalidateVars(lhs)
|
||||||
return
|
return
|
||||||
|
|
@ -171,7 +172,7 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||||
if l == r {
|
if l == r {
|
||||||
for i, lhs := range lhs {
|
for i, lhs := range lhs {
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.expr = rhs[0]
|
x.expr = rhs
|
||||||
x.typ = t.At(i).typ
|
x.typ = t.At(i).typ
|
||||||
check.initVar(lhs, &x)
|
check.initVar(lhs, &x)
|
||||||
}
|
}
|
||||||
|
|
@ -181,10 +182,13 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||||
|
|
||||||
if allowCommaOk && x.mode == valueok && l == 2 {
|
if allowCommaOk && x.mode == valueok && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
|
check.recordCommaOkType(rhs, x.typ)
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
check.initVar(lhs[0], &x)
|
check.initVar(lhs[0], &x)
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
|
x.expr = rhs
|
||||||
x.typ = Typ[UntypedBool]
|
x.typ = Typ[UntypedBool]
|
||||||
check.initVar(lhs[1], &x)
|
check.initVar(lhs[1], &x)
|
||||||
return
|
return
|
||||||
|
|
@ -219,7 +223,8 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||||
if r == 1 {
|
if r == 1 {
|
||||||
// l > 1
|
// l > 1
|
||||||
var x operand
|
var x operand
|
||||||
check.expr(&x, rhs[0])
|
rhs := rhs[0]
|
||||||
|
check.expr(&x, rhs)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +235,7 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||||
if l == r {
|
if l == r {
|
||||||
for i, lhs := range lhs {
|
for i, lhs := range lhs {
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.expr = rhs[0]
|
x.expr = rhs
|
||||||
x.typ = t.At(i).typ
|
x.typ = t.At(i).typ
|
||||||
check.assignVar(lhs, &x)
|
check.assignVar(lhs, &x)
|
||||||
}
|
}
|
||||||
|
|
@ -240,10 +245,13 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||||
|
|
||||||
if x.mode == valueok && l == 2 {
|
if x.mode == valueok && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
|
check.recordCommaOkType(rhs, x.typ)
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
check.assignVar(lhs[0], &x)
|
check.assignVar(lhs[0], &x)
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
|
x.expr = rhs
|
||||||
x.typ = Typ[UntypedBool]
|
x.typ = Typ[UntypedBool]
|
||||||
check.assignVar(lhs[1], &x)
|
check.assignVar(lhs[1], &x)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,18 @@ func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *checker) recordCommaOkType(x ast.Expr, typ Type) {
|
||||||
|
assert(x != nil && typ != nil)
|
||||||
|
if m := check.Types; m != nil {
|
||||||
|
assert(m[x] != nil) // should have been recorded already
|
||||||
|
pos := x.Pos()
|
||||||
|
m[x] = NewTuple(
|
||||||
|
NewVar(pos, check.pkg, "", typ),
|
||||||
|
NewVar(pos, check.pkg, "", Typ[UntypedBool]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (check *checker) recordObject(id *ast.Ident, obj Object) {
|
func (check *checker) recordObject(id *ast.Ident, obj Object) {
|
||||||
assert(id != nil)
|
assert(id != nil)
|
||||||
if m := check.Objects; m != nil {
|
if m := check.Objects; m != nil {
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,10 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
|
||||||
|
|
||||||
case *ast.TypeAssertExpr:
|
case *ast.TypeAssertExpr:
|
||||||
writeExpr(buf, x.X)
|
writeExpr(buf, x.X)
|
||||||
buf.WriteString(".(...)")
|
buf.WriteString(".(")
|
||||||
|
// TODO(gri) expand writeExpr so that types are not handled by default case
|
||||||
|
writeExpr(buf, x.Type)
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
writeExpr(buf, x.Fun)
|
writeExpr(buf, x.Fun)
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,13 @@ func IsIdentical(x, y Type) bool {
|
||||||
return IsIdentical(x.base, y.base)
|
return IsIdentical(x.base, y.base)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *Tuple:
|
||||||
|
// Two tuples types are identical if they have the same number of elements
|
||||||
|
// and corresponding elements have identical types.
|
||||||
|
if y, ok := y.(*Tuple); ok {
|
||||||
|
return identicalTuples(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
// Two function types are identical if they have the same number of parameters
|
// Two function types are identical if they have the same number of parameters
|
||||||
// and result values, corresponding parameter and result types are identical,
|
// and result values, corresponding parameter and result types are identical,
|
||||||
|
|
@ -153,8 +160,8 @@ func IsIdentical(x, y Type) bool {
|
||||||
// names are not required to match.
|
// names are not required to match.
|
||||||
if y, ok := y.(*Signature); ok {
|
if y, ok := y.(*Signature); ok {
|
||||||
return x.isVariadic == y.isVariadic &&
|
return x.isVariadic == y.isVariadic &&
|
||||||
identicalTypes(x.params, y.params) &&
|
identicalTuples(x.params, y.params) &&
|
||||||
identicalTypes(x.results, y.results)
|
identicalTuples(x.results, y.results)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
|
|
@ -189,9 +196,9 @@ func IsIdentical(x, y Type) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// identicalTypes returns true if both lists a and b have the
|
// identicalTuples returns true if both tuples a and b have the
|
||||||
// same length and corresponding objects have identical types.
|
// same length and corresponding elements have identical types.
|
||||||
func identicalTypes(a, b *Tuple) bool {
|
func identicalTuples(a, b *Tuple) bool {
|
||||||
if a.Len() != b.Len() {
|
if a.Len() != b.Len() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ var testExprs = []testEntry{
|
||||||
{"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
|
{"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
|
||||||
{"[]int{1, 2, 3}", "(composite literal)"},
|
{"[]int{1, 2, 3}", "(composite literal)"},
|
||||||
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
|
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
|
||||||
{"i.([]string)", "i.(...)"},
|
{"i.([]string)", "i.(<expr *ast.ArrayType>)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExprs(t *testing.T) {
|
func TestExprs(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue