Skip to content

Commit b7a41c7

Browse files
committed
For OutsideLoop we should not suggest add 'block label in if block, or we wiil get another err: block label not supported here.
fixes #123261
1 parent aa1c459 commit b7a41c7

6 files changed

+243
-41
lines changed

compiler/rustc_passes/src/errors.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ pub struct BreakInsideAsyncBlock<'a> {
10911091
pub struct OutsideLoop<'a> {
10921092
#[primary_span]
10931093
#[label]
1094-
pub span: Span,
1094+
pub spans: Vec<Span>,
10951095
pub name: &'a str,
10961096
pub is_break: bool,
10971097
#[subdiagnostic]
@@ -1103,7 +1103,7 @@ pub struct OutsideLoopSuggestion {
11031103
#[suggestion_part(code = "'block: ")]
11041104
pub block_span: Span,
11051105
#[suggestion_part(code = " 'block")]
1106-
pub break_span: Span,
1106+
pub break_spans: Vec<Span>,
11071107
}
11081108

11091109
#[derive(Diagnostic)]

compiler/rustc_passes/src/loops.rs

+101-20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::BTreeMap;
12
use Context::*;
23

34
use rustc_hir as hir;
@@ -24,22 +25,38 @@ enum Context {
2425
Closure(Span),
2526
AsyncClosure(Span),
2627
UnlabeledBlock(Span),
28+
IfUnlabeledBlock(Span),
2729
LabeledBlock,
2830
Constant,
2931
}
3032

31-
#[derive(Copy, Clone)]
33+
#[derive(Clone)]
34+
struct BlockInfo {
35+
name: String,
36+
spans: Vec<Span>,
37+
suggs: Vec<Span>,
38+
}
39+
40+
#[derive(Clone)]
3241
struct CheckLoopVisitor<'a, 'tcx> {
3342
sess: &'a Session,
3443
tcx: TyCtxt<'tcx>,
35-
cx: Context,
44+
// Used for diagnostic like when in a `if` block with some `break`s,
45+
// we should not suggest adding `'block` label in `if` block,
46+
// we can back to outer block and add label there.
47+
cx_stack: Vec<Context>,
48+
block_breaks: BTreeMap<Span, BlockInfo>,
3649
}
3750

3851
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
39-
tcx.hir().visit_item_likes_in_module(
40-
module_def_id,
41-
&mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal },
42-
);
52+
let mut check = CheckLoopVisitor {
53+
sess: tcx.sess,
54+
tcx,
55+
cx_stack: vec![Normal],
56+
block_breaks: Default::default(),
57+
};
58+
tcx.hir().visit_item_likes_in_module(module_def_id, &mut check);
59+
check.report_outside_loop_error();
4360
}
4461

4562
pub(crate) fn provide(providers: &mut Providers) {
@@ -82,6 +99,41 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
8299

83100
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
84101
match e.kind {
102+
hir::ExprKind::If(cond, then, else_opt) => {
103+
self.visit_expr(cond);
104+
if let hir::ExprKind::Block(ref b, None) = then.kind
105+
&& matches!(
106+
self.cx_stack.last(),
107+
Some(&Normal)
108+
| Some(&Constant)
109+
| Some(&UnlabeledBlock(_))
110+
| Some(&IfUnlabeledBlock(_))
111+
)
112+
{
113+
self.with_context(IfUnlabeledBlock(b.span.shrink_to_lo()), |v| {
114+
v.visit_block(b)
115+
});
116+
} else {
117+
self.visit_expr(then);
118+
}
119+
if let Some(else_expr) = else_opt {
120+
if let hir::ExprKind::Block(ref b, None) = else_expr.kind
121+
&& matches!(
122+
self.cx_stack.last(),
123+
Some(&Normal)
124+
| Some(&Constant)
125+
| Some(&UnlabeledBlock(_))
126+
| Some(&IfUnlabeledBlock(_))
127+
)
128+
{
129+
self.with_context(IfUnlabeledBlock(b.span.shrink_to_lo()), |v| {
130+
v.visit_block(b)
131+
});
132+
} else {
133+
self.visit_expr(else_expr);
134+
}
135+
}
136+
}
85137
hir::ExprKind::Loop(ref b, _, source, _) => {
86138
self.with_context(Loop(source), |v| v.visit_block(b));
87139
}
@@ -102,11 +154,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
102154
hir::ExprKind::Block(ref b, Some(_label)) => {
103155
self.with_context(LabeledBlock, |v| v.visit_block(b));
104156
}
105-
hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
157+
hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => {
106158
self.with_context(Normal, |v| v.visit_block(b));
107159
}
108160
hir::ExprKind::Block(ref b, None)
109-
if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
161+
if matches!(
162+
self.cx_stack.last(),
163+
Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_))
164+
) =>
110165
{
111166
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
112167
}
@@ -179,7 +234,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
179234
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
180235
None => sp_lo.shrink_to_lo(),
181236
};
182-
self.require_break_cx("break", e.span, label_sp);
237+
self.require_break_cx("break", e.span, label_sp, self.cx_stack.len() - 1);
183238
}
184239
hir::ExprKind::Continue(destination) => {
185240
self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -201,7 +256,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
201256
}
202257
Err(_) => {}
203258
}
204-
self.require_break_cx("continue", e.span, e.span)
259+
self.require_break_cx("continue", e.span, e.span, self.cx_stack.len() - 1)
205260
}
206261
_ => intravisit::walk_expr(self, e),
207262
}
@@ -213,15 +268,14 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
213268
where
214269
F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
215270
{
216-
let old_cx = self.cx;
217-
self.cx = cx;
271+
self.cx_stack.push(cx);
218272
f(self);
219-
self.cx = old_cx;
273+
self.cx_stack.pop();
220274
}
221275

