3 use rustc::session::Session;
5 use rustc::hir::map::Map;
6 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
7 use rustc::hir::{self, Node, Destination};
10 use errors::Applicability;
12 #[derive(Clone, Copy, Debug, PartialEq)]
14 Loop(hir::LoopSource),
19 fn name(self) -> &'static str {
21 LoopKind::Loop(hir::LoopSource::Loop) => "loop",
22 LoopKind::Loop(hir::LoopSource::WhileLet) => "while let",
23 LoopKind::Loop(hir::LoopSource::ForLoop) => "for",
24 LoopKind::WhileLoop => "while",
29 #[derive(Clone, Copy, Debug, PartialEq)]
38 #[derive(Copy, Clone)]
39 struct CheckLoopVisitor<'a, 'hir: 'a> {
41 hir_map: &'a Map<'hir>,
45 pub fn check_crate(sess: &Session, map: &Map) {
46 let krate = map.krate();
47 krate.visit_all_item_likes(&mut CheckLoopVisitor {
54 impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
55 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'hir> {
56 NestedVisitorMap::OnlyBodies(&self.hir_map)
59 fn visit_item(&mut self, i: &'hir hir::Item) {
60 self.with_context(Normal, |v| intravisit::walk_item(v, i));
63 fn visit_impl_item(&mut self, i: &'hir hir::ImplItem) {
64 self.with_context(Normal, |v| intravisit::walk_impl_item(v, i));
67 fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) {
68 self.with_context(AnonConst, |v| intravisit::walk_anon_const(v, c));
71 fn visit_expr(&mut self, e: &'hir hir::Expr) {
73 hir::ExprKind::While(ref e, ref b, _) => {
74 self.with_context(Loop(LoopKind::WhileLoop), |v| {
79 hir::ExprKind::Loop(ref b, _, source) => {
80 self.with_context(Loop(LoopKind::Loop(source)), |v| v.visit_block(&b));
82 hir::ExprKind::Closure(_, ref function_decl, b, _, _) => {
83 self.visit_fn_decl(&function_decl);
84 self.with_context(Closure, |v| v.visit_nested_body(b));
86 hir::ExprKind::Block(ref b, Some(_label)) => {
87 self.with_context(LabeledBlock, |v| v.visit_block(&b));
89 hir::ExprKind::Break(label, ref opt_expr) => {
90 opt_expr.as_ref().map(|e| self.visit_expr(e));
92 if self.require_label_in_labeled_block(e.span, &label, "break") {
93 // If we emitted an error about an unlabeled break in a labeled
94 // block, we don't need any further checking for this break any more
98 let loop_id = match label.target_id.into() {
99 Ok(loop_id) => loop_id,
100 Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
101 Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
102 self.emit_unlabled_cf_in_while_condition(e.span, "break");
105 Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
108 if loop_id != ast::DUMMY_NODE_ID {
109 if let Node::Block(_) = self.hir_map.find(loop_id).unwrap() {
114 if opt_expr.is_some() {
115 let loop_kind = if loop_id == ast::DUMMY_NODE_ID {
118 Some(match self.hir_map.expect_expr(loop_id).node {
119 hir::ExprKind::While(..) => LoopKind::WhileLoop,
120 hir::ExprKind::Loop(_, _, source) => LoopKind::Loop(source),
121 ref r => span_bug!(e.span,
122 "break label resolved to a non-loop: {:?}", r),
127 Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
129 struct_span_err!(self.sess, e.span, E0571,
130 "`break` with value from a `{}` loop",
133 "can only break with a value inside \
134 `loop` or breakable block")
135 .span_suggestion_with_applicability(
138 "instead, use `break` on its own \
139 without a value inside this `{}` loop",
143 Applicability::MaybeIncorrect,
150 self.require_break_cx("break", e.span);
152 hir::ExprKind::Continue(destination) => {
153 self.require_label_in_labeled_block(e.span, &destination, "continue");
155 match destination.target_id {
157 if let Node::Block(block) = self.hir_map.find(loop_id).unwrap() {
158 struct_span_err!(self.sess, e.span, E0696,
159 "`continue` pointing to a labeled block")
161 "labeled blocks cannot be `continue`'d")
162 .span_note(block.span,
163 "labeled block the continue points to")
167 Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
168 self.emit_unlabled_cf_in_while_condition(e.span, "continue");
172 self.require_break_cx("continue", e.span)
174 _ => intravisit::walk_expr(self, e),
179 impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
180 fn with_context<F>(&mut self, cx: Context, f: F)
181 where F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>)
183 let old_cx = self.cx;
189 fn require_break_cx(&self, name: &str, span: Span) {
191 LabeledBlock | Loop(_) => {}
193 struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name)
194 .span_label(span, "cannot break inside of a closure")
197 Normal | AnonConst => {
198 struct_span_err!(self.sess, span, E0268, "`{}` outside of loop", name)
199 .span_label(span, "cannot break outside of a loop")
205 fn require_label_in_labeled_block(&mut self, span: Span, label: &Destination, cf_type: &str)
208 if self.cx == LabeledBlock {
209 if label.label.is_none() {
210 struct_span_err!(self.sess, span, E0695,
211 "unlabeled `{}` inside of a labeled block", cf_type)
213 format!("`{}` statements that would diverge to or through \
214 a labeled block need to bear a label", cf_type))
221 fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) {
222 struct_span_err!(self.sess, span, E0590,
223 "`break` or `continue` with no label in the condition of a `while` loop")
225 format!("unlabeled `{}` in the condition of a `while` loop", cf_type))