Skip to content

Commit 5b64c79

Browse files
committed
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
1 parent e8f4da7 commit 5b64c79

File tree

7 files changed

+104
-59
lines changed

7 files changed

+104
-59
lines changed

src/librustc/middle/typeck/check/mod.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
14971497
match ty::get(lhs_resolved_t).sty {
14981498
ty::ty_fn(_) => {
14991499
tcx.sess.span_note(
1500-
ex.span, ~"did you forget the 'do' keyword for the call?");
1500+
ex.span, ~"did you forget the `do` keyword for the call?");
15011501
}
15021502
_ => ()
15031503
}
@@ -2207,23 +2207,33 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
22072207
Some(ty::ty_fn(ref fty)) => {
22082208
match fcx.mk_subty(false, expr.span,
22092209
(*fty).sig.output, ty::mk_bool(tcx)) {
2210-
result::Ok(_) => (),
2210+
result::Ok(_) =>
2211+
ty::mk_fn(tcx, FnTyBase {
2212+
meta: (*fty).meta,
2213+
sig: FnSig {output: ty::mk_nil(tcx),
2214+
../*bad*/copy (*fty).sig}
2215+
}),
22112216
result::Err(_) => {
22122217
fcx.type_error_message(expr.span,
22132218
|actual| {
2214-
fmt!("a `loop` function's last argument \
2215-
should return `bool`, not `%s`", actual)
2219+
fmt!("A `for` loop iterator should expect a \
2220+
closure that returns `bool`. This iterator \
2221+
expects a closure that returns `%s`. %s",
2222+
actual, if ty::type_is_nil((*fty).sig.output) {
2223+
"\nDid you mean to use `do` instead of \
2224+
`for`?" } else { "" } )
22162225
},
22172226
(*fty).sig.output, None);
22182227
err_happened = true;
2228+
// Kind of a hack: create a function type with the result
2229+
// replaced with ty_err, to suppress derived errors.
2230+
let t = ty::replace_fn_return_type(tcx, ty::mk_fn(tcx,
2231+
copy *fty),
2232+
ty::mk_err(tcx));
22192233
fcx.write_ty(id, ty::mk_err(tcx));
2234+
t
22202235
}
22212236
}
2222-
ty::mk_fn(tcx, FnTyBase {
2223-
meta: (*fty).meta,
2224-
sig: FnSig {output: ty::mk_nil(tcx),
2225-
../*bad*/copy (*fty).sig}
2226-
})
22272237
}
22282238
_ =>
22292239
match expected {
@@ -2245,14 +2255,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
22452255
}
22462256
};
22472257
match b.node {
2248-
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
2249-
check_expr_fn(fcx, b, None,
2250-
decl, *body, ForLoop, Some(inner_ty));
2251-
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
2252-
capture::check_capture_clause(tcx, b.id, cap_clause);
2253-
}
2254-
// argh
2255-
_ => fail ~"expr_fn_block"
2258+
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
2259+
// If an error occurred, we pretend this isn't a for
2260+
// loop, so as to assign types to all nodes while also
2261+
// propagating ty_err throughout so as to suppress
2262+
// derived errors. If we passed in ForLoop in the
2263+
// error case, we'd potentially emit a spurious error
2264+
// message because of the indirect_ret_ty.
2265+
let fn_kind = if err_happened { Vanilla }
2266+
else { ForLoop };
2267+
check_expr_fn(fcx, b, None,
2268+
decl, *body, fn_kind, Some(inner_ty));
2269+
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
2270+
capture::check_capture_clause(tcx, b.id, cap_clause);
2271+
}
2272+
// argh
2273+
_ => fail ~"expr_fn_block"
22562274
}
22572275
let block_ty = structurally_resolved_type(
22582276
fcx, expr.span, fcx.node_ty(b.id));

