go.tools/go/types: reduce spurious errors after missing identifiers

Fixes golang/go#8799.

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/143560043
This commit is contained in:
Robert Griesemer 2014-09-24 11:09:05 -07:00
parent 9cde9bdbd1
commit 37dd89e3af
7 changed files with 67 additions and 25 deletions

View File

@ -206,14 +206,17 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
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 l != r {
if get == nil || l != r {
// invalidate lhs and use rhs
for _, obj := range lhs {
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
}
check.use(rhs...)
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
@ -242,9 +245,12 @@ func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos)
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)
check.use(rhs...)
return
}

View File

@ -46,6 +46,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
default:
// make argument getter
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
if arg == nil {
x.mode = invalid
return
}
// evaluate first argument, if present
if nargs > 0 {
arg(x, 0)
@ -372,6 +376,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
if T == Typ[Invalid] {
return
}
var min int // minimum number of arguments
switch T.Underlying().(type) {
case *Slice:
@ -483,10 +488,12 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
check.use(arg0)
return
}
check.expr(x, selx.X)
if x.mode == invalid {
return
}
base := derefStructPtr(x.typ)
sel := selx.Sel.Name
obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)

View File

@ -62,6 +62,12 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
}
arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
if arg == nil {
x.mode = invalid
x.expr = e
return statement
}
check.arguments(x, e, sig, arg, n)
// determine result
@ -92,27 +98,39 @@ func (check *Checker) use(arg ...ast.Expr) {
}
}
// useGetter is like use, but takes a getter instead of a list of expressions.
// It should be called instead of use if a getter is present to avoid repeated
// evaluation of the first argument (since the getter was likely obtained via
// unpack, which may have evaluated the first argument already).
func (check *Checker) useGetter(get getter, n int) {
var x operand
for i := 0; i < n; i++ {
get(&x, i)
}
}
// A getter sets x as the i'th operand, where 0 <= i < n and n is the total
// number of operands (context-specific, and maintained elsewhere). A getter
// type-checks the i'th operand; the details of the actual check are getter-
// specific.
type getter func(x *operand, i int)
// unpack takes a getter get and a number of operands n. If n == 1 and the
// first operand is a function call, or a comma,ok expression and allowCommaOk
// is set, the result is a new getter and operand count providing access to the
// function results, or comma,ok values, respectively. The third result value
// reports if it is indeed the comma,ok case. In all other cases, the incoming
// getter and operand count are returned unchanged, and the third result value
// is false.
// unpack takes a getter get and a number of operands n. If n == 1, unpack
// calls the incoming getter for the first operand. If that operand is
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
// function call, or a comma-ok expression and allowCommaOk is set, the result
// is a new getter and operand count providing access to the function results,
// or comma-ok values, respectively. The third result value reports if it
// is indeed the comma-ok case. In all other cases, the incoming getter and
// operand count are returned unchanged, and the third result value is false.
//
// In other words, if there's exactly one operand that - after type-checking by
// calling get - stands for multiple operands, the resulting getter provides access
// to those operands instead.
// In other words, if there's exactly one operand that - after type-checking
// by calling get - stands for multiple operands, the resulting getter provides
// access to those operands instead.
//
// Note that unpack may call get(..., 0); but if the result getter is called
// at most once for a given operand index i (including i == 0), that operand
// is guaranteed to cause only one call of the incoming getter with that i.
// If the returned getter is called at most once for a given operand index i
// (including i == 0), that operand is guaranteed to cause only one call of
// the incoming getter with that i.
//
func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
if n == 1 {
@ -120,12 +138,7 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
var x0 operand
get(&x0, 0)
if x0.mode == invalid {
return func(x *operand, i int) {
if i != 0 {
unreachable()
}
x.mode = invalid
}, 1, false
return nil, 0, false
}
if t, ok := x0.typ.(*Tuple); ok {

View File

@ -116,10 +116,10 @@ var (
s2 = -s0 /* ERROR "not defined" */
s3 = !s0 /* ERROR "not defined" */
s4 = ^s0 /* ERROR "not defined" */
s5 = *s4 /* ERROR "cannot indirect" */
s5 = *s4
s6 = &s4
s7 = *s6
s8 = <-s7 /* ERROR "cannot receive" */
s8 = <-s7
// channel
ch chan int

View File

@ -424,7 +424,7 @@ func _calls() {
f2(3.14) /* ERROR "too few arguments" */
f2(3.14, "foo")
f2(x /* ERROR "cannot pass" */ , "foo")
f2(g0 /* ERROR "used as value" */ ()) /* ERROR "too few arguments" */
f2(g0 /* ERROR "used as value" */ ())
f2(g1 /* ERROR "cannot pass" */ ()) /* ERROR "too few arguments" */
f2(g2())

View File

@ -22,3 +22,16 @@ func issue8066() {
_ = float32(340282356779733661637539395458142568448 /* ERROR cannot convert */ )
)
}
// Check that a missing identifier doesn't lead to a spurious error cascade.
func issue8799a() {
x, ok := missing /* ERROR undeclared */ ()
_ = !ok
_ = x
}
func issue8799b(x int, ok bool) {
x, ok = missing /* ERROR undeclared */ ()
_ = !ok
_ = x
}

View File

@ -88,6 +88,9 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
case *Var:
obj.used = true
check.addDeclDep(obj)
if typ == Typ[Invalid] {
return
}
x.mode = variable
case *Func: