Skip to content

Commit e8a9412

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 e8a9412

File tree

4 files changed

+169
-16
lines changed

4 files changed

+169
-16
lines changed

compiler/rustc_passes/src/loops.rs

+68-16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_data_structures::fx::FxHashSet;
12
use Context::*;
23

34
use rustc_hir as hir;
@@ -9,7 +10,7 @@ use rustc_middle::query::Providers;
910
use rustc_middle::ty::TyCtxt;
1011
use rustc_session::Session;
1112
use rustc_span::hygiene::DesugaringKind;
12-
use rustc_span::{BytePos, Span};
13+
use rustc_span::{BytePos, Span, DUMMY_SP};
1314

1415
use crate::errors::{
1516
BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
@@ -24,21 +25,33 @@ 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+
/// cx_stack is a stack, used for some situation like in `if` block,
34+
/// we should not suggest adding `'block` label in `if` block,
35+
/// we can back to outer block and add label there.
36+
/// fix_label_spans used for recording spans which has added `'block` label,
37+
/// this can help avoiding suggesting redundant labels.
38+
#[derive(Clone)]
3239
struct CheckLoopVisitor<'a, 'tcx> {
3340
sess: &'a Session,
3441
tcx: TyCtxt<'tcx>,
35-
cx: Context,
42+
cx_stack: Vec<Context>,
43+
fix_label_spans: FxHashSet<Span>,
3644
}
3745

3846
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
3947
tcx.hir().visit_item_likes_in_module(
4048
module_def_id,
41-
&mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal },
49+
&mut CheckLoopVisitor {
50+
sess: tcx.sess,
51+
tcx,
52+
cx_stack: vec![Normal],
53+
fix_label_spans: Default::default(),
54+
},
4255
);
4356
}
4457

@@ -82,6 +95,35 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
8295

8396
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
8497
match e.kind {
98+
hir::ExprKind::If(cond, then, else_opt) => {
99+
self.visit_expr(cond);
100+
if let hir::ExprKind::Block(ref b, None) = then.kind
101+
&& matches!(
102+
self.cx_stack.last().unwrap(),
103+
Normal | Constant | UnlabeledBlock(_)
104+
)
105+
{
106+
self.with_context(IfUnlabeledBlock(b.span.shrink_to_lo()), |v| {
107+
v.visit_block(b)
108+
});
109+
} else {
110+
self.visit_expr(then);
111+
}
112+
if let Some(else_expr) = else_opt {
113+
if let hir::ExprKind::Block(ref b, None) = else_expr.kind
114+
&& matches!(
115+
self.cx_stack.last().unwrap(),
116+
Normal | Constant | UnlabeledBlock(_)
117+
)
118+
{
119+
self.with_context(IfUnlabeledBlock(b.span.shrink_to_lo()), |v| {
120+
v.visit_block(b)
121+
});
122+
} else {
123+
self.visit_expr(then);
124+
}
125+
}
126+
}
85127
hir::ExprKind::Loop(ref b, _, source, _) => {
86128
self.with_context(Loop(source), |v| v.visit_block(b));
87129
}
@@ -102,11 +144,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
102144
hir::ExprKind::Block(ref b, Some(_label)) => {
103145
self.with_context(LabeledBlock, |v| v.visit_block(b));
104146
}
105-
hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
147+
hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last().unwrap(), Fn) => {
106148
self.with_context(Normal, |v| v.visit_block(b));
107149
}
108150
hir::ExprKind::Block(ref b, None)
109-
if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
151+
if matches!(
152+
self.cx_stack.last().unwrap(),
153+
Normal | Constant | UnlabeledBlock(_)
154+
) =>
110155
{
111156
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
112157
}
@@ -179,7 +224,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
179224
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
180225
None => sp_lo.shrink_to_lo(),
181226
};
182-
self.require_break_cx("break", e.span, label_sp);
227+
self.require_break_cx("break", e.span, label_sp, self.cx_stack.len() - 1);
183228
}
184229
hir::ExprKind::Continue(destination) => {
185230
self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -201,7 +246,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
201246
}
202247
Err(_) => {}
203248
}
204-
self.require_break_cx("continue", e.span, e.span)
249+
self.require_break_cx("continue", e.span, e.span, self.cx_stack.len() - 1)
205250
}
206251
_ => intravisit::walk_expr(self, e),
207252
}
@@ -213,15 +258,14 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
213258
where
214259
F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
215260
{
216-
let old_cx = self.cx;
217-
self.cx = cx;
261+
self.cx_stack.push(cx);
218262
f(self);
219-
self.cx = old_cx;
263+
self.cx_stack.pop();
220264
}
221265

