]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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. | |
1a4d82fc | 10 | use self::Context::*; |
223e47cc | 11 | |
7453a54e | 12 | use rustc::session::Session; |
223e47cc | 13 | |
54a0048b | 14 | use rustc::hir::map::Map; |
476ff2be | 15 | use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; |
94b46f34 | 16 | use rustc::hir::{self, Destination}; |
476ff2be | 17 | use syntax::ast; |
3157f602 | 18 | use syntax_pos::Span; |
223e47cc | 19 | |
476ff2be SL |
20 | #[derive(Clone, Copy, PartialEq)] |
21 | enum LoopKind { | |
22 | Loop(hir::LoopSource), | |
23 | WhileLoop, | |
24 | } | |
25 | ||
26 | impl LoopKind { | |
27 | fn name(self) -> &'static str { | |
28 | match self { | |
29 | LoopKind::Loop(hir::LoopSource::Loop) => "loop", | |
30 | LoopKind::Loop(hir::LoopSource::WhileLet) => "while let", | |
31 | LoopKind::Loop(hir::LoopSource::ForLoop) => "for", | |
32 | LoopKind::WhileLoop => "while", | |
33 | } | |
34 | } | |
35 | } | |
36 | ||
1a4d82fc JJ |
37 | #[derive(Clone, Copy, PartialEq)] |
38 | enum Context { | |
5bcae85e | 39 | Normal, |
476ff2be | 40 | Loop(LoopKind), |
5bcae85e | 41 | Closure, |
94b46f34 | 42 | LabeledBlock, |
1a4d82fc JJ |
43 | } |
44 | ||
c34b1796 | 45 | #[derive(Copy, Clone)] |
32a655c1 | 46 | struct CheckLoopVisitor<'a, 'hir: 'a> { |
1a4d82fc | 47 | sess: &'a Session, |
32a655c1 | 48 | hir_map: &'a Map<'hir>, |
5bcae85e | 49 | cx: Context, |
1a4d82fc JJ |
50 | } |
51 | ||
7453a54e | 52 | pub fn check_crate(sess: &Session, map: &Map) { |
7453a54e | 53 | let krate = map.krate(); |
476ff2be | 54 | krate.visit_all_item_likes(&mut CheckLoopVisitor { |
3b2f2976 | 55 | sess, |
476ff2be | 56 | hir_map: map, |
5bcae85e | 57 | cx: Normal, |
476ff2be | 58 | }.as_deep_visitor()); |
1a4d82fc JJ |
59 | } |
60 | ||
32a655c1 SL |
61 | impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { |
62 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'hir> { | |
476ff2be SL |
63 | NestedVisitorMap::OnlyBodies(&self.hir_map) |
64 | } | |
65 | ||
32a655c1 | 66 | fn visit_item(&mut self, i: &'hir hir::Item) { |
92a42be0 | 67 | self.with_context(Normal, |v| intravisit::walk_item(v, i)); |
1a4d82fc JJ |
68 | } |
69 | ||
32a655c1 | 70 | fn visit_impl_item(&mut self, i: &'hir hir::ImplItem) { |
476ff2be SL |
71 | self.with_context(Normal, |v| intravisit::walk_impl_item(v, i)); |
72 | } | |
73 | ||
32a655c1 | 74 | fn visit_expr(&mut self, e: &'hir hir::Expr) { |
1a4d82fc | 75 | match e.node { |
e9174d1e | 76 | hir::ExprWhile(ref e, ref b, _) => { |
476ff2be SL |
77 | self.with_context(Loop(LoopKind::WhileLoop), |v| { |
78 | v.visit_expr(&e); | |
79 | v.visit_block(&b); | |
80 | }); | |
81 | } | |
82 | hir::ExprLoop(ref b, _, source) => { | |
83 | self.with_context(Loop(LoopKind::Loop(source)), |v| v.visit_block(&b)); | |
1a4d82fc | 84 | } |
94b46f34 XL |
85 | hir::ExprClosure(_, ref function_decl, b, _, _) => { |
86 | self.visit_fn_decl(&function_decl); | |
32a655c1 | 87 | self.with_context(Closure, |v| v.visit_nested_body(b)); |
1a4d82fc | 88 | } |
94b46f34 XL |
89 | hir::ExprBlock(ref b, Some(_label)) => { |
90 | self.with_context(LabeledBlock, |v| v.visit_block(&b)); | |
91 | } | |
476ff2be | 92 | hir::ExprBreak(label, ref opt_expr) => { |
94b46f34 XL |
93 | opt_expr.as_ref().map(|e| self.visit_expr(e)); |
94 | ||
95 | if self.require_label_in_labeled_block(e.span, &label, "break") { | |
96 | // If we emitted an error about an unlabeled break in a labeled | |
97 | // block, we don't need any further checking for this break any more | |
98 | return; | |
99 | } | |
100 | ||
101 | let loop_id = match label.target_id.into() { | |
102 | Ok(loop_id) => loop_id, | |
103 | Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID, | |
104 | Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { | |
105 | self.emit_unlabled_cf_in_while_condition(e.span, "break"); | |
106 | ast::DUMMY_NODE_ID | |
107 | }, | |
108 | Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID, | |
8bb4bdeb XL |
109 | }; |
110 | ||
94b46f34 XL |
111 | if loop_id != ast::DUMMY_NODE_ID { |
112 | match self.hir_map.find(loop_id).unwrap() { | |
113 | hir::map::NodeBlock(_) => return, | |
114 | _=> (), | |
115 | } | |
116 | } | |
117 | ||
476ff2be | 118 | if opt_expr.is_some() { |
8bb4bdeb | 119 | let loop_kind = if loop_id == ast::DUMMY_NODE_ID { |
476ff2be | 120 | None |
8bb4bdeb XL |
121 | } else { |
122 | Some(match self.hir_map.expect_expr(loop_id).node { | |
123 | hir::ExprWhile(..) => LoopKind::WhileLoop, | |
124 | hir::ExprLoop(_, _, source) => LoopKind::Loop(source), | |
125 | ref r => span_bug!(e.span, | |
126 | "break label resolved to a non-loop: {:?}", r), | |
127 | }) | |
476ff2be SL |
128 | }; |
129 | match loop_kind { | |
94b46f34 XL |
130 | None | |
131 | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (), | |
476ff2be SL |
132 | Some(kind) => { |
133 | struct_span_err!(self.sess, e.span, E0571, | |
134 | "`break` with value from a `{}` loop", | |
135 | kind.name()) | |
136 | .span_label(e.span, | |
94b46f34 XL |
137 | "can only break with a value inside \ |
138 | `loop` or breakable block") | |
2c00a5a8 XL |
139 | .span_suggestion(e.span, |
140 | &format!("instead, use `break` on its own \ | |
141 | without a value inside this `{}` loop", | |
142 | kind.name()), | |
143 | "break".to_string()) | |
476ff2be SL |
144 | .emit(); |
145 | } | |
146 | } | |
147 | } | |
8bb4bdeb | 148 | |
94b46f34 | 149 | self.require_break_cx("break", e.span); |
1a4d82fc | 150 | } |
8bb4bdeb | 151 | hir::ExprAgain(label) => { |
94b46f34 XL |
152 | self.require_label_in_labeled_block(e.span, &label, "continue"); |
153 | ||
154 | match label.target_id { | |
155 | Ok(loop_id) => { | |
156 | if let hir::map::NodeBlock(block) = self.hir_map.find(loop_id).unwrap() { | |
157 | struct_span_err!(self.sess, e.span, E0696, | |
158 | "`continue` pointing to a labeled block") | |
159 | .span_label(e.span, | |
160 | "labeled blocks cannot be `continue`'d") | |
161 | .span_note(block.span, | |
162 | "labeled block the continue points to") | |
163 | .emit(); | |
164 | } | |
165 | } | |
166 | Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { | |
167 | self.emit_unlabled_cf_in_while_condition(e.span, "continue"); | |
168 | } | |
169 | _ => {} | |
8bb4bdeb | 170 | } |
94b46f34 | 171 | self.require_break_cx("continue", e.span) |
8bb4bdeb | 172 | }, |
5bcae85e | 173 | _ => intravisit::walk_expr(self, e), |
1a4d82fc JJ |
174 | } |
175 | } | |
223e47cc LB |
176 | } |
177 | ||
32a655c1 | 178 | impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { |
5bcae85e | 179 | fn with_context<F>(&mut self, cx: Context, f: F) |
32a655c1 | 180 | where F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>) |
1a4d82fc JJ |
181 | { |
182 | let old_cx = self.cx; | |
183 | self.cx = cx; | |
184 | f(self); | |
185 | self.cx = old_cx; | |
186 | } | |
187 | ||
94b46f34 | 188 | fn require_break_cx(&self, name: &str, span: Span) { |
1a4d82fc | 189 | match self.cx { |
94b46f34 | 190 | LabeledBlock | |
476ff2be | 191 | Loop(_) => {} |
1a4d82fc | 192 | Closure => { |
5bcae85e | 193 | struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name) |
7cac9316 | 194 | .span_label(span, "cannot break inside of a closure") |
5bcae85e | 195 | .emit(); |
1a4d82fc JJ |
196 | } |
197 | Normal => { | |
5bcae85e | 198 | struct_span_err!(self.sess, span, E0268, "`{}` outside of loop", name) |
7cac9316 | 199 | .span_label(span, "cannot break outside of a loop") |
5bcae85e | 200 | .emit(); |
223e47cc | 201 | } |
1a4d82fc JJ |
202 | } |
203 | } | |
8bb4bdeb | 204 | |
94b46f34 XL |
205 | fn require_label_in_labeled_block(&mut self, span: Span, label: &Destination, cf_type: &str) |
206 | -> bool | |
207 | { | |
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) | |
212 | .span_label(span, | |
213 | format!("`{}` statements that would diverge to or through \ | |
214 | a labeled block need to bear a label", cf_type)) | |
215 | .emit(); | |
216 | return true; | |
217 | } | |
218 | } | |
219 | return false; | |
220 | } | |
8bb4bdeb XL |
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") | |
224 | .span_label(span, | |
7cac9316 | 225 | format!("unlabeled `{}` in the condition of a `while` loop", cf_type)) |
8bb4bdeb XL |
226 | .emit(); |
227 | } | |
223e47cc | 228 | } |