1
Fork 0

Improve error message for breaks in blocks

Before it was always stated that it was a "break outside of a loop" when you
could very well be in a loop, but just in a block instead.

Closes #3064
This commit is contained in:
Alex Crichton 2013-11-11 11:29:15 -08:00
parent 0cc5e6c83f
commit 5fdbcc4020
3 changed files with 60 additions and 49 deletions

View file

@ -8,68 +8,68 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use middle::ty; use middle::ty;
use syntax::ast::*; use syntax::ast;
use syntax::visit; use syntax::codemap::Span;
use syntax::visit::Visitor; use syntax::visit::Visitor;
use syntax::visit;
#[deriving(Clone)] #[deriving(Clone, Eq)]
pub struct Context { enum Context {
in_loop: bool, Normal, Loop, Closure
can_ret: bool
} }
struct CheckLoopVisitor { struct CheckLoopVisitor {
tcx: ty::ctxt, tcx: ty::ctxt,
} }
pub fn check_crate(tcx: ty::ctxt, crate: &Crate) { pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
visit::walk_crate(&mut CheckLoopVisitor { tcx: tcx }, visit::walk_crate(&mut CheckLoopVisitor { tcx: tcx }, crate, Normal)
crate,
Context { in_loop: false, can_ret: true });
} }
impl Visitor<Context> for CheckLoopVisitor { impl Visitor<Context> for CheckLoopVisitor {
fn visit_item(&mut self, i:@item, _cx:Context) { fn visit_item(&mut self, i: @ast::item, _cx: Context) {
visit::walk_item(self, i, Context { visit::walk_item(self, i, Normal);
in_loop: false,
can_ret: true
});
} }
fn visit_expr(&mut self, e:@Expr, cx:Context) { fn visit_expr(&mut self, e: @ast::Expr, cx:Context) {
match e.node {
match e.node { ast::ExprWhile(e, ref b) => {
ExprWhile(e, ref b) => {
self.visit_expr(e, cx); self.visit_expr(e, cx);
self.visit_block(b, Context { in_loop: true,.. cx }); self.visit_block(b, Loop);
} }
ExprLoop(ref b, _) => { ast::ExprLoop(ref b, _) => {
self.visit_block(b, Context { in_loop: true,.. cx }); self.visit_block(b, Loop);
} }
ExprFnBlock(_, ref b) | ExprProc(_, ref b) => { ast::ExprFnBlock(_, ref b) | ast::ExprProc(_, ref b) => {
self.visit_block(b, Context { in_loop: false, can_ret: false }); self.visit_block(b, Closure);
} }
ExprBreak(_) => { ast::ExprBreak(_) => self.require_loop("break", cx, e.span),
if !cx.in_loop { ast::ExprAgain(_) => self.require_loop("continue", cx, e.span),
self.tcx.sess.span_err(e.span, "`break` outside of loop"); ast::ExprRet(oe) => {
} if cx == Closure {
} self.tcx.sess.span_err(e.span, "`return` in a closure");
ExprAgain(_) => {
if !cx.in_loop {
self.tcx.sess.span_err(e.span, "`loop` outside of loop");
}
}
ExprRet(oe) => {
if !cx.can_ret {
self.tcx.sess.span_err(e.span, "`return` in block function");
} }
visit::walk_expr_opt(self, oe, cx); visit::walk_expr_opt(self, oe, cx);
}
_ => visit::walk_expr(self, e, cx)
} }
_ => visit::walk_expr(self, e, cx)
}
}
}
impl CheckLoopVisitor {
fn require_loop(&self, name: &str, cx: Context, span: Span) {
match cx {
Loop => {}
Closure => {
self.tcx.sess.span_err(span, format!("`{}` inside of a closure",
name));
}
Normal => {
self.tcx.sess.span_err(span, format!("`{}` outside of loop",
name));
}
}
} }
} }

View file

@ -8,15 +8,26 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern:`break` outside of loop
struct Foo { struct Foo {
t: ~str t: ~str
} }
fn cond() -> bool { true }
fn foo(_: ||) {}
fn main() { fn main() {
let pth = break; let pth = break; //~ ERROR: `break` outside of loop
if cond() { continue } //~ ERROR: `continue` outside of loop
while cond() {
if cond() { break }
if cond() { continue }
do foo {
if cond() { break } //~ ERROR: `break` inside of a closure
if cond() { continue } //~ ERROR: `continue` inside of a closure
}
}
let rs: Foo = Foo{t: pth}; let rs: Foo = Foo{t: pth};
} }

View file

@ -10,6 +10,6 @@
fn main() { fn main() {
let _x = || { let _x = || {
return //~ ERROR: `return` in block function return //~ ERROR: `return` in a closure
}; };
} }