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:
parent
eae0511b70
commit
e5cfd92deb
87
go/types/api_test.go
Normal file
87
go/types/api_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user