222-
fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
276+
fn require_break_cx(&mut self, name: &str, span: Span, break_span: Span, cx_pos: usize) {
223277
let is_break = name == "break";
224-
match self.cx {
278+
match self.cx_stack[cx_pos] {
225279
LabeledBlock | Loop(_) => {}
226280
Closure(closure_span) => {
227281
self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
@@ -230,11 +284,24 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
230284
self.sess.dcx().emit_err(BreakInsideAsyncBlock { span, closure_span, name });
231285
}
232286
UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
233-
let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
234-
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion });
287+
let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo {
288+
name: name.to_string(),
289+
spans: vec![],
290+
suggs: vec![],
291+
});
292+
block.spans.push(span);
293+
block.suggs.push(break_span);
294+
}
295+
IfUnlabeledBlock(_) if is_break => {
296+
self.require_break_cx(name, span, break_span, cx_pos - 1);
235297
}
236-
Normal | Constant | Fn | UnlabeledBlock(_) => {
237-
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None });
298+
Normal | Constant | Fn | UnlabeledBlock(_) | IfUnlabeledBlock(_) => {
299+
self.sess.dcx().emit_err(OutsideLoop {
300+
spans: vec![span],
301+
name,
302+
is_break,
303+
suggestion: None,
304+
});
238305
}
239306
}
240307
}
@@ -246,12 +313,26 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
246313
cf_type: &str,
247314
) -> bool {
248315
if !span.is_desugaring(DesugaringKind::QuestionMark)
249-
&& self.cx == LabeledBlock
316+
&& self.cx_stack.last() == Some(&LabeledBlock)
250317
&& label.label.is_none()
251318
{
252319
self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
253320
return true;
254321
}
255322
false
256323
}
324+
325+
fn report_outside_loop_error(&mut self) {
326+
self.block_breaks.iter().for_each(|(s, block)| {
327+
self.sess.dcx().emit_err(OutsideLoop {
328+
spans: block.spans.clone(),
329+
name: &block.name,
330+
is_break: true,
331+
suggestion: Some(OutsideLoopSuggestion {
332+
block_span: *s,
333+
break_spans: block.suggs.clone(),
334+
}),
335+
});
336+
});
337+
}
257338
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)]
4+
5+
fn main() {
6+
let n = 1;
7+
let m = 2;
8+
let x = 'block: {
9+
if n == 0 {
10+
break 'block 1; //~ ERROR [E0268]
11+
} else {
12+
break 'block 2;
13+
}
14+
};
15+
16+
let y = 'block: {
17+
if n == 0 {
18+
break 'block 1; //~ ERROR [E0268]
19+
}
20+
break 'block 0;
21+
};
22+
23+
let z = 'block: {
24+
if n == 0 {
25+
if m > 1 {
26+
break 'block 3; //~ ERROR [E0268]
27+
}
28+
}
29+
break 'block 1;
30+
};
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)]
4+
5+
fn main() {
6+
let n = 1;
7+
let m = 2;
8+
let x = {
9+
if n == 0 {
10+
break 1; //~ ERROR [E0268]
11+
} else {
12+
break 2;
13+
}
14+
};
15+
16+
let y = {
17+
if n == 0 {
18+
break 1; //~ ERROR [E0268]
19+
}
20+
break 0;
21+
};
22+
23+
let z = {
24+
if n == 0 {
25+
if m > 1 {
26+
break 3; //~ ERROR [E0268]
27+
}
28+
}
29+
break 1;
30+
};
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error[E0268]: `break` outside of a loop or labeled block
2+
--> $DIR/loop-if-else-break-issue-123261.rs:10:13
3+
|
4+
LL | break 1;
5+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
6+
LL | } else {
7+
LL | break 2;
8+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
9+
|
10+
help: consider labeling this block to be able to break within it
11+
|
12+
LL ~ let x = 'block: {
13+
LL | if n == 0 {
14+
LL ~ break 'block 1;
15+
LL | } else {
16+
LL ~ break 'block 2;
17+
|
18+
19+
error[E0268]: `break` outside of a loop or labeled block
20+
--> $DIR/loop-if-else-break-issue-123261.rs:18:13
21+
|
22+
LL | break 1;
23+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
24+
LL | }
25+
LL | break 0;
26+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
27+
|
28+
help: consider labeling this block to be able to break within it
29+
|
30+
LL ~ let y = 'block: {
31+
LL | if n == 0 {
32+
LL ~ break 'block 1;
33+
LL | }
34+
LL ~ break 'block 0;
35+
|
36+
37+
error[E0268]: `break` outside of a loop or labeled block
38+
--> $DIR/loop-if-else-break-issue-123261.rs:26:17
39+
|
40+
LL | break 3;
41+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
42+
...
43+
LL | break 1;
44+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
45+
|
46+
help: consider labeling this block to be able to break within it
47+
|
48+
LL ~ let z = 'block: {
49+
LL | if n == 0 {
50+
LL | if m > 1 {
51+
LL ~ break 'block 3;
52+
LL | }
53+
LL | }
54+
LL ~ break 'block 1;
55+
|
56+
57+
error: aborting due to 3 previous errors
58+
59+
For more information about this error, try `rustc --explain E0268`.

0 commit comments

Comments
 (0)