Commit 25496407 authored by Robert Griesemer's avatar Robert Griesemer

go/types, go/exact: "vendor" go/types into std repo

This is a first step towards moving go/types from the tools
repo into the std repo. The files were brought over via the
added src/go/types.bash script for reproducability. The
script can be removed once all dependencies on go/types
have moved to the std repo go/types.

The script moved packages as follows:

- x/tools/go/types => go/types (type-checker)
- x/tools/go/exact => go/exact (constants)
- x/tools/go/gcimporter => go/types/internal/gcimporter

The gcimporter is needed to be able to run tests. go/types
should probably have some factory function to provide an
appropriate importer.

Some of the go/types tests fail for a handful of platforms
(windows and nacl). In order to keep this change "clean"
from manual changes, the next change will disable those
tests for now so we can move forward.

Change-Id: I448d8f7faa39ad2e04811911b699f7682627c224
Reviewed-on: https://go-review.googlesource.com/8530Reviewed-by: 's avatarRob Pike <r@golang.org>
parent 0def13ac
This diff is collapsed.
// 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.
package exact
import (
"go/token"
"strings"
"testing"
)
// TODO(gri) expand this test framework
var opTests = []string{
// unary operations
`+ 0 = 0`,
`+ ? = ?`,
`- 1 = -1`,
`- ? = ?`,
`^ 0 = -1`,
`^ ? = ?`,
`! true = false`,
`! false = true`,
`! ? = ?`,
// etc.
// binary operations
`"" + "" = ""`,
`"foo" + "" = "foo"`,
`"" + "bar" = "bar"`,
`"foo" + "bar" = "foobar"`,
`0 + 0 = 0`,
`0 + 0.1 = 0.1`,
`0 + 0.1i = 0.1i`,
`0.1 + 0.9 = 1`,
`1e100 + 1e100 = 2e100`,
`? + 0 = ?`,
`0 + ? = ?`,
`0 - 0 = 0`,
`0 - 0.1 = -0.1`,
`0 - 0.1i = -0.1i`,
`1e100 - 1e100 = 0`,
`? - 0 = ?`,
`0 - ? = ?`,
`0 * 0 = 0`,
`1 * 0.1 = 0.1`,
`1 * 0.1i = 0.1i`,
`1i * 1i = -1`,
`? * 0 = ?`,
`0 * ? = ?`,
`0 / 0 = "division_by_zero"`,
`10 / 2 = 5`,
`5 / 3 = 5/3`,
`5i / 3i = 5/3`,
`? / 0 = ?`,
`0 / ? = ?`,
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
`10 % 3 = 1`,
`? % 0 = ?`,
`0 % ? = ?`,
`0 & 0 = 0`,
`12345 & 0 = 0`,
`0xff & 0xf = 0xf`,
`? & 0 = ?`,
`0 & ? = ?`,
`0 | 0 = 0`,
`12345 | 0 = 12345`,
`0xb | 0xa0 = 0xab`,
`? | 0 = ?`,
`0 | ? = ?`,
`0 ^ 0 = 0`,
`1 ^ -1 = -2`,
`? ^ 0 = ?`,
`0 ^ ? = ?`,
`0 &^ 0 = 0`,
`0xf &^ 1 = 0xe`,
`1 &^ 0xf = 0`,
// etc.
// shifts
`0 << 0 = 0`,
`1 << 10 = 1024`,
`0 >> 0 = 0`,
`1024 >> 10 == 1`,
`? << 0 == ?`,
`? >> 10 == ?`,
// etc.
// comparisons
`false == false = true`,
`false == true = false`,
`true == false = false`,
`true == true = true`,
`false != false = false`,
`false != true = true`,
`true != false = true`,
`true != true = false`,
`"foo" == "bar" = false`,
`"foo" != "bar" = true`,
`"foo" < "bar" = false`,
`"foo" <= "bar" = false`,
`"foo" > "bar" = true`,
`"foo" >= "bar" = true`,
`0 == 0 = true`,
`0 != 0 = false`,
`0 < 10 = true`,
`10 <= 10 = true`,
`0 > 10 = false`,
`10 >= 10 = true`,
`1/123456789 == 1/123456789 == true`,
`1/123456789 != 1/123456789 == false`,
`1/123456789 < 1/123456788 == true`,
`1/123456788 <= 1/123456789 == false`,
`0.11 > 0.11 = false`,
`0.11 >= 0.11 = true`,
`? == 0 = false`,
`? != 0 = false`,
`? < 10 = false`,
`? <= 10 = false`,
`? > 10 = false`,
`? >= 10 = false`,
`0 == ? = false`,
`0 != ? = false`,
`0 < ? = false`,
`10 <= ? = false`,
`0 > ? = false`,
`10 >= ? = false`,
// etc.
}
func TestOps(t *testing.T) {
for _, test := range opTests {
a := strings.Split(test, " ")
i := 0 // operator index
var x, x0 Value
switch len(a) {
case 4:
// unary operation
case 5:
// binary operation
x, x0 = val(a[0]), val(a[0])
i = 1
default:
t.Errorf("invalid test case: %s", test)
continue
}
op, ok := optab[a[i]]
if !ok {
panic("missing optab entry for " + a[i])
}
y, y0 := val(a[i+1]), val(a[i+1])
got := doOp(x, op, y)
want := val(a[i+3])
if !eql(got, want) {
t.Errorf("%s: got %s; want %s", test, got, want)
}
if x0 != nil && !eql(x, x0) {
t.Errorf("%s: x changed to %s", test, x)
}
if !eql(y, y0) {
t.Errorf("%s: y changed to %s", test, y)
}
}
}
func eql(x, y Value) bool {
_, ux := x.(unknownVal)
_, uy := y.(unknownVal)
if ux || uy {
return ux == uy
}
return Compare(x, token.EQL, y)
}
// ----------------------------------------------------------------------------
// Support functions
func val(lit string) Value {
if len(lit) == 0 {
return MakeUnknown()
}
switch lit {
case "?":
return MakeUnknown()
case "true":
return MakeBool(true)
case "false":
return MakeBool(false)
}
tok := token.INT
switch first, last := lit[0], lit[len(lit)-1]; {
case first == '"' || first == '`':
tok = token.STRING
lit = strings.Replace(lit, "_", " ", -1)
case first == '\'':
tok = token.CHAR
case last == 'i':
tok = token.IMAG
default:
if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") {
tok = token.FLOAT
}
}
return MakeFromLiteral(lit, tok)
}
var optab = map[string]token.Token{
"!": token.NOT,
"+": token.ADD,
"-": token.SUB,
"*": token.MUL,
"/": token.QUO,
"%": token.REM,
"<<": token.SHL,
">>": token.SHR,
"&": token.AND,
"|": token.OR,
"^": token.XOR,
"&^": token.AND_NOT,
"==": token.EQL,
"!=": token.NEQ,
"<": token.LSS,
"<=": token.LEQ,
">": token.GTR,
">=": token.GEQ,
}
func panicHandler(v *Value) {
switch p := recover().(type) {
case nil:
// nothing to do
case string:
*v = MakeString(p)
case error:
*v = MakeString(p.Error())
default:
panic(p)
}
}
func doOp(x Value, op token.Token, y Value) (z Value) {
defer panicHandler(&z)
if x == nil {
return UnaryOp(op, y, -1)
}
switch op {
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
return MakeBool(Compare(x, op, y))
case token.SHL, token.SHR:
s, _ := Int64Val(y)
return Shift(x, op, uint(s))
default:
return BinaryOp(x, op, y)
}
}
// ----------------------------------------------------------------------------
// Other tests
var fracTests = []string{
"0 0 1",
"1 1 1",
"-1 -1 1",
"1.2 6 5",
"-0.991 -991 1000",
"1e100 1e100 1",
}
func TestFractions(t *testing.T) {
for _, test := range fracTests {
a := strings.Split(test, " ")
if len(a) != 3 {
t.Errorf("invalid test case: %s", test)
continue
}
x := val(a[0])
n := val(a[1])
d := val(a[2])
if got := Num(x); !eql(got, n) {
t.Errorf("%s: got num = %s; want %s", test, got, n)
}
if got := Denom(x); !eql(got, d) {
t.Errorf("%s: got denom = %s; want %s", test, got, d)
}
}
}
var bytesTests = []string{
"0",
"1",
"123456789",
"123456789012345678901234567890123456789012345678901234567890",
}
func TestBytes(t *testing.T) {
for _, test := range bytesTests {
x := val(test)
bytes := Bytes(x)
// special case 0
if Sign(x) == 0 && len(bytes) != 0 {
t.Errorf("%s: got %v; want empty byte slice", test, bytes)
}
if n := len(bytes); n > 0 && bytes[n-1] == 0 {
t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
}
if got := MakeFromBytes(bytes); !eql(got, x) {
t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
}
}
}
func TestUnknown(t *testing.T) {
u := MakeUnknown()
var values = []Value{
u,
MakeBool(false), // token.ADD ok below, operation is never considered
MakeString(""),
MakeInt64(1),
MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT),
MakeFloat64(1.2),
MakeImag(MakeFloat64(1.2)),
}
for _, val := range values {
x, y := val, u
for i := range [2]int{} {
if i == 1 {
x, y = y, x
}
if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
}
if got := Compare(x, token.EQL, y); got {
t.Errorf("%s == %s: got true; want false", x, y)
}
}
}
}
// Copyright 2014 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.
// +build !go1.4
package exact
import (
"math"
"math/big"
)
func ratToFloat32(x *big.Rat) (float32, bool) {
// Before 1.4, there's no Rat.Float32.
// Emulate it, albeit at the cost of
// imprecision in corner cases.
x64, exact := x.Float64()
x32 := float32(x64)
if math.IsInf(float64(x32), 0) {
exact = false
}
return x32, exact
}
// Copyright 2014 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.
// +build go1.4
package exact
import "math/big"
func ratToFloat32(x *big.Rat) (float32, bool) {
return x.Float32()
}
#!/bin/bash
# Copyright 2015 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.
# Run this script to update the packages ./exact and ./types
# in the $GOROOT/src/go directory. They are vendored from the
# original sources in x/tools. Imports are renamed as needed.
#
# Delete this script once go/exact and go/types don't exist anymore in x/tools.
set -e
### Safety first.
if [ ! -d "$GOPATH" ]; then
echo 2>&1 '$GOPATH must be set.'
exit 1
fi
if [ ! -d "$GOROOT" ]; then
echo 2>&1 '$GOROOT must be set.'
exit 1
fi
GODIR=$GOROOT/src/go
function vendor() (
SRCDIR=$GOPATH/src/golang.org/x/tools/$1
DSTDIR=$GODIR/$2
echo 2>&1 "vendoring $SRCDIR => $DSTDIR"
# create directory
rm -rf $DSTDIR
mkdir -p $DSTDIR
cd $DSTDIR
# copy go sources and update import paths
for f in $SRCDIR/*.go; do
# copy $f and update imports
sed -e 's|"golang.org/x/tools/go/exact"|"go/exact"|' \
-e 's|"golang.org/x/tools/go/types"|"go/types"|' \
-e 's|"golang.org/x/tools/go/gcimporter"|"go/types/internal/gcimporter"|' \
$f | gofmt > tmp.go
mv -f tmp.go `basename $f`
done
# copy testdata, if any
if [ -e $SRCDIR/testdata ]; then
cp -R $SRCDIR/testdata/ $DSTDIR/testdata/
fi
)
function install() (
PKG=$GODIR/$1
echo 2>&1 "installing $PKG"
cd $PKG
go install
)
function test() (
PKG=$GODIR/$1
echo 2>&1 "testing $PKG"
cd $PKG
if ! go test; then
echo 2>&1 "TESTING $PKG FAILED"
exit 1
fi
)
### go/exact
vendor go/exact exact
test exact
install exact
### go/types
vendor go/types types
# cannot test w/o gcimporter
install types
### go/gcimporter
vendor go/gcimporter types/internal/gcimporter
test types/internal/gcimporter
install types/internal/gcimporter
### test go/types (requires gcimporter)
test types
# All done.
echo 2>&1 "DONE"
exit 0
This diff is collapsed.
This diff is collapsed.
// 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.
// This file implements initialization and assignment checks.
package types
import (
"go/ast"
"go/token"
)
// assignment reports whether x can be assigned to a variable of type T,
// if necessary by attempting to convert untyped values to the appropriate
// type. If x.mode == invalid upon return, then assignment has already
// issued an error message and the caller doesn't have to report another.
// Use T == nil to indicate assignment to an untyped blank identifier.
//
// TODO(gri) Should find a better way to handle in-band errors.
//
func (check *Checker) assignment(x *operand, T Type) bool {
switch x.mode {
case invalid:
return true // error reported before
case constant, variable, mapindex, value, commaok:
// ok
default:
unreachable()
}
// x must be a single value
// (tuple types are never named - no need for underlying type)
if t, _ := x.typ.(*Tuple); t != nil {
assert(t.Len() > 1)
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
x.mode = invalid
return false
}
if isUntyped(x.typ) {
target := T
// spec: "If an untyped constant is assigned to a variable of interface
// type or the blank identifier, the constant is first converted to type
// bool, rune, int, float64, complex128 or string respectively, depending
// on whether the value is a boolean, rune, integer, floating-point, complex,
// or string constant."
if T == nil || IsInterface(T) {
if T == nil && x.typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil")
x.mode = invalid
return false
}
target = defaultType(x.typ)
}
check.convertUntyped(x, target)
if x.mode == invalid {
return false
}
}
// spec: "If a left-hand side is the blank identifier, any typed or
// non-constant value except for the predeclared identifier nil may
// be assigned to it."
return T == nil || x.assignableTo(check.conf, T)
}
func (check *Checker) initConst(lhs *Const, x *operand) {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return
}
// rhs must be a constant
if x.mode != constant {
check.errorf(x.pos(), "%s is not constant", x)
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return
}
assert(isConstType(x.typ))
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
lhs.typ = x.typ
}
if !check.assignment(x, lhs.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x)
}
return
}
lhs.val = x.val
}
// If result is set, lhs is a function result parameter and x is a return result.
func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return nil
}
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
typ := x.typ
if isUntyped(typ) {
// convert untyped types to default types
if typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil")
lhs.typ = Typ[Invalid]
return nil
}
typ = defaultType(typ)
}
lhs.typ = typ
}
if !check.assignment(x, lhs.typ) {
if x.mode != invalid {
if result {
// don't refer to lhs.name because it may be an anonymous result parameter
check.errorf(x.pos(), "cannot return %s as value of type %s", x, lhs.typ)
} else {
check.errorf(x.pos(), "cannot initialize %s with %s", lhs, x)
}
}
return nil
}
return x.typ
}
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] {
return nil
}
// Determine if the lhs is a (possibly parenthesized) identifier.
ident, _ := unparen(lhs).(*ast.Ident)
// Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Name == "_" {
check.recordDef(ident, nil)
if !check.assignment(x, nil) {
assert(x.mode == invalid)
x.typ = nil
}
return x.typ
}
// If the lhs is an identifier denoting a variable v, this assignment
// is not a 'use' of v. Remember current value of v.used and restore
// after evaluating the lhs via check.expr.
var v *Var
var v_used bool
if ident != nil {
if _, obj := check.scope.LookupParent(ident.Name); obj != nil {
v, _ = obj.(*Var)
if v != nil {
v_used = v.used
}
}
}
var z operand
check.expr(&z, lhs)
if v != nil {
v.used = v_used // restore v.used
}
if z.mode == invalid || z.typ == Typ[Invalid] {
return nil
}
// spec: "Each left-hand side operand must be addressable, a map index
// expression, or the blank identifier. Operands may be parenthesized."
switch z.mode {
case invalid:
return nil
case variable, mapindex:
// ok
default:
check.errorf(z.pos(), "cannot assign to %s", &z)
return nil
}
if !check.assignment(x, z.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
}
return nil
}
return x.typ
}
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
if get == nil || l != r {
// invalidate lhs and use rhs
for _, obj := range lhs {
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
}
if get == nil {
return // error reported by unpack
}
check.useGetter(get, r)
if returnPos.IsValid() {
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
return
}
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
return
}
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
}
check.recordCommaOkTypes(rhs[0], a)
return
}
for i, lhs := range lhs {
get(&x, i)
check.initVar(lhs, &x, returnPos.IsValid())
}
}
func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
if get == nil {
return // error reported by unpack
}
if l != r {
check.useGetter(get, r)
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
return
}
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.assignVar(lhs[i], &x)
}
check.recordCommaOkTypes(rhs[0], a)
return
}
for i, lhs := range lhs {
get(&x, i)
check.assignVar(lhs, &x)
}
}
func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
scope := check.scope
// collect lhs variables
var newVars []*Var
var lhsVars = make([]*Var, len(lhs))
for i, lhs := range lhs {
var obj *Var
if ident, _ := lhs.(*ast.Ident); ident != nil {
// Use the correct obj if the ident is redeclared. The
// variable's scope starts after the declaration; so we
// must use Scope.Lookup here and call Scope.Insert
// (via check.declare) later.
name := ident.Name
if alt := scope.Lookup(name); alt != nil {
// redeclared object must be a variable
if alt, _ := alt.(*Var); alt != nil {
obj = alt
} else {
check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
}
check.recordUse(ident, alt)
} else {
// declare new variable, possibly a blank (_) variable
obj = NewVar(ident.Pos(), check.pkg, name, nil)
if name != "_" {
newVars = append(newVars, obj)
}
check.recordDef(ident, obj)
}
} else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
}
if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
lhsVars[i] = obj
}
check.initVars(lhsVars, rhs, token.NoPos)
// declare new variables
if len(newVars) > 0 {
for _, obj := range newVars {
check.declare(scope, nil, obj) // recordObject already called
}
} else {
check.softErrorf(pos, "no new variables on left side of :=")
}
}
This diff is collapsed.
// 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.
package types_test
import (
"fmt"
"go/ast"
"go/parser"
"testing"
. "go/types"
_ "go/types/internal/gcimporter"
)
var builtinCalls = []struct {
name, src, sig string
}{
{"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
{"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
{"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
{"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
{"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`},
{"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`},
{"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`},
{"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
{"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
{"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
{"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
{"len", `_ = len("foo")`, `invalid type`}, // constant
{"len", `var s string; _ = len(s)`, `func(string) int`},
{"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant
{"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
{"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
{"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
{"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
{"close", `var c chan int; close(c)`, `func(chan int)`},
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
{"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
{"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
{"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
{"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
{"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
{"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
{"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`},
{"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`},
{"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`},
{"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`},
{"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
{"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
{"imag", `_ = imag(1i)`, `invalid type`}, // constant
{"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
{"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
{"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
{"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
{"real", `_ = real(1i)`, `invalid type`}, // constant
{"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
{"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
{"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
{"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
{"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
{"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
{"new", `_ = new(int)`, `func(int) *int`},
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
{"panic", `panic(0)`, `func(interface{})`},
{"panic", `panic("foo")`, `func(interface{})`},
{"print", `print()`, `func()`},
{"print", `print(0)`, `func(int)`},
{"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
{"println", `println()`, `func()`},
{"println", `println(0)`, `func(int)`},
{"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
{"recover", `recover()`, `func() interface{}`},
{"recover", `_ = recover()`, `func() interface{}`},
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
{"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
{"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
{"assert", `assert(true)`, `invalid type`}, // constant
{"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
// no tests for trace since it produces output as a side-effect
}
func TestBuiltinSignatures(t *testing.T) {
DefPredeclaredTestFuncs()
seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
for _, call := range builtinCalls {
testBuiltinSignature(t, call.name, call.src, call.sig)
seen[call.name] = true
}
// make sure we didn't miss one
for _, name := range Universe.Names() {
if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
t.Errorf("missing test for %s", name)
}
}
for _, name := range Unsafe.Scope().Names() {
if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] {
t.Errorf("missing test for unsafe.%s", name)
}
}
}
func testBuiltinSignature(t *testing.T, name, src0, want string) {
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
t.Errorf("%s: %s", src0, err)
return
}
var conf Config
uses := make(map[*ast.Ident]Object)
types := make(map[ast.Expr]TypeAndValue)
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
if err != nil {
t.Errorf("%s: %s", src0, err)
return
}
// find called function
n := 0
var fun ast.Expr
for x := range types {
if call, _ := x.(*ast.CallExpr); call != nil {
fun = call.Fun
n++
}
}
if n != 1 {
t.Errorf("%s: got %d CallExprs; want 1", src0, n)
return
}
// check recorded types for fun and descendents (may be parenthesized)
for {
// the recorded type for the built-in must match the wanted signature
typ := types[fun].Type
if typ == nil {
t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
return
}
if got := typ.String(); got != want {
t.Errorf("%s: got type %s; want %s", src0, got, want)
return
}
// called function must be a (possibly parenthesized, qualified)
// identifier denoting the expected built-in
switch p := fun.(type) {
case *ast.Ident:
obj := uses[p]
if obj == nil {
t.Errorf("%s: no object found for %s", src0, p)
return
}
bin, _ := obj.(*Builtin)
if bin == nil {
t.Errorf("%s: %s does not denote a built-in", src0, p)
return
}
if bin.Name() != name {
t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
return
}
return // we're done
case *ast.ParenExpr:
fun = p.X // unpack
case *ast.SelectorExpr:
// built-in from package unsafe - ignore details
return // we're done
default:
t.Errorf("%s: invalid function call", src0)
return
}
}
}
This diff is collapsed.
This diff is collapsed.
// Copyright 2011 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.
// This file implements a typechecker test harness. The packages specified
// in tests are typechecked. Error messages reported by the typechecker are
// compared against the error messages expected in the test files.
//
// Expected errors are indicated in the test files by putting a comment
// of the form /* ERROR "rx" */ immediately following an offending token.
// The harness will verify that an error matching the regular expression
// rx is reported at that source position. Consecutive comments may be
// used to indicate multiple errors for the same token position.
//
// For instance, the following test file indicates that a "not declared"
// error should be reported for the undeclared variable x:
//
// package p
// func f() {
// _ = x /* ERROR "not declared" */ + 1
// }
// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
// and test against strict mode.
package types_test
import (
"flag"
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"io/ioutil"
"regexp"
"strings"
"testing"
. "go/types"
_ "go/types/internal/gcimporter"
)
var (
listErrors = flag.Bool("list", false, "list errors")
testFiles = flag.String("files", "", "space-separated list of test files")
)
// The test filenames do not end in .go so that they are invisible
// to gofmt since they contain comments that must not change their
// positions relative to surrounding tokens.
// Each tests entry is list of files belonging to the same package.
var tests = [][]string{
{"testdata/errors.src"},
{"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
{"testdata/importdecl1a.src", "testdata/importdecl1b.src"},
{"testdata/cycles.src"},
{"testdata/cycles1.src"},
{"testdata/cycles2.src"},
{"testdata/cycles3.src"},
{"testdata/cycles4.src"},
{"testdata/init0.src"},
{"testdata/init1.src"},
{"testdata/init2.src"},
{"testdata/decls0.src"},
{"testdata/decls1.src"},
{"testdata/decls2a.src", "testdata/decls2b.src"},
{"testdata/decls3.src"},
{"testdata/const0.src"},
{"testdata/const1.src"},
{"testdata/constdecl.src"},
{"testdata/vardecl.src"},
{"testdata/expr0.src"},
{"testdata/expr1.src"},
{"testdata/expr2.src"},
{"testdata/expr3.src"},
{"testdata/methodsets.src"},
{"testdata/shifts.src"},
{"testdata/builtins.src"},
{"testdata/conversions.src"},
{"testdata/stmt0.src"},
{"testdata/stmt1.src"},
{"testdata/gotos.src"},
{"testdata/labels.src"},
{"testdata/issues.src"},
{"testdata/blank.src"},
}
var fset = token.NewFileSet()
// Positioned errors are of the form filename:line:column: message .
var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
// splitError splits an error's error message into a position string
// and the actual error message. If there's no position information,
// pos is the empty string, and msg is the entire error message.
//
func splitError(err error) (pos, msg string) {
msg = err.Error()
if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
pos = m[1]
msg = m[2]
}
return
}
func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
var files []*ast.File
var errlist []error
for _, filename := range filenames {
file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
if file == nil {
t.Fatalf("%s: %s", filename, err)
}
files = append(files, file)
if err != nil {
if list, _ := err.(scanner.ErrorList); len(list) > 0 {
for _, err := range list {
errlist = append(errlist, err)
}
} else {
errlist = append(errlist, err)
}
}
}
return files, errlist
}
// ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where
// rx is a regular expression that matches the expected error message.
// Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"`
// for error messages that are located immediately after rather than
// at a token's position.
//
var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
// errMap collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
//
func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
// map of position strings to lists of error message patterns
errmap := make(map[string][]string)
for _, file := range files {
filename := fset.Position(file.Package).Filename
src, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("%s: could not read %s", testname, filename)
}
var s scanner.Scanner
s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
var prev token.Pos // position of last non-comment, non-semicolon token
var here token.Pos // position immediately after the token at position prev
scanFile:
for {
pos, tok, lit := s.Scan()
switch tok {
case token.EOF:
break scanFile
case token.COMMENT:
if lit[1] == '*' {
lit = lit[:len(lit)-2] // strip trailing */
}
if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 {
pos := prev
if s[1] == "HERE" {
pos = here
}
p := fset.Position(pos).String()
errmap[p] = append(errmap[p], strings.TrimSpace(s[2]))
}
case token.SEMICOLON:
// ignore automatically inserted semicolon
if lit == "\n" {
continue scanFile
}
fallthrough
default:
prev = pos
var l int // token length
if tok.IsLiteral() {
l = len(lit)
} else {
l = len(tok.String())
}
here = prev + token.Pos(l)
}
}
}
return errmap
}
func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
for _, err := range errlist {
pos, gotMsg := splitError(err)
list := errmap[pos]
index := -1 // list index of matching message, if any
// we expect one of the messages in list to match the error at pos
for i, wantRx := range list {
rx, err := regexp.Compile(wantRx)
if err != nil {
t.Errorf("%s: %v", pos, err)
continue
}
if rx.MatchString(gotMsg) {
index = i
break
}
}
if index >= 0 {
// eliminate from list
if n := len(list) - 1; n > 0 {
// not the last entry - swap in last element and shorten list by 1
list[index] = list[n]
errmap[pos] = list[:n]
} else {
// last entry - remove list from map
delete(errmap, pos)
}
} else {
t.Errorf("%s: no error expected: %q", pos, gotMsg)
}
}
}
func checkFiles(t *testing.T, testfiles []string) {
// parse files and collect parser errors
files, errlist := parseFiles(t, testfiles)
pkgName := "<no package>"
if len(files) > 0 {
pkgName = files[0].Name.Name
}
if *listErrors && len(errlist) > 0 {
t.Errorf("--- %s:", pkgName)
for _, err := range errlist {
t.Error(err)
}
}
// typecheck and collect typechecker errors
var conf Config
conf.Error = func(err error) {
if *listErrors {
t.Error(err)
return
}
// Ignore secondary error messages starting with "\t";
// they are clarifying messages for a primary error.
if !strings.Contains(err.Error(), ": \t") {
errlist = append(errlist, err)
}
}
conf.Check(pkgName, fset, files, nil)
if *listErrors {
return
}
// match and eliminate errors;
// we are expecting the following errors
errmap := errMap(t, pkgName, files)
eliminate(t, errmap, errlist)
// there should be no expected errors left
if len(errmap) > 0 {
t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
for pos, list := range errmap {
for _, rx := range list {
t.Errorf("%s: %q", pos, rx)
}
}
}
}
func TestCheck(t *testing.T) {
// Declare builtins for testing.
DefPredeclaredTestFuncs()
// If explicit test files are specified, only check those.
if files := *testFiles; files != "" {
checkFiles(t, strings.Split(files, " "))
return
}
// Otherwise, run all the tests.
for _, files := range tests {
checkFiles(t, files)
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// 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.
// +build !go1.2
package types
import "go/ast"
func slice3(x *ast.SliceExpr) bool {
return false
}
func sliceMax(x *ast.SliceExpr) ast.Expr {
return nil
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment