Skip to content

Commit 8dce3a9

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 rust-lang#123261
1 parent aa1c459 commit 8dce3a9

6 files changed

+242
-39
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

+102-20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_data_structures::fx::FxHashMap;
12
use Context::*;
23

34
use rustc_hir as hir;
@@ -24,22 +25,40 @@ 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+
blocks: Vec<Span>,
49+
block_breaks: FxHashMap<Span, BlockInfo>,
3650
}
3751

3852
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-
);
53+
let mut check = CheckLoopVisitor {
54+
sess: tcx.sess,
55+
tcx,
56+
cx_stack: vec![Normal],
57+
blocks: Default::default(),
58+
block_breaks: Default::default(),
59+
};
60+
tcx.hir().visit_item_likes_in_module(module_def_id, &mut check);
61+
check.report_outside_loop_error();
4362
}
4463

4564
pub(crate) fn provide(providers: &mut Providers) {
@@ -82,6 +101,35 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
82101

83102
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
84103
match e.kind {
104+
hir::ExprKind::If(cond, then, else_opt) => {
105+
self.visit_expr(cond);
106+
if let hir::ExprKind::Block(ref b, None) = then.kind
107+
&& matches!(
108+
self.cx_stack.last(),
109+
Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_)) | Some(&IfUnlabeledBlock(_))
110+
)
111+
{
112+
self.with_context(IfUnlabeledBlock(b.span.shrink_to_lo()), |v| {
113+
v.visit_block(b)
114+
});
115+
} else {
116+
self.visit_expr(then);
117+
}
118+
if let Some(else_expr) = else_opt {
119+
if let hir::ExprKind::Block(ref b, None) = else_expr.kind
120+
&& matches!(
121+
self.cx_stack.last(),
122+
Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_)) | Some(&IfUnlabeledBlock(_))
123+
)
124+
{
125+
self.with_context(IfUnlabeledBlock(b.span.shrink_to_lo()), |v| {
126+
v.visit_block(b)
127+
});
128+
} else {
129+
self.visit_expr(else_expr);
130+
}
131+
}
132+
}
85133
hir::ExprKind::Loop(ref b, _, source, _) => {
86134
self.with_context(Loop(source), |v| v.visit_block(b));
87135
}
@@ -102,11 +150,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
102150
hir::ExprKind::Block(ref b, Some(_label)) => {
103151
self.with_context(LabeledBlock, |v| v.visit_block(b));
104152
}
105-
hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
153+
hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => {
106154
self.with_context(Normal, |v| v.visit_block(b));
107155
}
108156
hir::ExprKind::Block(ref b, None)
109-
if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
157+
if matches!(
158+
self.cx_stack.last(),
159+
Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_))
160+
) =>
110161
{
111162
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
112163
}
@@ -179,7 +230,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
179230
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
180231
None => sp_lo.shrink_to_lo(),
181232
};
182-
self.require_break_cx("break", e.span, label_sp);
233+
self.require_break_cx("break", e.span, label_sp, self.cx_stack.len() - 1);
183234
}
184235
hir::ExprKind::Continue(destination) => {
185236
self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -201,7 +252,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
201252
}
202253
Err(_) => {}
203254
}
204-
self.require_break_cx("continue", e.span, e.span)
255+
self.require_break_cx("continue", e.span, e.span, self.cx_stack.len() - 1)
205256
}
206257
_ => intravisit::walk_expr(self, e),
207258
}
@@ -213,15 +264,14 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
213264
where
214265
F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
215266
{
216-
let old_cx = self.cx;
217-
self.cx = cx;
267+
self.cx_stack.push(cx);
218268
f(self);
219-
self.cx = old_cx;
269+
self.cx_stack.pop();
220270
}
221271

222-
fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
272+
fn require_break_cx(&mut self, name: &str, span: Span, break_span: Span, cx_pos: usize) {
223273
let is_break = name == "break";
224-
match self.cx {
274+
match self.cx_stack[cx_pos] {
225275
LabeledBlock | Loop(_) => {}
226276
Closure(closure_span) => {
227277
self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
@@ -230,11 +280,27 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
230280
self.sess.dcx().emit_err(BreakInsideAsyncBlock { span, closure_span, name });
231281
}
232282
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 });
283+
let block = self.block_breaks.entry(block_span).or_insert_with(|| {
284+
self.blocks.push(block_span);
285+
BlockInfo {
286+
name: name.to_string(),
287+
spans: vec![],
288+
suggs: vec![],
289+
}
290+
});
291+
block.spans.push(span);
292+
block.suggs.push(break_span);
293+
}
294+
IfUnlabeledBlock(_) if is_break => {
295+
self.require_break_cx(name, span, break_span, cx_pos - 1);
235296
}
236-
Normal | Constant | Fn | UnlabeledBlock(_) => {
237-
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None });
297+
Normal | Constant | Fn | UnlabeledBlock(_) | IfUnlabeledBlock(_) => {
298+
self.sess.dcx().emit_err(OutsideLoop {
299+
spans: vec![span],
300+
name,
301+
is_break,
302+
suggestion: None,
303+
});
238304
}
239305
}
240306
}
@@ -246,12 +312,28 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
246312
cf_type: &str,
247313
) -> bool {
248314
if !span.is_desugaring(DesugaringKind::QuestionMark)
249-
&& self.cx == LabeledBlock
315+
&& self.cx_stack.last() == Some(&LabeledBlock)
250316
&& label.label.is_none()
251317
{
252318
self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
253319
return true;
254320
}
255321
false
256322
}
323+
324+
fn report_outside_loop_error(&mut self) {
325+
self.blocks.iter().for_each(|s| {
326+
if let Some(block) = self.block_breaks.get(s) {
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+
});
338+
}
257339
}
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`.

tests/ui/parser/break-in-unlabeled-block-in-macro.stderr

+17-17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ LL | foo!(());
2020
|
2121
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
2222

23+
error[E0268]: `break` outside of a loop or labeled block
24+
--> $DIR/break-in-unlabeled-block-in-macro.rs:33:17
25+
|
26+
LL | foo!(=> break ());
27+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
28+
29+
error[E0268]: `break` outside of a loop or labeled block
30+
--> $DIR/break-in-unlabeled-block-in-macro.rs:38:17
31+
|
32+
LL | break ()
33+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
34+
...
35+
LL | bar!()
36+
| ------ in this macro invocation
37+
|
38+
= note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info)
39+
2340
error[E0268]: `break` outside of a loop or labeled block
2441
--> $DIR/break-in-unlabeled-block-in-macro.rs:27:19
2542
|
@@ -47,23 +64,6 @@ help: consider labeling this block to be able to break within it
4764
LL | 'block: { break 'block $e; }
4865
| +++++++ ++++++
4966

50-
error[E0268]: `break` outside of a loop or labeled block
51-
--> $DIR/break-in-unlabeled-block-in-macro.rs:33:17
52-
|
53-
LL | foo!(=> break ());
54-
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
55-
56-
error[E0268]: `break` outside of a loop or labeled block
57-
--> $DIR/break-in-unlabeled-block-in-macro.rs:38:17
58-
|
59-
LL | break ()
60-
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
61-
...
62-
LL | bar!()
63-
| ------ in this macro invocation
64-
|
65-
= note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info)
66-
6767
error: aborting due to 6 previous errors
6868

6969
For more information about this error, try `rustc --explain E0268`.

0 commit comments

Comments
 (0)