1
Fork 0

syntax/rustc: Improve error message for misuse of for loop

Print out a clearer error message when a `for` gets
used with the wrong type of iterator. Also fix spans on `for` loop
bodies, and suppress some more derived errors.

r=brson

Closes #3651
This commit is contained in:
Tim Chevalier 2013-01-23 20:27:12 -08:00
parent e8f4da78e7
commit 5b64c796a4
7 changed files with 104 additions and 59 deletions

View file

@ -1497,7 +1497,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
match ty::get(lhs_resolved_t).sty {
ty::ty_fn(_) => {
tcx.sess.span_note(
ex.span, ~"did you forget the 'do' keyword for the call?");
ex.span, ~"did you forget the `do` keyword for the call?");
}
_ => ()
}
@ -2207,23 +2207,33 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
Some(ty::ty_fn(ref fty)) => {
match fcx.mk_subty(false, expr.span,
(*fty).sig.output, ty::mk_bool(tcx)) {
result::Ok(_) => (),
result::Err(_) => {
fcx.type_error_message(expr.span,
|actual| {
fmt!("a `loop` function's last argument \
should return `bool`, not `%s`", actual)
},
(*fty).sig.output, None);
err_happened = true;
fcx.write_ty(id, ty::mk_err(tcx));
}
}
result::Ok(_) =>
ty::mk_fn(tcx, FnTyBase {
meta: (*fty).meta,
sig: FnSig {output: ty::mk_nil(tcx),
../*bad*/copy (*fty).sig}
})
}),
result::Err(_) => {
fcx.type_error_message(expr.span,
|actual| {
fmt!("A `for` loop iterator should expect a \
closure that returns `bool`. This iterator \
expects a closure that returns `%s`. %s",
actual, if ty::type_is_nil((*fty).sig.output) {
"\nDid you mean to use `do` instead of \
`for`?" } else { "" } )
},
(*fty).sig.output, None);
err_happened = true;
// Kind of a hack: create a function type with the result
// replaced with ty_err, to suppress derived errors.
let t = ty::replace_fn_return_type(tcx, ty::mk_fn(tcx,
copy *fty),
ty::mk_err(tcx));
fcx.write_ty(id, ty::mk_err(tcx));
t
}
}
}
_ =>
match expected {
@ -2246,8 +2256,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
};
match b.node {
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
// If an error occurred, we pretend this isn't a for
// loop, so as to assign types to all nodes while also
// propagating ty_err throughout so as to suppress
// derived errors. If we passed in ForLoop in the
// error case, we'd potentially emit a spurious error
// message because of the indirect_ret_ty.
let fn_kind = if err_happened { Vanilla }
else { ForLoop };
check_expr_fn(fcx, b, None,
decl, *body, ForLoop, Some(inner_ty));
decl, *body, fn_kind, Some(inner_ty));
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
capture::check_capture_clause(tcx, b.id, cap_clause);
}

View file

@ -1665,21 +1665,22 @@ impl Parser {
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
ctor(block));
let args = vec::append(args, ~[last_arg]);
@expr {node: expr_call(f, args, true), .. *e}
self.mk_expr(lo.lo, block.span.hi, expr_call(f, args, true))
}
expr_method_call(f, i, tps, args, false) => {
let block = self.parse_lambda_block_expr();
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
ctor(block));
let args = vec::append(args, ~[last_arg]);
@expr {node: expr_method_call(f, i, tps, args, true), .. *e}
self.mk_expr(lo.lo, block.span.hi,
expr_method_call(f, i, tps, args, true))
}
expr_field(f, i, tps) => {
let block = self.parse_lambda_block_expr();
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
ctor(block));
@expr {node: expr_method_call(f, i, tps, ~[last_arg], true),
.. *e}
self.mk_expr(lo.lo, block.span.hi,
expr_method_call(f, i, tps, ~[last_arg], true))
}
expr_path(*) | expr_call(*) | expr_method_call(*) |
expr_paren(*) => {
@ -1695,7 +1696,8 @@ impl Parser {
// but they aren't represented by tests
debug!("sugary call on %?", e.node);
self.span_fatal(
lo, fmt!("`%s` must be followed by a block call", keyword));
lo, fmt!("`%s` must be followed by a block call",
keyword));
}
}
}

View file

@ -10,5 +10,5 @@
fn main() {
fn baz(_x: fn(y: int) -> int) {}
for baz |_e| { } //~ ERROR should return `bool`
for baz |_e| { } //~ ERROR A `for` loop iterator should expect a closure that returns `bool`
}

View file

@ -14,13 +14,12 @@ fn main() {
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
false
};
for not_bool |_i| { //~ ERROR a `loop` function's last argument should return `bool`
//~^ ERROR A for-loop body must return (), but
for not_bool |_i| { //~ ERROR a `for` loop iterator should expect a closure that returns `bool`
~"hi"
};
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
~"hi"
};
for not_bool() |_i| { //~ ERROR a `loop` function's last argument
for not_bool() |_i| { //~ ERROR a `for` loop iterator should expect a closure that returns `bool`
};
}

View file

@ -0,0 +1,13 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
do 5.times {} //~ ERROR Do-block body must return bool, but returns () here. Perhaps
}

View file

@ -0,0 +1,13 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
for task::spawn { return true; } //~ ERROR A `for` loop iterator should expect a closure that
}

View file

@ -15,5 +15,5 @@ fn foo(f: fn()) { f() }
fn main() {
~"" || 42; //~ ERROR binary operation || cannot be applied to type `~str`
foo || {}; //~ ERROR binary operation || cannot be applied to type `extern fn(&fn())`
//~^ NOTE did you forget the 'do' keyword for the call?
//~^ NOTE did you forget the `do` keyword for the call?
}