src/libsyntax/parse/parser.rs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,43 +1660,45 @@ impl Parser {
16601660
// them as the lambda arguments
16611661
let e = self.parse_expr_res(RESTRICT_NO_BAR_OR_DOUBLEBAR_OP);
16621662
match e.node {
1663-
expr_call(f, args, false) => {
1664-
let block = self.parse_lambda_block_expr();
1665-
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1666-
ctor(block));
1667-
let args = vec::append(args, ~[last_arg]);
1668-
@expr {node: expr_call(f, args, true), .. *e}
1669-
}
1670-
expr_method_call(f, i, tps, args, false) => {
1671-
let block = self.parse_lambda_block_expr();
1672-
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1673-
ctor(block));
1674-
let args = vec::append(args, ~[last_arg]);
1675-
@expr {node: expr_method_call(f, i, tps, args, true), .. *e}
1676-
}
1677-
expr_field(f, i, tps) => {
1678-
let block = self.parse_lambda_block_expr();
1679-
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1680-
ctor(block));
1681-
@expr {node: expr_method_call(f, i, tps, ~[last_arg], true),
1682-
.. *e}
1683-
}
1684-
expr_path(*) | expr_call(*) | expr_method_call(*) |
1685-
expr_paren(*) => {
1686-
let block = self.parse_lambda_block_expr();
1687-
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1688-
ctor(block));
1689-
self.mk_expr(lo.lo, last_arg.span.hi,
1690-
expr_call(e, ~[last_arg], true))
1691-
}
1692-
_ => {
1693-
// There may be other types of expressions that can
1694-
// represent the callee in `for` and `do` expressions
1695-
// but they aren't represented by tests
1696-
debug!("sugary call on %?", e.node);
1697-
self.span_fatal(
1698-
lo, fmt!("`%s` must be followed by a block call", keyword));
1699-
}
1663+
expr_call(f, args, false) => {
1664+
let block = self.parse_lambda_block_expr();
1665+
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1666+
ctor(block));
1667+
let args = vec::append(args, ~[last_arg]);
1668+
self.mk_expr(lo.lo, block.span.hi, expr_call(f, args, true))
1669+
}
1670+
expr_method_call(f, i, tps, args, false) => {
1671+
let block = self.parse_lambda_block_expr();
1672+
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1673+
ctor(block));
1674+
let args = vec::append(args, ~[last_arg]);
1675+
self.mk_expr(lo.lo, block.span.hi,
1676+
expr_method_call(f, i, tps, args, true))
1677+
}
1678+
expr_field(f, i, tps) => {
1679+
let block = self.parse_lambda_block_expr();
1680+
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1681+
ctor(block));
1682+
self.mk_expr(lo.lo, block.span.hi,
1683+
expr_method_call(f, i, tps, ~[last_arg], true))
1684+
}
1685+
expr_path(*) | expr_call(*) | expr_method_call(*) |
1686+
expr_paren(*) => {
1687+
let block = self.parse_lambda_block_expr();
1688+
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
1689+
ctor(block));
1690+
self.mk_expr(lo.lo, last_arg.span.hi,
1691+
expr_call(e, ~[last_arg], true))
1692+
}
1693+
_ => {
1694+
// There may be other types of expressions that can
1695+
// represent the callee in `for` and `do` expressions
1696+
// but they aren't represented by tests
1697+
debug!("sugary call on %?", e.node);
1698+
self.span_fatal(
1699+
lo, fmt!("`%s` must be followed by a block call",
1700+
keyword));
1701+
}
17001702
}
17011703
}
17021704

src/test/compile-fail/bad-for-loop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010

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

src/test/compile-fail/issue-2817-2.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ fn main() {
1414
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
1515
false
1616
};
17-
for not_bool |_i| { //~ ERROR a `loop` function's last argument should return `bool`
18-
//~^ ERROR A for-loop body must return (), but
17+
for not_bool |_i| { //~ ERROR a `for` loop iterator should expect a closure that returns `bool`
1918
~"hi"
2019
};
2120
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
2221
~"hi"
2322
};
24-
for not_bool() |_i| { //~ ERROR a `loop` function's last argument
23+
for not_bool() |_i| { //~ ERROR a `for` loop iterator should expect a closure that returns `bool`
2524
};
2625
}

src/test/compile-fail/issue-3651-2.rs

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

src/test/compile-fail/issue-3651.rs

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

src/test/compile-fail/missing-do.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ fn foo(f: fn()) { f() }
1515
fn main() {
1616
~"" || 42; //~ ERROR binary operation || cannot be applied to type `~str`
1717
foo || {}; //~ ERROR binary operation || cannot be applied to type `extern fn(&fn())`
18-
//~^ NOTE did you forget the 'do' keyword for the call?
18+
//~^ NOTE did you forget the `do` keyword for the call?
1919
}

0 commit comments

Comments
 (0)