aboutsummaryrefslogtreecommitdiff
path: root/src/go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go')
-rw-r--r--src/go/ast/ast.go6
-rw-r--r--src/go/build/build.go6
-rw-r--r--src/go/types/api_test.go9
-rw-r--r--src/go/types/assignments.go17
-rw-r--r--src/go/types/check_test.go32
-rw-r--r--src/go/types/errors.go39
-rw-r--r--src/go/types/expr.go153
-rw-r--r--src/go/types/fixedbugs/issue23203a.src (renamed from src/go/types/testdata/issue23203a.src)0
-rw-r--r--src/go/types/fixedbugs/issue23203b.src (renamed from src/go/types/testdata/issue23203b.src)0
-rw-r--r--src/go/types/fixedbugs/issue26390.src (renamed from src/go/types/testdata/issue26390.src)2
-rw-r--r--src/go/types/fixedbugs/issue28251.src (renamed from src/go/types/testdata/issue28251.src)0
-rw-r--r--src/go/types/fixedbugs/issue6977.src (renamed from src/go/types/testdata/issue6977.src)0
-rw-r--r--src/go/types/operand.go43
-rw-r--r--src/go/types/self_test.go25
-rw-r--r--src/go/types/stdlib_test.go51
-rw-r--r--src/go/types/testdata/shifts.src23
16 files changed, 257 insertions, 149 deletions
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index 81c64589d0..1061f1d3ce 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -285,6 +285,12 @@ type (
}
// A BasicLit node represents a literal of basic type.
+ //
+ // Note that for the CHAR and STRING kinds, the literal is stored
+ // with its quotes. For example, for a double-quoted STRING, the
+ // first and the last rune in the Value field will be ". The
+ // Unquote and UnquoteChar functions in the strconv package can be
+ // used to unquote STRING and CHAR values, respectively.
BasicLit struct {
ValuePos token.Pos // literal position
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
diff --git a/src/go/build/build.go b/src/go/build/build.go
index 4a5da308a0..39bc3591a7 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -793,6 +793,12 @@ Found:
if d.IsDir() {
continue
}
+ if (d.Mode() & os.ModeSymlink) != 0 {
+ if fi, err := os.Stat(filepath.Join(p.Dir, d.Name())); err == nil && fi.IsDir() {
+ // Symlinks to directories are not source files.
+ continue
+ }
+ }
name := d.Name()
ext := nameExt(name)
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 798c09bbff..75cebc9826 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -1243,6 +1243,9 @@ func TestConvertibleTo(t *testing.T) {
{newDefined(new(Struct)), new(Struct), true},
{newDefined(Typ[Int]), new(Struct), false},
{Typ[UntypedInt], Typ[Int], true},
+ // Untyped string values are not permitted by the spec, so the below
+ // behavior is undefined.
+ {Typ[UntypedString], Typ[String], true},
} {
if got := ConvertibleTo(test.v, test.t); got != test.want {
t.Errorf("ConvertibleTo(%v, %v) = %t, want %t", test.v, test.t, got, test.want)
@@ -1260,6 +1263,12 @@ func TestAssignableTo(t *testing.T) {
{newDefined(Typ[Int]), Typ[Int], false},
{newDefined(new(Struct)), new(Struct), true},
{Typ[UntypedBool], Typ[Bool], true},
+ {Typ[UntypedString], Typ[Bool], false},
+ // Neither untyped string nor untyped numeric assignments arise during
+ // normal type checking, so the below behavior is technically undefined by
+ // the spec.
+ {Typ[UntypedString], Typ[String], true},
+ {Typ[UntypedInt], Typ[Int], true},
} {
if got := AssignableTo(test.v, test.t); got != test.want {
t.Errorf("AssignableTo(%v, %v) = %t, want %t", test.v, test.t, got, test.want)
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index 34a9d7843d..4e8ec278fc 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -7,6 +7,7 @@
package types
import (
+ "errors"
"go/ast"
"go/token"
)
@@ -33,8 +34,8 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
// 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."
+ // 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 in %s", context)
@@ -43,8 +44,16 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
}
target = Default(x.typ)
}
- check.convertUntyped(x, target)
- if x.mode == invalid {
+ if err := check.canConvertUntyped(x, target); err != nil {
+ var internalErr Error
+ var msg string
+ if errors.As(err, &internalErr) {
+ msg = internalErr.Msg
+ } else {
+ msg = err.Error()
+ }
+ check.errorf(x.pos(), "cannot use %s as %s value in %s: %v", x, target, context, msg)
+ x.mode = invalid
return
}
}
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
index 89122d75ff..e01c3de13b 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -34,6 +34,7 @@ import (
"go/token"
"internal/testenv"
"io/ioutil"
+ "path/filepath"
"regexp"
"strings"
"testing"
@@ -93,11 +94,6 @@ var tests = [][]string{
{"testdata/issues.src"},
{"testdata/blank.src"},
{"testdata/issue25008b.src", "testdata/issue25008a.src"}, // order (b before a) is crucial!
- {"testdata/issue26390.src"}, // stand-alone test to ensure case is triggered
- {"testdata/issue23203a.src"},
- {"testdata/issue23203b.src"},
- {"testdata/issue28251.src"},
- {"testdata/issue6977.src"},
}
var fset = token.NewFileSet()
@@ -259,7 +255,7 @@ func checkFiles(t *testing.T, testfiles []string) {
// typecheck and collect typechecker errors
var conf Config
// special case for importC.src
- if len(testfiles) == 1 && testfiles[0] == "testdata/importC.src" {
+ if len(testfiles) == 1 && strings.HasSuffix(testfiles[0], "importC.src") {
conf.FakeImportC = true
}
conf.Importer = importer.Default()
@@ -316,3 +312,27 @@ func TestCheck(t *testing.T) {
checkFiles(t, files)
}
}
+
+func TestFixedBugs(t *testing.T) { testDir(t, "fixedbugs") }
+
+func testDir(t *testing.T, dir string) {
+ testenv.MustHaveGoBuild(t)
+
+ fis, err := ioutil.ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, fi := range fis {
+ testname := filepath.Base(fi.Name())
+ testname = strings.TrimSuffix(testname, filepath.Ext(testname))
+ t.Run(testname, func(t *testing.T) {
+ filename := filepath.Join(dir, fi.Name())
+ if fi.IsDir() {
+ t.Errorf("skipped directory %q", filename)
+ return
+ }
+ checkFiles(t, []string{filename})
+ })
+ }
+}
diff --git a/src/go/types/errors.go b/src/go/types/errors.go
index 91b077163c..88e41c5713 100644
--- a/src/go/types/errors.go
+++ b/src/go/types/errors.go
@@ -7,6 +7,7 @@
package types
import (
+ "errors"
"fmt"
"go/ast"
"go/token"
@@ -72,22 +73,33 @@ func (check *Checker) dump(format string, args ...interface{}) {
fmt.Println(check.sprintf(format, args...))
}
-func (check *Checker) err(pos token.Pos, msg string, soft bool) {
+func (check *Checker) err(err error) {
+ if err == nil {
+ return
+ }
+ var e Error
+ isInternal := errors.As(err, &e)
// Cheap trick: Don't report errors with messages containing
// "invalid operand" or "invalid type" as those tend to be
// follow-on errors which don't add useful information. Only
// exclude them if these strings are not at the beginning,
// and only if we have at least one error already reported.
- if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
+ isInvalidErr := isInternal && (strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0)
+ if check.firstErr != nil && isInvalidErr {
return
}
- err := Error{check.fset, pos, msg, soft}
if check.firstErr == nil {
check.firstErr = err
}
if trace {
+ pos := e.Pos
+ msg := e.Msg
+ if !isInternal {
+ msg = err.Error()
+ pos = token.NoPos
+ }
check.trace(pos, "ERROR: %s", msg)
}
@@ -99,15 +111,30 @@ func (check *Checker) err(pos token.Pos, msg string, soft bool) {
}
func (check *Checker) error(pos token.Pos, msg string) {
- check.err(pos, msg, false)
+ check.err(Error{Fset: check.fset, Pos: pos, Msg: msg})
+}
+
+// newErrorf creates a new Error, but does not handle it.
+func (check *Checker) newErrorf(pos token.Pos, format string, args ...interface{}) error {
+ return Error{
+ Fset: check.fset,
+ Pos: pos,
+ Msg: check.sprintf(format, args...),
+ Soft: false,
+ }
}
func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
- check.err(pos, check.sprintf(format, args...), false)
+ check.error(pos, check.sprintf(format, args...))
}
func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
- check.err(pos, check.sprintf(format, args...), true)
+ check.err(Error{
+ Fset: check.fset,
+ Pos: pos,
+ Msg: check.sprintf(format, args...),
+ Soft: true,
+ })
}
func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index d1e892a9b7..94d98f0fbb 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -329,8 +329,16 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
return false
}
-// representable checks that a constant operand is representable in the given basic type.
+// representable checks that a constant operand is representable in the given
+// basic type.
func (check *Checker) representable(x *operand, typ *Basic) {
+ if err := check.isRepresentable(x, typ); err != nil {
+ x.mode = invalid
+ check.err(err)
+ }
+}
+
+func (check *Checker) isRepresentable(x *operand, typ *Basic) error {
assert(x.mode == constant_)
if !representableConst(x.val, check, typ, &x.val) {
var msg string
@@ -350,9 +358,9 @@ func (check *Checker) representable(x *operand, typ *Basic) {
} else {
msg = "cannot convert %s to %s"
}
- check.errorf(x.pos(), msg, x, typ)
- x.mode = invalid
+ return check.newErrorf(x.pos(), msg, x, typ)
}
+ return nil
}
// updateExprType updates the type of x to typ and invokes itself
@@ -488,12 +496,16 @@ func (check *Checker) updateExprVal(x ast.Expr, val constant.Value) {
// convertUntyped attempts to set the type of an untyped value to the target type.
func (check *Checker) convertUntyped(x *operand, target Type) {
- if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
- return
+ if err := check.canConvertUntyped(x, target); err != nil {
+ x.mode = invalid
+ check.err(err)
}
+}
- // TODO(gri) Sloppy code - clean up. This function is central
- // to assignment and expression checking.
+func (check *Checker) canConvertUntyped(x *operand, target Type) error {
+ if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
+ return nil
+ }
if isUntyped(target) {
// both x and target are untyped
@@ -505,82 +517,91 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
check.updateExprType(x.expr, target, false)
}
} else if xkind != tkind {
- goto Error
+ return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target)
}
- return
+ return nil
+ }
+
+ if t, ok := target.Underlying().(*Basic); ok && x.mode == constant_ {
+ if err := check.isRepresentable(x, t); err != nil {
+ return err
+ }
+ // Expression value may have been rounded - update if needed.
+ check.updateExprVal(x.expr, x.val)
+ } else {
+ newTarget := check.implicitType(x, target)
+ if newTarget == nil {
+ return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target)
+ }
+ target = newTarget
}
+ x.typ = target
+ // Even though implicitType can return UntypedNil, this value is final: the
+ // predeclared identifier nil has no type.
+ check.updateExprType(x.expr, target, true)
+ return nil
+}
- // typed target
+// implicitType returns the implicit type of x when used in a context where the
+// target type is expected. If no such implicit conversion is possible, it
+// returns nil.
+func (check *Checker) implicitType(x *operand, target Type) Type {
+ assert(isUntyped(x.typ))
switch t := target.Underlying().(type) {
case *Basic:
- if x.mode == constant_ {
- check.representable(x, t)
- if x.mode == invalid {
- return
+ assert(x.mode != constant_)
+ // Non-constant untyped values may appear as the
+ // result of comparisons (untyped bool), intermediate
+ // (delayed-checked) rhs operands of shifts, and as
+ // the value nil.
+ switch x.typ.(*Basic).kind {
+ case UntypedBool:
+ if !isBoolean(target) {
+ return nil
}
- // expression value may have been rounded - update if needed
- check.updateExprVal(x.expr, x.val)
- } else {
- // Non-constant untyped values may appear as the
- // result of comparisons (untyped bool), intermediate
- // (delayed-checked) rhs operands of shifts, and as
- // the value nil.
- switch x.typ.(*Basic).kind {
- case UntypedBool:
- if !isBoolean(target) {
- goto Error
- }
- case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
- if !isNumeric(target) {
- goto Error
- }
- case UntypedString:
- // Non-constant untyped string values are not
- // permitted by the spec and should not occur.
- unreachable()
- case UntypedNil:
- // Unsafe.Pointer is a basic type that includes nil.
- if !hasNil(target) {
- goto Error
- }
- default:
- goto Error
+ case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
+ if !isNumeric(target) {
+ return nil
+ }
+ case UntypedString:
+ // Non-constant untyped string values are not permitted by the spec and
+ // should not occur during normal typechecking passes, but this path is
+ // reachable via the AssignableTo API.
+ if !isString(target) {
+ return nil
+ }
+ case UntypedNil:
+ // Unsafe.Pointer is a basic type that includes nil.
+ if !hasNil(target) {
+ return nil
}
+ default:
+ return nil
}
case *Interface:
- // Update operand types to the default type rather then
- // the target (interface) type: values must have concrete
- // dynamic types. If the value is nil, keep it untyped
- // (this is important for tools such as go vet which need
- // the dynamic type for argument checking of say, print
+ // Values must have concrete dynamic types. If the value is nil,
+ // keep it untyped (this is important for tools such as go vet which
+ // need the dynamic type for argument checking of say, print
// functions)
if x.isNil() {
- target = Typ[UntypedNil]
- } else {
- // cannot assign untyped values to non-empty interfaces
- check.completeInterface(t)
- if !t.Empty() {
- goto Error
- }
- target = Default(x.typ)
+ return Typ[UntypedNil]
+ }
+ // cannot assign untyped values to non-empty interfaces
+ check.completeInterface(t)
+ if !t.Empty() {
+ return nil
}
+ return Default(x.typ)
case *Pointer, *Signature, *Slice, *Map, *Chan:
if !x.isNil() {
- goto Error
+ return nil
}
- // keep nil untyped - see comment for interfaces, above
- target = Typ[UntypedNil]
+ // Keep nil untyped - see comment for interfaces, above.
+ return Typ[UntypedNil]
default:
- goto Error
+ return nil
}
-
- x.typ = target
- check.updateExprType(x.expr, target, true) // UntypedNils are final
- return
-
-Error:
- check.errorf(x.pos(), "cannot convert %s to %s", x, target)
- x.mode = invalid
+ return target
}
func (check *Checker) comparison(x, y *operand, op token.Token) {
diff --git a/src/go/types/testdata/issue23203a.src b/src/go/types/fixedbugs/issue23203a.src
index 48cb5889cd..48cb5889cd 100644
--- a/src/go/types/testdata/issue23203a.src
+++ b/src/go/types/fixedbugs/issue23203a.src
diff --git a/src/go/types/testdata/issue23203b.src b/src/go/types/fixedbugs/issue23203b.src
index 638ec6c5ce..638ec6c5ce 100644
--- a/src/go/types/testdata/issue23203b.src
+++ b/src/go/types/fixedbugs/issue23203b.src
diff --git a/src/go/types/testdata/issue26390.src b/src/go/types/fixedbugs/issue26390.src
index b8e67e9bdd..9e0101f581 100644
--- a/src/go/types/testdata/issue26390.src
+++ b/src/go/types/fixedbugs/issue26390.src
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// stand-alone test to ensure case is triggered
+
package issue26390
type A = T
diff --git a/src/go/types/testdata/issue28251.src b/src/go/types/fixedbugs/issue28251.src
index cd79e0e8b5..cd79e0e8b5 100644
--- a/src/go/types/testdata/issue28251.src
+++ b/src/go/types/fixedbugs/issue28251.src
diff --git a/src/go/types/testdata/issue6977.src b/src/go/types/fixedbugs/issue6977.src
index 8f4e9ba2b2..8f4e9ba2b2 100644
--- a/src/go/types/testdata/issue6977.src
+++ b/src/go/types/fixedbugs/issue6977.src
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
index 80d11e2f21..6fbfe09627 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -205,15 +205,11 @@ func (x *operand) isNil() bool {
return x.mode == value && x.typ == Typ[UntypedNil]
}
-// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
-// checker.representable, and checker.assignment are
-// overlapping in functionality. Need to simplify and clean up.
-
-// assignableTo reports whether x is assignable to a variable of type T.
-// If the result is false and a non-nil reason is provided, it may be set
-// to a more detailed explanation of the failure (result != "").
-// The check parameter may be nil if assignableTo is invoked through
-// an exported API call, i.e., when all methods have been type-checked.
+// assignableTo reports whether x is assignable to a variable of type T. If the
+// result is false and a non-nil reason is provided, it may be set to a more
+// detailed explanation of the failure (result != ""). The check parameter may
+// be nil if assignableTo is invoked through an exported API call, i.e., when
+// all methods have been type-checked.
func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
if x.mode == invalid || T == Typ[Invalid] {
return true // avoid spurious errors
@@ -229,34 +225,17 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
Vu := V.Underlying()
Tu := T.Underlying()
- // x is an untyped value representable by a value of type T
- // TODO(gri) This is borrowing from checker.convertUntyped and
- // checker.representable. Need to clean up.
+ // x is an untyped value representable by a value of type T.
if isUntyped(Vu) {
- switch t := Tu.(type) {
- case *Basic:
- if x.isNil() && t.kind == UnsafePointer {
- return true
- }
- if x.mode == constant_ {
- return representableConst(x.val, check, t, nil)
- }
- // The result of a comparison is an untyped boolean,
- // but may not be a constant.
- if Vb, _ := Vu.(*Basic); Vb != nil {
- return Vb.kind == UntypedBool && isBoolean(Tu)
- }
- case *Interface:
- check.completeInterface(t)
- return x.isNil() || t.Empty()
- case *Pointer, *Signature, *Slice, *Map, *Chan:
- return x.isNil()
+ if t, ok := Tu.(*Basic); ok && x.mode == constant_ {
+ return representableConst(x.val, check, t, nil)
}
+ return check.implicitType(x, Tu) != nil
}
// Vu is typed
- // x's type V and T have identical underlying types
- // and at least one of V or T is not a named type
+ // x's type V and T have identical underlying types and at least one of V or
+ // T is not a named type.
if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
return true
}
diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go
index 10ad06fbca..04c9cd3458 100644
--- a/src/go/types/self_test.go
+++ b/src/go/types/self_test.go
@@ -47,8 +47,13 @@ func TestBenchmark(t *testing.T) {
// We're not using testing's benchmarking mechanism directly
// because we want custom output.
- for _, p := range []string{"types", "constant", filepath.Join("internal", "gcimporter")} {
- path := filepath.Join("..", p)
+ for _, p := range []string{
+ "net/http",
+ "go/parser",
+ "go/constant",
+ filepath.Join("go", "internal", "gcimporter"),
+ } {
+ path := filepath.Join("..", "..", p)
runbench(t, path, false)
runbench(t, path, true)
fmt.Println()
@@ -64,8 +69,13 @@ func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
b := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
- conf := Config{IgnoreFuncBodies: ignoreFuncBodies}
- conf.Check(path, fset, files, nil)
+ conf := Config{
+ IgnoreFuncBodies: ignoreFuncBodies,
+ Importer: importer.Default(),
+ }
+ if _, err := conf.Check(path, fset, files, nil); err != nil {
+ t.Fatal(err)
+ }
}
})
@@ -77,10 +87,9 @@ func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
})
d := time.Duration(b.NsPerOp())
- fmt.Printf(
- "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n",
- filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies,
- )
+ fmt.Printf("%s (ignoreFuncBodies = %v):\n", filepath.Base(path), ignoreFuncBodies)
+ fmt.Printf("\t%s for %d lines (%.0f lines/s)\n", d, lines, float64(lines)/d.Seconds())
+ fmt.Printf("\t%s\n", b.MemString())
}
func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) {
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 51ee0b1c36..f5a3273fa1 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -27,22 +27,21 @@ import (
. "go/types"
)
-var (
- pkgCount int // number of packages processed
- start time.Time
-
- // Use the same importer for all std lib tests to
- // avoid repeated importing of the same packages.
- stdLibImporter = importer.Default()
-)
+// Use the same importer for all std lib tests to
+// avoid repeated importing of the same packages.
+var stdLibImporter = importer.Default()
func TestStdlib(t *testing.T) {
testenv.MustHaveGoBuild(t)
- start = time.Now()
- walkDirs(t, filepath.Join(runtime.GOROOT(), "src"))
+ pkgCount := 0
+ duration := walkPkgDirs(filepath.Join(runtime.GOROOT(), "src"), func(dir string, filenames []string) {
+ typecheck(t, dir, filenames)
+ pkgCount++
+ }, t.Error)
+
if testing.Verbose() {
- fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
+ fmt.Println(pkgCount, "packages typechecked in", duration)
}
}
@@ -235,7 +234,6 @@ func typecheck(t *testing.T, path string, filenames []string) {
}
info := Info{Uses: make(map[*ast.Ident]Object)}
conf.Check(path, fset, files, &info)
- pkgCount++
// Perform checks of API invariants.
@@ -278,39 +276,48 @@ func pkgFilenames(dir string) ([]string, error) {
return filenames, nil
}
-// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
-// necessarily be shorter or clearer after adding the code to
-// terminate early for -short tests.
+func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...interface{})) time.Duration {
+ w := walker{time.Now(), 10 * time.Millisecond, pkgh, errh}
+ w.walk(dir)
+ return time.Since(w.start)
+}
-func walkDirs(t *testing.T, dir string) {
+type walker struct {
+ start time.Time
+ dmax time.Duration
+ pkgh func(dir string, filenames []string)
+ errh func(args ...interface{})
+}
+
+func (w *walker) walk(dir string) {
// limit run time for short tests
- if testing.Short() && time.Since(start) >= 10*time.Millisecond {
+ if testing.Short() && time.Since(w.start) >= w.dmax {
return
}
fis, err := ioutil.ReadDir(dir)
if err != nil {
- t.Error(err)
+ w.errh(err)
return
}
- // typecheck package in directory
+ // apply pkgh to the files in directory dir
// but ignore files directly under $GOROOT/src (might be temporary test files).
if dir != filepath.Join(runtime.GOROOT(), "src") {
files, err := pkgFilenames(dir)
if err != nil {
- t.Error(err)
+ w.errh(err)
return
}
if files != nil {
- typecheck(t, dir, files)
+ w.pkgh(dir, files)
}
}
// traverse subdirectories, but don't walk into testdata
for _, fi := range fis {
if fi.IsDir() && fi.Name() != "testdata" {
- walkDirs(t, filepath.Join(dir, fi.Name()))
+ w.walk(filepath.Join(dir, fi.Name()))
}
}
}
diff --git a/src/go/types/testdata/shifts.src b/src/go/types/testdata/shifts.src
index ebc95ba4d7..c9a38ae169 100644
--- a/src/go/types/testdata/shifts.src
+++ b/src/go/types/testdata/shifts.src
@@ -193,14 +193,27 @@ func shifts6() {
_ = float32(1.0 /* ERROR "must be integer" */ <<s)
_ = float32(1.1 /* ERROR "must be integer" */ <<s)
+ _ = int32(0x80000000 /* ERROR "overflows int32" */ << s)
+ // TODO(rfindley) Eliminate the redundant error here.
+ _ = int32(( /* ERROR "truncated to int32" */ 0x80000000 /* ERROR "truncated to int32" */ + 0i) << s)
+
+ _ = int(1+0i<<0)
+ _ = int((1+0i)<<s)
+ _ = int(1.0<<s)
+ _ = int(complex(1, 0)<<s)
+ _ = int(float32/* ERROR "must be integer" */(1.0) <<s)
+ _ = int(1.1 /* ERROR must be integer */ <<s)
+ _ = int(( /* ERROR "must be integer" */ 1+1i) <<s)
+
+ _ = complex(1 /* ERROR "must be integer" */ <<s, 0)
+
var b []int
_ = append(b, 1<<s)
_ = append(b, 1.0<<s)
+ _ = append(b, (1+0i)<<s)
_ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
-
- _ = append(b, 1<<s)
- _ = append(b, 1.0<<s) // should fail - see TODO in append code
- _ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
+ _ = append(b, (1 + 0i) <<s)
+ _ = append(b, ( /* ERROR "must be integer" */ 1 + 1i) <<s)
_ = complex(1.0 /* ERROR "must be integer" */ <<s, 0)
_ = complex(1.1 /* ERROR "must be integer" */ <<s, 0)
@@ -379,4 +392,4 @@ func issue22969() {
var _ int8 = 0xff /* ERROR "overflows int8" */ << s
var _ int16 = 0xffff /* ERROR "overflows int16" */ << s
var _ int32 = 0x80000000 /* ERROR "overflows int32" */ << s
-} \ No newline at end of file
+}