Skip to content

Commit 8a13abb

Browse files
committed
Tweak error for invalid break expr
Point at loop head on invalid `break expr`. Suggest removing `expr` or using label if available.
1 parent 060dba6 commit 8a13abb

File tree

6 files changed

+175
-75
lines changed

6 files changed

+175
-75
lines changed

compiler/rustc_passes/src/loops.rs

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -90,47 +90,83 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
9090
};
9191

9292
if let Some(Node::Block(_)) = loop_id.and_then(|id| self.hir_map.find(id)) {
93-
return;
94-
}
93+
return;
9594
}
9695

97-
if opt_expr.is_some() {
98-
let loop_kind = if let Some(loop_id) = loop_id {
99-
Some(match self.hir_map.expect_expr(loop_id).kind {
100-
hir::ExprKind::Loop(_, _, source) => source,
96+
if let Some(break_expr) = opt_expr {
97+
let (head, label, loop_kind) = if let Some(loop_id) = loop_id {
98+
match self.hir_map.expect_expr(loop_id).kind {
99+
hir::ExprKind::Loop(_, label, source, sp) => {
100+
(Some(sp), label, Some(source))
101+
}
101102
ref r => {
102103
span_bug!(e.span, "break label resolved to a non-loop: {:?}", r)
103104
}
104-
})
105+
}
105106
} else {
106-
None
107+
(None, None, None)
107108
};
108109
match loop_kind {
109110
None | Some(hir::LoopSource::Loop) => (),
110111
Some(kind) => {
111-
struct_span_err!(
112+
let mut err = struct_span_err!(
112113
self.sess,
113114
e.span,
114115
E0571,
115116
"`break` with value from a `{}` loop",
116117
kind.name()
117-
)
118-
.span_label(
118+
);
119+
err.span_label(
119120
e.span,
120-
"can only break with a value inside \
121-
`loop` or breakable block",
122-
)
123-
.span_suggestion(
121+
"can only break with a value inside `loop` or breakable block",
122+
);
123+
if let Some(head) = head {
124+
err.span_label(
125+
head,
126+
&format!(
127+
"you can't `break` with a value in a `{}` loop",
128+
kind.name()
129+
),
130+
);
131+
}
132+
err.span_suggestion(
124133
e.span,
125134
&format!(
126-
"instead, use `break` on its own \
127-
without a value inside this `{}` loop",
128-
kind.name()
135+
"use `break` on its own without a value inside this `{}` loop",
136+
kind.name(),
129137
),
130138
"break".to_string(),
131139
Applicability::MaybeIncorrect,
132-
)
133-
.emit();
140+
);
141+
if let Some(label) = label {
142+
match break_expr.kind {
143+
hir::ExprKind::Path(hir::QPath::Resolved(
144+
None,
145+
hir::Path {
146+
segments: [segment],
147+
res: hir::def::Res::Err,
148+
..
149+
},
150+
)) if label.ident.to_string()
151+
== format!("'{}", segment.ident) =>
152+
{
153+
// This error is redundant, we will have already emitted a
154+
// suggestion to use the label when `segment` wasn't found
155+
// (hence the `Res::Err` check).
156+
err.delay_as_bug();
157+
}
158+
_ => {
159+
err.span_suggestion(
160+
break_expr.span,
161+
"alternatively, you might have meant to use the \
162+
available loop label",
163+
label.ident.to_string(),
164+
Applicability::MaybeIncorrect,
165+
);
166+
}
167+
}
168+
}
169+
err.emit();
134170
}
135171
}
136172
}

src/test/ui/label/label_misspelled.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
fn main() {
2-
'LOOP: loop {
3-
LOOP;
4-
//~^ ERROR cannot find value `LOOP` in this scope
5-
};
62
'while_loop: while true { //~ WARN denote infinite loops with
73
while_loop;
84
//~^ ERROR cannot find value `while_loop` in this scope
@@ -15,6 +11,10 @@ fn main() {
1511
for_loop;
1612
//~^ ERROR cannot find value `for_loop` in this scope
1713
};
14+
'LOOP: loop {
15+
LOOP;
16+
//~^ ERROR cannot find value `LOOP` in this scope
17+
};
1818
}
1919

2020
fn foo() {
@@ -25,16 +25,29 @@ fn foo() {
2525
'while_loop: while true { //~ WARN denote infinite loops with
2626
break while_loop;
2727
//~^ ERROR cannot find value `while_loop` in this scope
28-
//~| ERROR `break` with value from a `while` loop
2928
};
3029
'while_let: while let Some(_) = Some(()) {
3130
break while_let;
3231
//~^ ERROR cannot find value `while_let` in this scope
33-
//~| ERROR `break` with value from a `while` loop
3432
}
3533
'for_loop: for _ in 0..3 {
3634
break for_loop;
3735
//~^ ERROR cannot find value `for_loop` in this scope
38-
//~| ERROR `break` with value from a `for` loop
36+
};
37+
}
38+
39+
fn bar() {
40+
let foo = ();
41+
'while_loop: while true { //~ WARN denote infinite loops with
42+
break foo;
43+
//~^ ERROR `break` with value from a `while` loop
44+
};
45+
'while_let: while let Some(_) = Some(()) {
46+
break foo;
47+
//~^ ERROR `break` with value from a `while` loop
48+
}
49+
'for_loop: for _ in 0..3 {
50+
break foo;
51+
//~^ ERROR `break` with value from a `for` loop
3952
};
4053
}
Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
1-
error[E0425]: cannot find value `LOOP` in this scope
2-
--> $DIR/label_misspelled.rs:3:9
3-
|
4-
LL | 'LOOP: loop {
5-
| ----- a label with a similar name exists
6-
LL | LOOP;
7-
| ^^^^ not found in this scope
8-
91
error[E0425]: cannot find value `while_loop` in this scope
10-
--> $DIR/label_misspelled.rs:7:9
2+
--> $DIR/label_misspelled.rs:3:9
113
|
124
LL | 'while_loop: while true {
135
| ----------- a label with a similar name exists
146
LL | while_loop;
157
| ^^^^^^^^^^ not found in this scope
168

179
error[E0425]: cannot find value `while_let` in this scope
18-
--> $DIR/label_misspelled.rs:11:9
10+
--> $DIR/label_misspelled.rs:7:9
1911
|
2012
LL | 'while_let: while let Some(_) = Some(()) {
2113
| ---------- a label with a similar name exists
2214
LL | while_let;
2315
| ^^^^^^^^^ not found in this scope
2416

2517
error[E0425]: cannot find value `for_loop` in this scope
26-
--> $DIR/label_misspelled.rs:15:9
18+
--> $DIR/label_misspelled.rs:11:9
2719
|
2820
LL | 'for_loop: for _ in 0..3 {
2921
| --------- a label with a similar name exists
3022
LL | for_loop;
3123
| ^^^^^^^^ not found in this scope
3224

25+
error[E0425]: cannot find value `LOOP` in this scope
26+
--> $DIR/label_misspelled.rs:15:9
27+
|
28+
LL | 'LOOP: loop {
29+
| ----- a label with a similar name exists
30+
LL | LOOP;
31+
| ^^^^ not found in this scope
32+
3333
error[E0425]: cannot find value `LOOP` in this scope
3434
--> $DIR/label_misspelled.rs:22:15
3535
|
@@ -53,7 +53,7 @@ LL | break while_loop;
5353
| help: use the similarly named label: `'while_loop`
5454

5555
error[E0425]: cannot find value `while_let` in this scope
56-
--> $DIR/label_misspelled.rs:31:15
56+
--> $DIR/label_misspelled.rs:30:15
5757
|
5858
LL | 'while_let: while let Some(_) = Some(()) {
5959
| ---------- a label with a similar name exists
@@ -64,7 +64,7 @@ LL | break while_let;
6464
| help: use the similarly named label: `'while_let`
6565

6666
error[E0425]: cannot find value `for_loop` in this scope
67-
--> $DIR/label_misspelled.rs:36:15
67+
--> $DIR/label_misspelled.rs:34:15
6868
|
6969
LL | 'for_loop: for _ in 0..3 {
7070
| --------- a label with a similar name exists
@@ -75,7 +75,7 @@ LL | break for_loop;
7575
| help: use the similarly named label: `'for_loop`
7676

