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:
parent
9cde9bdbd1
commit
37dd89e3af
|
|
@ -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) {
|
func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
|
||||||
l := len(lhs)
|
l := len(lhs)
|
||||||
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
|
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
|
// invalidate lhs and use rhs
|
||||||
for _, obj := range lhs {
|
for _, obj := range lhs {
|
||||||
if obj.typ == nil {
|
if obj.typ == nil {
|
||||||
obj.typ = Typ[Invalid]
|
obj.typ = Typ[Invalid]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.use(rhs...)
|
if get == nil {
|
||||||
|
return // error reported by unpack
|
||||||
|
}
|
||||||
|
check.useGetter(get, r)
|
||||||
if returnPos.IsValid() {
|
if returnPos.IsValid() {
|
||||||
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
|
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
|
||||||
return
|
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) {
|
func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
|
||||||
l := len(lhs)
|
l := len(lhs)
|
||||||
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
|
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 {
|
if l != r {
|
||||||
|
check.useGetter(get, r)
|
||||||
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||||
check.use(rhs...)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
default:
|
default:
|
||||||
// make argument getter
|
// make argument getter
|
||||||
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
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
|
// evaluate first argument, if present
|
||||||
if nargs > 0 {
|
if nargs > 0 {
|
||||||
arg(x, 0)
|
arg(x, 0)
|
||||||
|
|
@ -372,6 +376,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
if T == Typ[Invalid] {
|
if T == Typ[Invalid] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var min int // minimum number of arguments
|
var min int // minimum number of arguments
|
||||||
switch T.Underlying().(type) {
|
switch T.Underlying().(type) {
|
||||||
case *Slice:
|
case *Slice:
|
||||||
|
|
@ -483,10 +488,12 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
check.use(arg0)
|
check.use(arg0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
check.expr(x, selx.X)
|
check.expr(x, selx.X)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
base := derefStructPtr(x.typ)
|
base := derefStructPtr(x.typ)
|
||||||
sel := selx.Sel.Name
|
sel := selx.Sel.Name
|
||||||
obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
|
obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
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)
|
check.arguments(x, e, sig, arg, n)
|
||||||
|
|
||||||
// determine result
|
// 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
|
// 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
|
// number of operands (context-specific, and maintained elsewhere). A getter
|
||||||
// type-checks the i'th operand; the details of the actual check are getter-
|
// type-checks the i'th operand; the details of the actual check are getter-
|
||||||
// specific.
|
// specific.
|
||||||
type getter func(x *operand, i int)
|
type getter func(x *operand, i int)
|
||||||
|
|
||||||
// unpack takes a getter get and a number of operands n. If n == 1 and the
|
// unpack takes a getter get and a number of operands n. If n == 1, unpack
|
||||||
// first operand is a function call, or a comma,ok expression and allowCommaOk
|
// calls the incoming getter for the first operand. If that operand is
|
||||||
// is set, the result is a new getter and operand count providing access to the
|
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
|
||||||
// function results, or comma,ok values, respectively. The third result value
|
// function call, or a comma-ok expression and allowCommaOk is set, the result
|
||||||
// reports if it is indeed the comma,ok case. In all other cases, the incoming
|
// is a new getter and operand count providing access to the function results,
|
||||||
// getter and operand count are returned unchanged, and the third result value
|
// or comma-ok values, respectively. The third result value reports if it
|
||||||
// is false.
|
// 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
|
// In other words, if there's exactly one operand that - after type-checking
|
||||||
// calling get - stands for multiple operands, the resulting getter provides access
|
// by calling get - stands for multiple operands, the resulting getter provides
|
||||||
// to those operands instead.
|
// access to those operands instead.
|
||||||
//
|
//
|
||||||
// Note that unpack may call get(..., 0); but if the result getter is called
|
// If the returned getter is called at most once for a given operand index i
|
||||||
// at most once for a given operand index i (including i == 0), that operand
|
// (including i == 0), that operand is guaranteed to cause only one call of
|
||||||
// is guaranteed to cause only one call of the incoming getter with that i.
|
// the incoming getter with that i.
|
||||||
//
|
//
|
||||||
func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
|
func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
|
|
@ -120,12 +138,7 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
|
||||||
var x0 operand
|
var x0 operand
|
||||||
get(&x0, 0)
|
get(&x0, 0)
|
||||||
if x0.mode == invalid {
|
if x0.mode == invalid {
|
||||||
return func(x *operand, i int) {
|
return nil, 0, false
|
||||||
if i != 0 {
|
|
||||||
unreachable()
|
|
||||||
}
|
|
||||||
x.mode = invalid
|
|
||||||
}, 1, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, ok := x0.typ.(*Tuple); ok {
|
if t, ok := x0.typ.(*Tuple); ok {
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,10 @@ var (
|
||||||
s2 = -s0 /* ERROR "not defined" */
|
s2 = -s0 /* ERROR "not defined" */
|
||||||
s3 = !s0 /* ERROR "not defined" */
|
s3 = !s0 /* ERROR "not defined" */
|
||||||
s4 = ^s0 /* ERROR "not defined" */
|
s4 = ^s0 /* ERROR "not defined" */
|
||||||
s5 = *s4 /* ERROR "cannot indirect" */
|
s5 = *s4
|
||||||
s6 = &s4
|
s6 = &s4
|
||||||
s7 = *s6
|
s7 = *s6
|
||||||
s8 = <-s7 /* ERROR "cannot receive" */
|
s8 = <-s7
|
||||||
|
|
||||||
// channel
|
// channel
|
||||||
ch chan int
|
ch chan int
|
||||||
|
|
|
||||||
|
|
@ -424,7 +424,7 @@ func _calls() {
|
||||||
f2(3.14) /* ERROR "too few arguments" */
|
f2(3.14) /* ERROR "too few arguments" */
|
||||||
f2(3.14, "foo")
|
f2(3.14, "foo")
|
||||||
f2(x /* ERROR "cannot pass" */ , "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(g1 /* ERROR "cannot pass" */ ()) /* ERROR "too few arguments" */
|
||||||
f2(g2())
|
f2(g2())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,16 @@ func issue8066() {
|
||||||
_ = float32(340282356779733661637539395458142568448 /* ERROR cannot convert */ )
|
_ = 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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,9 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
|
||||||
case *Var:
|
case *Var:
|
||||||
obj.used = true
|
obj.used = true
|
||||||
check.addDeclDep(obj)
|
check.addDeclDep(obj)
|
||||||
|
if typ == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
x.mode = variable
|
x.mode = variable
|
||||||
|
|
||||||
case *Func:
|
case *Func:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue