Skip to content

Commit 84d44dd

Browse files
committed
Auto merge of #116366 - estebank:issue-103982, r=oli-obk
Suggest labeling block if `break` is in bare block Fix #103982.
2 parents fa6d1e7 + d23dc20 commit 84d44dd

30 files changed

+220
-8
lines changed

Diff for: compiler/rustc_passes/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,8 @@ passes_outside_loop =
580580
*[false] {""}
581581
}
582582
583+
passes_outside_loop_suggestion = consider labeling this block to be able to break within it
584+
583585
passes_params_not_allowed =
584586
referencing function parameters is not allowed in naked functions
585587
.help = follow the calling convention in asm block to use parameters

Diff for: compiler/rustc_passes/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,16 @@ pub struct OutsideLoop<'a> {
10991099
pub span: Span,
11001100
pub name: &'a str,
11011101
pub is_break: bool,
1102+
#[subdiagnostic]
1103+
pub suggestion: Option<OutsideLoopSuggestion>,
1104+
}
1105+
#[derive(Subdiagnostic)]
1106+
#[multipart_suggestion(passes_outside_loop_suggestion, applicability = "maybe-incorrect")]
1107+
pub struct OutsideLoopSuggestion {
1108+
#[suggestion_part(code = "'block: ")]
1109+
pub block_span: Span,
1110+
#[suggestion_part(code = " 'block")]
1111+
pub break_span: Span,
11021112
}
11031113

11041114
#[derive(Diagnostic)]

Diff for: compiler/rustc_passes/src/loops.rs

+47-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use Context::*;
22

33
use rustc_hir as hir;
4-
use rustc_hir::def_id::LocalModDefId;
4+
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
55
use rustc_hir::intravisit::{self, Visitor};
66
use rustc_hir::{Destination, Movability, Node};
77
use rustc_middle::hir::map::Map;
@@ -10,19 +10,21 @@ use rustc_middle::query::Providers;
1010
use rustc_middle::ty::TyCtxt;
1111
use rustc_session::Session;
1212
use rustc_span::hygiene::DesugaringKind;
13-
use rustc_span::Span;
13+
use rustc_span::{BytePos, Span};
1414

1515
use crate::errors::{
1616
BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
17-
UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
17+
OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
1818
};
1919

2020
#[derive(Clone, Copy, Debug, PartialEq)]
2121
enum Context {
2222
Normal,
23+
Fn,
2324
Loop(hir::LoopSource),
2425
Closure(Span),
2526
AsyncClosure(Span),
27+
UnlabeledBlock(Span),
2628
LabeledBlock,
2729
Constant,
2830
}
@@ -60,6 +62,25 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
6062
self.with_context(Constant, |v| intravisit::walk_inline_const(v, c));
6163
}
6264

65+
fn visit_fn(
66+
&mut self,
67+
fk: hir::intravisit::FnKind<'hir>,
68+
fd: &'hir hir::FnDecl<'hir>,
69+
b: hir::BodyId,
70+
_: Span,
71+
id: LocalDefId,
72+
) {
73+
self.with_context(Fn, |v| intravisit::walk_fn(v, fk, fd, b, id));
74+
}
75+
76+
fn visit_trait_item(&mut self, trait_item: &'hir hir::TraitItem<'hir>) {
77+
self.with_context(Fn, |v| intravisit::walk_trait_item(v, trait_item));
78+
}
79+
80+
fn visit_impl_item(&mut self, impl_item: &'hir hir::ImplItem<'hir>) {
81+
self.with_context(Fn, |v| intravisit::walk_impl_item(v, impl_item));
82+
}
83+
6384
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
6485
match e.kind {
6586
hir::ExprKind::Loop(ref b, _, source, _) => {
@@ -83,6 +104,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
83104
hir::ExprKind::Block(ref b, Some(_label)) => {
84105
self.with_context(LabeledBlock, |v| v.visit_block(&b));
85106
}
107+
hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
108+
self.with_context(Normal, |v| v.visit_block(&b));
109+
}
110+
hir::ExprKind::Block(ref b, None)
111+
if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
112+
{
113+
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(&b));
114+
}
86115
hir::ExprKind::Break(break_label, ref opt_expr) => {
87116
if let Some(e) = opt_expr {
88117
self.visit_expr(e);
@@ -147,7 +176,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
147176
}
148177
}
149178

150-
self.require_break_cx("break", e.span);
179+
let sp_lo = e.span.with_lo(e.span.lo() + BytePos("break".len() as u32));
180+
let label_sp = match break_label.label {
181+
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
182+
None => sp_lo.shrink_to_lo(),
183+
};
184+
self.require_break_cx("break", e.span, label_sp);
151185
}
152186
hir::ExprKind::Continue(destination) => {
153187
self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -169,7 +203,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
169203
}
170204
Err(_) => {}
171205
}
172-
self.require_break_cx("continue", e.span)
206+
self.require_break_cx("continue", e.span, e.span)
173207
}
174208
_ => intravisit::walk_expr(self, e),
175209
}
@@ -187,7 +221,8 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
187221
self.cx = old_cx;
188222
}
189223

190-
fn require_break_cx(&self, name: &str, span: Span) {
224+
fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
225+
let is_break = name == "break";
191226
match self.cx {
192227
LabeledBlock | Loop(_) => {}
193228
Closure(closure_span) => {
@@ -196,8 +231,12 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
196231
AsyncClosure(closure_span) => {
197232
self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
198233
}
199-
Normal | Constant => {
200-
self.sess.emit_err(OutsideLoop { span, name, is_break: name == "break" });
234+
UnlabeledBlock(block_span) if is_break && block_span.ctxt() == break_span.ctxt() => {
235+
let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
236+
self.sess.emit_err(OutsideLoop { span, name, is_break, suggestion });
237+
}
238+
Normal | Constant | Fn | UnlabeledBlock(_) => {
239+
self.sess.emit_err(OutsideLoop { span, name, is_break, suggestion: None });
201240
}
202241
}
203242
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Diff for: tests/ui/parser/break-in-unlabeled-block-in-macro.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
macro_rules! foo {
2+
() => {
3+
break (); //~ ERROR `break` outside of a loop or labeled block
4+
};
5+
($e: expr) => {
6+
break $e; //~ ERROR `break` outside of a loop or labeled block
7+
};
8+
(stmt $s: stmt) => {
9+
$s
10+
};
11+
(@ $e: expr) => {
12+
{ break $e; } //~ ERROR `break` outside of a loop or labeled block
13+
};
14+
(=> $s: stmt) => {
15+
{ $s }
16+
};
17+
}
18+
19+
fn main() {
20+
{
21+
foo!();
22+
}
23+
{
24+
foo!(());
25+
}
26+
{
27+
foo!(stmt break ()); //~ ERROR `break` outside of a loop or labeled block
28+
}
29+
{
30+
foo!(@ ());
31+
}
32+
{
33+
foo!(=> break ()); //~ ERROR `break` outside of a loop or labeled block
34+
}
35+
{
36+
macro_rules! bar {
37+
() => {
38+
break () //~ ERROR `break` outside of a loop or labeled block
39+
};
40+
}
41+
bar!()
42+
}
43+
}
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
error[E0268]: `break` outside of a loop or labeled block
2+
--> $DIR/break-in-unlabeled-block-in-macro.rs:3:9
3+
|
4+
LL | break ();
5+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
6+
...
7+
LL | foo!();
8+
| ------ in this macro invocation
9+
|
10+
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error[E0268]: `break` outside of a loop or labeled block
13+
--> $DIR/break-in-unlabeled-block-in-macro.rs:6:9
14+
|
15+
LL | break $e;
16+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
17+
...
18+
LL | foo!(());
19+
| -------- in this macro invocation
20+
|
21+
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
22+
23+
error[E0268]: `break` outside of a loop or labeled block
24+
--> $DIR/break-in-unlabeled-block-in-macro.rs:27:19
25+
|
26+
LL | foo!(stmt break ());
27+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
28+
|
29+
help: consider labeling this block to be able to break within it
30+
|
31+
LL ~ 'block: {
32+
LL ~ foo!(stmt break 'block ());
33+
|
34+
35+
error[E0268]: `break` outside of a loop or labeled block
36+
--> $DIR/break-in-unlabeled-block-in-macro.rs:12:11
37+
|
38+
LL | { break $e; }
39+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
40+
...
41+
LL | foo!(@ ());
42+
| ---------- in this macro invocation
43+
|
44+
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
45+
help: consider labeling this block to be able to break within it
46+
|
47+
LL | 'block: { break 'block $e; }
48+
| +++++++ ++++++
49+
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+
67+
error: aborting due to 6 previous errors
68+
69+
For more information about this error, try `rustc --explain E0268`.

Diff for: tests/ui/parser/break-in-unlabeled-block.fixed

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// run-rustfix
2+
fn main() {
3+
'block: {
4+
break 'block (); //~ ERROR `break` outside of a loop or labeled block
5+
}
6+
{
7+
'block: {
8+
break 'block (); //~ ERROR `break` outside of a loop or labeled block
9+
}
10+
}
11+
}

Diff for: tests/ui/parser/break-in-unlabeled-block.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// run-rustfix
2+
fn main() {
3+
{
4+
break (); //~ ERROR `break` outside of a loop or labeled block
5+
}
6+
{
7+
{
8+
break (); //~ ERROR `break` outside of a loop or labeled block
9+
}
10+
}
11+
}

Diff for: tests/ui/parser/break-in-unlabeled-block.stderr

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0268]: `break` outside of a loop or labeled block
2+
--> $DIR/break-in-unlabeled-block.rs:4:9
3+
|
4+
LL | break ();
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 ~ 'block: {
10+
LL ~ break 'block ();
11+
|
12+
13+
error[E0268]: `break` outside of a loop or labeled block
14+
--> $DIR/break-in-unlabeled-block.rs:8:13
15+
|
16+
LL | break ();
17+
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
18+
|
19+
help: consider labeling this block to be able to break within it
20+
|
21+
LL ~ 'block: {
22+
LL ~ break 'block ();
23+
|
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0268`.

0 commit comments

Comments
 (0)