7777
warning: denote infinite loops with `loop { ... }`
78-
--> $DIR/label_misspelled.rs:6:5
78+
--> $DIR/label_misspelled.rs:2:5
7979
|
8080
LL | 'while_loop: while true {
8181
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
@@ -88,40 +88,64 @@ warning: denote infinite loops with `loop { ... }`
8888
LL | 'while_loop: while true {
8989
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
9090

91+
warning: denote infinite loops with `loop { ... }`
92+
--> $DIR/label_misspelled.rs:41:5
93+
|
94+
LL | 'while_loop: while true {
95+
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
96+
9197
error[E0571]: `break` with value from a `while` loop
92-
--> $DIR/label_misspelled.rs:26:9
98+
--> $DIR/label_misspelled.rs:42:9
9399
|
94-
LL | break while_loop;
95-
| ^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
100+
LL | 'while_loop: while true {
101+
| ----------------------- you can't `break` with a value in a `while` loop
102+
LL | break foo;
103+
| ^^^^^^^^^ can only break with a value inside `loop` or breakable block
96104
|
97-
help: instead, use `break` on its own without a value inside this `while` loop
105+
help: use `break` on its own without a value inside this `while` loop
98106
|
99107
LL | break;
100108
| ^^^^^
109+
help: alternatively, you might have meant to use the available loop label
110+
|
111+
LL | break 'while_loop;
112+
| ^^^^^^^^^^^
101113

102114
error[E0571]: `break` with value from a `while` loop
103-
--> $DIR/label_misspelled.rs:31:9
115+
--> $DIR/label_misspelled.rs:46:9
104116
|
105-
LL | break while_let;
106-
| ^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
117+
LL | 'while_let: while let Some(_) = Some(()) {
118+
| ---------------------------------------- you can't `break` with a value in a `while` loop
119+
LL | break foo;
120+
| ^^^^^^^^^ can only break with a value inside `loop` or breakable block
107121
|
108-
help: instead, use `break` on its own without a value inside this `while` loop
122+
help: use `break` on its own without a value inside this `while` loop
109123
|
110124
LL | break;
111125
| ^^^^^
126+
help: alternatively, you might have meant to use the available loop label
127+
|
128+
LL | break 'while_let;
129+
| ^^^^^^^^^^
112130

113131
error[E0571]: `break` with value from a `for` loop
114-
--> $DIR/label_misspelled.rs:36:9
132+
--> $DIR/label_misspelled.rs:50:9
115133
|
116-
LL | break for_loop;
117-
| ^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
134+
LL | 'for_loop: for _ in 0..3 {
135+
| ------------------------ you can't `break` with a value in a `for` loop
136+
LL | break foo;
137+
| ^^^^^^^^^ can only break with a value inside `loop` or breakable block
118138
|
119-
help: instead, use `break` on its own without a value inside this `for` loop
139+
help: use `break` on its own without a value inside this `for` loop
120140
|
121141
LL | break;
122142
| ^^^^^
143+
help: alternatively, you might have meant to use the available loop label
144+
|
145+
LL | break 'for_loop;
146+
| ^^^^^^^^^
123147

124-
error: aborting due to 11 previous errors; 2 warnings emitted
148+
error: aborting due to 11 previous errors; 3 warnings emitted
125149

126150
Some errors have detailed explanations: E0425, E0571.
127151
For more information about an error, try `rustc --explain E0425`.

src/test/ui/loops/loop-break-value-no-repeat.stderr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
error[E0571]: `break` with value from a `for` loop
22
--> $DIR/loop-break-value-no-repeat.rs:12:9
33
|
4+
LL | for _ in &[1,2,3] {
5+
| ----------------- you can't `break` with a value in a `for` loop
46
LL | break 22
57
| ^^^^^^^^ can only break with a value inside `loop` or breakable block
68
|
7-
help: instead, use `break` on its own without a value inside this `for` loop
9+
help: use `break` on its own without a value inside this `for` loop
810
|
911
LL | break
1012
| ^^^^^

src/test/ui/loops/loop-break-value.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,5 @@ fn main() {
9494
'LOOP: for _ in 0 .. 9 {
9595
break LOOP;
9696
//~^ ERROR cannot find value `LOOP` in this scope
97-
//~| ERROR `break` with value from a `for` loop
9897
}
9998
}

0 commit comments

Comments
 (0)