1
0
mirror of https://github.com/golang/go synced 2024-11-18 19:34:41 -07:00

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:
Robert Griesemer 2013-07-31 09:33:46 -07:00
parent eae0511b70
commit e5cfd92deb
6 changed files with 128 additions and 11 deletions

87
go/types/api_test.go Normal file
View File

@ -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)
}
}
}

View File

@ -159,7 +159,8 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
// Start with rhs so we have expression types
// for declarations with implicit types.
var x operand
check.expr(&x, rhs[0])
rhs := rhs[0]
check.expr(&x, rhs)
if x.mode == invalid {
invalidateVars(lhs)
return
@ -171,7 +172,7 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
if l == r {
for i, lhs := range lhs {
x.mode = value
x.expr = rhs[0]
x.expr = rhs
x.typ = t.At(i).typ
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 {
// comma-ok expression
check.recordCommaOkType(rhs, x.typ)
x.mode = value
check.initVar(lhs[0], &x)
x.mode = value
x.expr = rhs
x.typ = Typ[UntypedBool]
check.initVar(lhs[1], &x)
return
@ -219,7 +223,8 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
if r == 1 {
// l > 1
var x operand
check.expr(&x, rhs[0])
rhs := rhs[0]
check.expr(&x, rhs)
if x.mode == invalid {
return
}
@ -230,7 +235,7 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
if l == r {
for i, lhs := range lhs {
x.mode = value
x.expr = rhs[0]
x.expr = rhs
x.typ = t.At(i).typ
check.assignVar(lhs, &x)
}
@ -240,10 +245,13 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
if x.mode == valueok && l == 2 {
// comma-ok expression
check.recordCommaOkType(rhs, x.typ)
x.mode = value
check.assignVar(lhs[0], &x)
x.mode = value
x.expr = rhs
x.typ = Typ[UntypedBool]
check.assignVar(lhs[1], &x)
return

View File

@ -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) {
assert(id != nil)
if m := check.Objects; m != nil {

View File

@ -133,7 +133,10 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
case *ast.TypeAssertExpr:
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:
writeExpr(buf, x.Fun)

View File

@ -146,6 +146,13 @@ func IsIdentical(x, y Type) bool {
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:
// Two function types are identical if they have the same number of parameters
// 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.
if y, ok := y.(*Signature); ok {
return x.isVariadic == y.isVariadic &&
identicalTypes(x.params, y.params) &&
identicalTypes(x.results, y.results)
identicalTuples(x.params, y.params) &&
identicalTuples(x.results, y.results)
}
case *Interface:
@ -189,9 +196,9 @@ func IsIdentical(x, y Type) bool {
return false
}
// identicalTypes returns true if both lists a and b have the
// same length and corresponding objects have identical types.
func identicalTypes(a, b *Tuple) bool {
// identicalTuples returns true if both tuples a and b have the
// same length and corresponding elements have identical types.
func identicalTuples(a, b *Tuple) bool {
if a.Len() != b.Len() {
return false
}

View File

@ -148,7 +148,7 @@ var testExprs = []testEntry{
{"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
{"[]int{1, 2, 3}", "(composite literal)"},
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
{"i.([]string)", "i.(...)"},
{"i.([]string)", "i.(<expr *ast.ArrayType>)"},
}
func TestExprs(t *testing.T) {