222-
fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
266+
fn require_break_cx(&mut self, name: &str, span: Span, break_span: Span, cx_pos: usize) {
223267
let is_break = name == "break";
224-
match self.cx {
268+
match self.cx_stack[cx_pos] {
225269
LabeledBlock | Loop(_) => {}
226270
Closure(closure_span) => {
227271
self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
@@ -230,10 +274,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
230274
self.sess.dcx().emit_err(BreakInsideAsyncBlock { span, closure_span, name });
231275
}
232276
UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
233-
let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
277+
let suggestion = if !self.fix_label_spans.contains(&block_span) {
278+
Some(OutsideLoopSuggestion { block_span, break_span })
279+
} else {
280+
Some(OutsideLoopSuggestion { block_span: DUMMY_SP, break_span })
281+
};
234282
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion });
283+
self.fix_label_spans.insert(block_span);
284+
}
285+
IfUnlabeledBlock(_) if is_break => {
286+
self.require_break_cx(name, span, break_span, cx_pos - 1);
235287
}
236-
Normal | Constant | Fn | UnlabeledBlock(_) => {
288+
Normal | Constant | Fn | UnlabeledBlock(_) | IfUnlabeledBlock(_) => {
237289
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None });
238290
}
239291
}
@@ -246,7 +298,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
246298
cf_type: &str,
247299
) -> bool {
248300
if !span.is_desugaring(DesugaringKind::QuestionMark)
249-
&& self.cx == LabeledBlock
301+
&& *self.cx_stack.last().unwrap() == LabeledBlock
250302
&& label.label.is_none()
251303
{
252304
self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)]
4+
5+
fn main() {
6+
let n = 1;
7+
let x = 'block: {
8+
if n == 0 {
9+
break 'block 1; //~ ERROR [E0268]
10+
} else {
11+
break 'block 2; //~ ERROR [E0268]
12+
}
13+
};
14+
15+
let y = 'block: {
16+
if n == 0 {
17+
break 'block 1; //~ ERROR [E0268]
18+
}
19+
break 'block 0; //~ ERROR [E0268]
20+
};
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)]
4+
5+
fn main() {
6+
let n = 1;
7+
let x = {
8+
if n == 0 {
9+
break 1; //~ ERROR [E0268]
10+
} else {
11+
break 2; //~ ERROR [E0268]
12+
}
13+
};
14+
15+
let y = {
16+
if n == 0 {
17+
break 1; //~ ERROR [E0268]
18+
}
19+
break 0; //~ ERROR [E0268]
20+
};
21+
}
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:9:13
3+
|
4+
LL | break 1;
5+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
6+
|
7+
help: consider labeling this block to be able to break within it
8+
|
9+
LL ~ let x = 'block: {
10+
LL | if n == 0 {
11+
LL ~ break 'block 1;
12+
|
13+
14+
error[E0268]: `break` outside of a loop or labeled block
15+
--> $DIR/loop-if-else-break-issue-123261.rs:11:13
16+
|
17+
LL | break 2;
18+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
19+
|
20+
help: consider labeling this block to be able to break within it
21+
|
22+
LL ~ 'block: //@ run-rustfix
23+
LL |
24+
...
25+
LL | } else {
26+
LL ~ break 'block 2;
27+
|
28+
29+
error[E0268]: `break` outside of a loop or labeled block
30+
--> $DIR/loop-if-else-break-issue-123261.rs:17:13
31+
|
32+
LL | break 1;
33+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
34+
|
35+
help: consider labeling this block to be able to break within it
36+
|
37+
LL ~ let y = 'block: {
38+
LL | if n == 0 {
39+
LL ~ break 'block 1;
40+
|
41+
42+
error[E0268]: `break` outside of a loop or labeled block
43+
--> $DIR/loop-if-else-break-issue-123261.rs:19:9
44+
|
45+
LL | break 0;
46+
| ^^^^^^^ cannot `break` outside of a loop or labeled block
47+
|
48+
help: consider labeling this block to be able to break within it
49+
|
50+
LL ~ 'block: //@ run-rustfix
51+
LL |
52+
...
53+
LL | }
54+
LL ~ break 'block 0;
55+
|
56+
57+
error: aborting due to 4 previous errors
58+
59+
For more information about this error, try `rustc --explain E0268`.

0 commit comments

Comments
 (0)