about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-11-11 11:29:15 -0800
committerAlex Crichton <alex@alexcrichton.com>2013-11-12 09:40:21 -0800
commit5fdbcc4020a440e18a3c570ffad5d2bb089c08db (patch)
tree4cc65ef2f213253598b5ca2d55c0bcdcfa10fa67
parent0cc5e6c83f9d0f64ecd69cdca06511527f4f1554 (diff)
downloadrust-5fdbcc4020a440e18a3c570ffad5d2bb089c08db.tar.gz
rust-5fdbcc4020a440e18a3c570ffad5d2bb089c08db.zip
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
-rw-r--r--src/librustc/middle/check_loop.rs86
-rw-r--r--src/test/compile-fail/break-outside-loop.rs19
-rw-r--r--src/test/compile-fail/return-in-block-function.rs2
3 files changed, 59 insertions, 48 deletions
diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs
index a6aab151e5a..8a320f88649 100644
--- a/src/librustc/middle/check_loop.rs
+++ b/src/librustc/middle/check_loop.rs
@@ -8,68 +8,68 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
 use middle::ty;
 
-use syntax::ast::*;
-use syntax::visit;
+use syntax::ast;
+use syntax::codemap::Span;
 use syntax::visit::Visitor;
+use syntax::visit;
 
-#[deriving(Clone)]
-pub struct Context {
-    in_loop: bool,
-    can_ret: bool
+#[deriving(Clone, Eq)]
+enum Context {
+    Normal, Loop, Closure
 }
 
 struct CheckLoopVisitor {
     tcx: ty::ctxt,
 }
 
-pub fn check_crate(tcx: ty::ctxt, crate: &Crate) {
-    visit::walk_crate(&mut CheckLoopVisitor { tcx: tcx },
-                      crate,
-                      Context { in_loop: false, can_ret: true });
+pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
+    visit::walk_crate(&mut CheckLoopVisitor { tcx: tcx }, crate, Normal)
 }
 
 impl Visitor<Context> for CheckLoopVisitor {
-    fn visit_item(&mut self, i:@item, _cx:Context) {
-        visit::walk_item(self, i, Context {
-                                    in_loop: false,
-                                    can_ret: true
-                                  });
+    fn visit_item(&mut self, i: @ast::item, _cx: Context) {
+        visit::walk_item(self, i, Normal);
     }
 
-    fn visit_expr(&mut self, e:@Expr, cx:Context) {
-
-            match e.node {
-              ExprWhile(e, ref b) => {
+    fn visit_expr(&mut self, e: @ast::Expr, cx:Context) {
+        match e.node {
+            ast::ExprWhile(e, ref b) => {
                 self.visit_expr(e, cx);
-                self.visit_block(b, Context { in_loop: true,.. cx });
-              }
-              ExprLoop(ref b, _) => {
-                self.visit_block(b, Context { in_loop: true,.. cx });
-              }
-              ExprFnBlock(_, ref b) | ExprProc(_, ref b) => {
-                self.visit_block(b, Context { in_loop: false, can_ret: false });
-              }
-              ExprBreak(_) => {
-                if !cx.in_loop {
-                    self.tcx.sess.span_err(e.span, "`break` outside of loop");
-                }
-              }
-              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");
+                self.visit_block(b, Loop);
+            }
+            ast::ExprLoop(ref b, _) => {
+                self.visit_block(b, Loop);
+            }
+            ast::ExprFnBlock(_, ref b) | ast::ExprProc(_, ref b) => {
+                self.visit_block(b, Closure);
+            }
+            ast::ExprBreak(_) => self.require_loop("break", cx, e.span),
+            ast::ExprAgain(_) => self.require_loop("continue", cx, e.span),
+            ast::ExprRet(oe) => {
+                if cx == Closure {
+                    self.tcx.sess.span_err(e.span, "`return` in a closure");
                 }
                 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));
+            }
+        }
     }
 }
diff --git a/src/test/compile-fail/break-outside-loop.rs b/src/test/compile-fail/break-outside-loop.rs
index b3154c9742a..06281a5e288 100644
--- a/src/test/compile-fail/break-outside-loop.rs
+++ b/src/test/compile-fail/break-outside-loop.rs
@@ -8,15 +8,26 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:`break` outside of loop
-
 struct Foo {
     t: ~str
 }
 
+fn cond() -> bool { true }
+
+fn foo(_: ||) {}
+
 fn main() {
-    let pth = break;
+    let pth = break; //~ ERROR: `break` outside of loop
+    if cond() { continue } //~ ERROR: `continue` outside of loop
 
-    let rs: Foo = Foo{t: pth};
+    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};
 }
diff --git a/src/test/compile-fail/return-in-block-function.rs b/src/test/compile-fail/return-in-block-function.rs
index 75a72f97204..f231810cbf1 100644
--- a/src/test/compile-fail/return-in-block-function.rs
+++ b/src/test/compile-fail/return-in-block-function.rs
@@ -10,6 +10,6 @@
 
 fn main() {
     let _x = || {
-        return //~ ERROR: `return` in block function
+        return //~ ERROR: `return` in a closure
     };
 }