]> git.proxmox.com Git - rustc.git/blame - src/librustc_passes/loops.rs
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / librustc_passes / loops.rs
CommitLineData
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 10use self::Context::*;
223e47cc 11
7453a54e 12use rustc::session::Session;
223e47cc 13
54a0048b 14use rustc::hir::map::Map;
476ff2be 15use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
94b46f34 16use rustc::hir::{self, Destination};
476ff2be 17use syntax::ast;
3157f602 18use syntax_pos::Span;
223e47cc 19
476ff2be
SL
20#[derive(Clone, Copy, PartialEq)]
21enum LoopKind {
22 Loop(hir::LoopSource),
23 WhileLoop,
24}
25
26impl 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)]
38enum Context {
5bcae85e 39 Normal,
476ff2be 40 Loop(LoopKind),
5bcae85e 41 Closure,
94b46f34 42 LabeledBlock,
1a4d82fc
JJ
43}
44
c34b1796 45#[derive(Copy, Clone)]
32a655c1 46struct 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 52pub 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
61impl<'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 178impl<'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}