Tweak error for invalid break expr

Point at loop head on invalid `break expr`.
Suggest removing `expr` or using label if available.
This commit is contained in:
Esteban Küber 2021-01-20 17:25:27 -08:00
parent 060dba67b7
commit 8a13abba1d
6 changed files with 180 additions and 80 deletions

View file

@ -90,47 +90,83 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
}; };
if let Some(Node::Block(_)) = loop_id.and_then(|id| self.hir_map.find(id)) { if let Some(Node::Block(_)) = loop_id.and_then(|id| self.hir_map.find(id)) {
return; return;
}
} }
if opt_expr.is_some() { if let Some(break_expr) = opt_expr {
let loop_kind = if let Some(loop_id) = loop_id { let (head, label, loop_kind) = if let Some(loop_id) = loop_id {
Some(match self.hir_map.expect_expr(loop_id).kind { match self.hir_map.expect_expr(loop_id).kind {
hir::ExprKind::Loop(_, _, source) => source, hir::ExprKind::Loop(_, label, source, sp) => {
(Some(sp), label, Some(source))
}
ref r => { ref r => {
span_bug!(e.span, "break label resolved to a non-loop: {:?}", r) span_bug!(e.span, "break label resolved to a non-loop: {:?}", r)
} }
}) }
} else { } else {
None (None, None, None)
}; };
match loop_kind { match loop_kind {
None | Some(hir::LoopSource::Loop) => (), None | Some(hir::LoopSource::Loop) => (),
Some(kind) => { Some(kind) => {
struct_span_err!( let mut err = struct_span_err!(
self.sess, self.sess,
e.span, e.span,
E0571, E0571,
"`break` with value from a `{}` loop", "`break` with value from a `{}` loop",
kind.name() kind.name()
) );
.span_label( err.span_label(
e.span, e.span,
"can only break with a value inside \ "can only break with a value inside `loop` or breakable block",
`loop` or breakable block", );
) if let Some(head) = head {
.span_suggestion( err.span_label(
head,
&format!(
"you can't `break` with a value in a `{}` loop",
kind.name()
),
);
}
err.span_suggestion(
e.span, e.span,
&format!( &format!(
"instead, use `break` on its own \ "use `break` on its own without a value inside this `{}` loop",
without a value inside this `{}` loop", kind.name(),
kind.name()
), ),
"break".to_string(), "break".to_string(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) );
.emit(); if let Some(label) = label {
match break_expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(
None,
hir::Path {
segments: [segment],
res: hir::def::Res::Err,
..
},
)) if label.ident.to_string()
== format!("'{}", segment.ident) =>
{
// This error is redundant, we will have already emitted a
// suggestion to use the label when `segment` wasn't found
// (hence the `Res::Err` check).
err.delay_as_bug();
}
_ => {
err.span_suggestion(
break_expr.span,
"alternatively, you might have meant to use the \
available loop label",
label.ident.to_string(),
Applicability::MaybeIncorrect,
);
}
}
}
err.emit();
} }
} }
} }

View file

@ -1,8 +1,4 @@
fn main() { fn main() {
'LOOP: loop {
LOOP;
//~^ ERROR cannot find value `LOOP` in this scope
};
'while_loop: while true { //~ WARN denote infinite loops with 'while_loop: while true { //~ WARN denote infinite loops with
while_loop; while_loop;
//~^ ERROR cannot find value `while_loop` in this scope //~^ ERROR cannot find value `while_loop` in this scope
@ -15,6 +11,10 @@ fn main() {
for_loop; for_loop;
//~^ ERROR cannot find value `for_loop` in this scope //~^ ERROR cannot find value `for_loop` in this scope
}; };
'LOOP: loop {
LOOP;
//~^ ERROR cannot find value `LOOP` in this scope
};
} }
fn foo() { fn foo() {
@ -25,16 +25,29 @@ fn foo() {
'while_loop: while true { //~ WARN denote infinite loops with 'while_loop: while true { //~ WARN denote infinite loops with
break while_loop; break while_loop;
//~^ ERROR cannot find value `while_loop` in this scope //~^ ERROR cannot find value `while_loop` in this scope
//~| ERROR `break` with value from a `while` loop
}; };
'while_let: while let Some(_) = Some(()) { 'while_let: while let Some(_) = Some(()) {
break while_let; break while_let;
//~^ ERROR cannot find value `while_let` in this scope //~^ ERROR cannot find value `while_let` in this scope
//~| ERROR `break` with value from a `while` loop
} }
'for_loop: for _ in 0..3 { 'for_loop: for _ in 0..3 {
break for_loop; break for_loop;
//~^ ERROR cannot find value `for_loop` in this scope //~^ ERROR cannot find value `for_loop` in this scope
//~| ERROR `break` with value from a `for` loop };
}
fn bar() {
let foo = ();
'while_loop: while true { //~ WARN denote infinite loops with
break foo;
//~^ ERROR `break` with value from a `while` loop
};
'while_let: while let Some(_) = Some(()) {
break foo;
//~^ ERROR `break` with value from a `while` loop
}
'for_loop: for _ in 0..3 {
break foo;
//~^ ERROR `break` with value from a `for` loop
}; };
} }

View file

@ -1,13 +1,5 @@
error[E0425]: cannot find value `LOOP` in this scope
--> $DIR/label_misspelled.rs:3:9
|
LL | 'LOOP: loop {
| ----- a label with a similar name exists
LL | LOOP;
| ^^^^ not found in this scope
error[E0425]: cannot find value `while_loop` in this scope error[E0425]: cannot find value `while_loop` in this scope
--> $DIR/label_misspelled.rs:7:9 --> $DIR/label_misspelled.rs:3:9
| |
LL | 'while_loop: while true { LL | 'while_loop: while true {
| ----------- a label with a similar name exists | ----------- a label with a similar name exists
@ -15,7 +7,7 @@ LL | while_loop;
| ^^^^^^^^^^ not found in this scope | ^^^^^^^^^^ not found in this scope
error[E0425]: cannot find value `while_let` in this scope error[E0425]: cannot find value `while_let` in this scope
--> $DIR/label_misspelled.rs:11:9 --> $DIR/label_misspelled.rs:7:9
| |
LL | 'while_let: while let Some(_) = Some(()) { LL | 'while_let: while let Some(_) = Some(()) {
| ---------- a label with a similar name exists | ---------- a label with a similar name exists
@ -23,13 +15,21 @@ LL | while_let;
| ^^^^^^^^^ not found in this scope | ^^^^^^^^^ not found in this scope
error[E0425]: cannot find value `for_loop` in this scope error[E0425]: cannot find value `for_loop` in this scope
--> $DIR/label_misspelled.rs:15:9 --> $DIR/label_misspelled.rs:11:9
| |
LL | 'for_loop: for _ in 0..3 { LL | 'for_loop: for _ in 0..3 {
| --------- a label with a similar name exists | --------- a label with a similar name exists
LL | for_loop; LL | for_loop;
| ^^^^^^^^ not found in this scope | ^^^^^^^^ not found in this scope
error[E0425]: cannot find value `LOOP` in this scope
--> $DIR/label_misspelled.rs:15:9
|
LL | 'LOOP: loop {
| ----- a label with a similar name exists
LL | LOOP;
| ^^^^ not found in this scope
error[E0425]: cannot find value `LOOP` in this scope error[E0425]: cannot find value `LOOP` in this scope
--> $DIR/label_misspelled.rs:22:15 --> $DIR/label_misspelled.rs:22:15
| |
@ -53,7 +53,7 @@ LL | break while_loop;
| help: use the similarly named label: `'while_loop` | help: use the similarly named label: `'while_loop`
error[E0425]: cannot find value `while_let` in this scope error[E0425]: cannot find value `while_let` in this scope
--> $DIR/label_misspelled.rs:31:15 --> $DIR/label_misspelled.rs:30:15
| |
LL | 'while_let: while let Some(_) = Some(()) { LL | 'while_let: while let Some(_) = Some(()) {
| ---------- a label with a similar name exists | ---------- a label with a similar name exists
@ -64,7 +64,7 @@ LL | break while_let;
| help: use the similarly named label: `'while_let` | help: use the similarly named label: `'while_let`
error[E0425]: cannot find value `for_loop` in this scope error[E0425]: cannot find value `for_loop` in this scope
--> $DIR/label_misspelled.rs:36:15 --> $DIR/label_misspelled.rs:34:15
| |
LL | 'for_loop: for _ in 0..3 { LL | 'for_loop: for _ in 0..3 {
| --------- a label with a similar name exists | --------- a label with a similar name exists
@ -75,7 +75,7 @@ LL | break for_loop;
| help: use the similarly named label: `'for_loop` | help: use the similarly named label: `'for_loop`
warning: denote infinite loops with `loop { ... }` warning: denote infinite loops with `loop { ... }`
--> $DIR/label_misspelled.rs:6:5 --> $DIR/label_misspelled.rs:2:5
| |
LL | 'while_loop: while true { LL | 'while_loop: while true {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop` | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
@ -88,40 +88,64 @@ warning: denote infinite loops with `loop { ... }`
LL | 'while_loop: while true { LL | 'while_loop: while true {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop` | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
error[E0571]: `break` with value from a `while` loop warning: denote infinite loops with `loop { ... }`
--> $DIR/label_misspelled.rs:26:9 --> $DIR/label_misspelled.rs:41:5
| |
LL | break while_loop; LL | 'while_loop: while true {
| ^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
|
help: instead, use `break` on its own without a value inside this `while` loop
|
LL | break;
| ^^^^^
error[E0571]: `break` with value from a `while` loop error[E0571]: `break` with value from a `while` loop
--> $DIR/label_misspelled.rs:31:9 --> $DIR/label_misspelled.rs:42:9
| |
LL | break while_let; LL | 'while_loop: while true {
| ^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ----------------------- you can't `break` with a value in a `while` loop
LL | break foo;
| ^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `while` loop help: use `break` on its own without a value inside this `while` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
help: alternatively, you might have meant to use the available loop label
|
LL | break 'while_loop;
| ^^^^^^^^^^^
error[E0571]: `break` with value from a `while` loop
--> $DIR/label_misspelled.rs:46:9
|
LL | 'while_let: while let Some(_) = Some(()) {
| ---------------------------------------- you can't `break` with a value in a `while` loop
LL | break foo;
| ^^^^^^^^^ can only break with a value inside `loop` or breakable block
|
help: use `break` on its own without a value inside this `while` loop
|
LL | break;
| ^^^^^
help: alternatively, you might have meant to use the available loop label
|
LL | break 'while_let;
| ^^^^^^^^^^
error[E0571]: `break` with value from a `for` loop error[E0571]: `break` with value from a `for` loop
--> $DIR/label_misspelled.rs:36:9 --> $DIR/label_misspelled.rs:50:9
| |
LL | break for_loop; LL | 'for_loop: for _ in 0..3 {
| ^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ------------------------ you can't `break` with a value in a `for` loop
LL | break foo;
| ^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `for` loop help: use `break` on its own without a value inside this `for` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
help: alternatively, you might have meant to use the available loop label
|
LL | break 'for_loop;
| ^^^^^^^^^
error: aborting due to 11 previous errors; 2 warnings emitted error: aborting due to 11 previous errors; 3 warnings emitted
Some errors have detailed explanations: E0425, E0571. Some errors have detailed explanations: E0425, E0571.
For more information about an error, try `rustc --explain E0425`. For more information about an error, try `rustc --explain E0425`.

View file

@ -1,10 +1,12 @@
error[E0571]: `break` with value from a `for` loop error[E0571]: `break` with value from a `for` loop
--> $DIR/loop-break-value-no-repeat.rs:12:9 --> $DIR/loop-break-value-no-repeat.rs:12:9
| |
LL | for _ in &[1,2,3] {
| ----------------- you can't `break` with a value in a `for` loop
LL | break 22 LL | break 22
| ^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `for` loop help: use `break` on its own without a value inside this `for` loop
| |
LL | break LL | break
| ^^^^^ | ^^^^^

View file

@ -94,6 +94,5 @@ fn main() {
'LOOP: for _ in 0 .. 9 { 'LOOP: for _ in 0 .. 9 {
break LOOP; break LOOP;
//~^ ERROR cannot find value `LOOP` in this scope //~^ ERROR cannot find value `LOOP` in this scope
//~| ERROR `break` with value from a `for` loop
} }
} }

View file

@ -20,32 +20,48 @@ LL | 'while_loop: while true {
error[E0571]: `break` with value from a `while` loop error[E0571]: `break` with value from a `while` loop
--> $DIR/loop-break-value.rs:28:9 --> $DIR/loop-break-value.rs:28:9
| |
LL | 'while_loop: while true {
| ----------------------- you can't `break` with a value in a `while` loop
LL | break;
LL | break (); LL | break ();
| ^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `while` loop help: use `break` on its own without a value inside this `while` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
help: alternatively, you might have meant to use the available loop label
|
LL | break 'while_loop;
| ^^^^^^^^^^^
error[E0571]: `break` with value from a `while` loop error[E0571]: `break` with value from a `while` loop
--> $DIR/loop-break-value.rs:30:13 --> $DIR/loop-break-value.rs:30:13
| |
LL | 'while_loop: while true {
| ----------------------- you can't `break` with a value in a `while` loop
...
LL | break 'while_loop 123; LL | break 'while_loop 123;
| ^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `while` loop help: use `break` on its own without a value inside this `while` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
help: alternatively, you might have meant to use the available loop label
|
LL | break 'while_loop 'while_loop;
| ^^^^^^^^^^^
error[E0571]: `break` with value from a `while` loop error[E0571]: `break` with value from a `while` loop
--> $DIR/loop-break-value.rs:38:12 --> $DIR/loop-break-value.rs:38:12
| |
LL | while let Some(_) = Some(()) {
| ---------------------------- you can't `break` with a value in a `while` loop
LL | if break () { LL | if break () {
| ^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `while` loop help: use `break` on its own without a value inside this `while` loop
| |
LL | if break { LL | if break {
| ^^^^^ | ^^^^^
@ -53,10 +69,12 @@ LL | if break {
error[E0571]: `break` with value from a `while` loop error[E0571]: `break` with value from a `while` loop
--> $DIR/loop-break-value.rs:43:9 --> $DIR/loop-break-value.rs:43:9
| |
LL | while let Some(_) = Some(()) {
| ---------------------------- you can't `break` with a value in a `while` loop
LL | break None; LL | break None;
| ^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `while` loop help: use `break` on its own without a value inside this `while` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
@ -64,21 +82,30 @@ LL | break;
error[E0571]: `break` with value from a `while` loop error[E0571]: `break` with value from a `while` loop
--> $DIR/loop-break-value.rs:49:13 --> $DIR/loop-break-value.rs:49:13
| |
LL | 'while_let_loop: while let Some(_) = Some(()) {
| --------------------------------------------- you can't `break` with a value in a `while` loop
LL | loop {
LL | break 'while_let_loop "nope"; LL | break 'while_let_loop "nope";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `while` loop help: use `break` on its own without a value inside this `while` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
help: alternatively, you might have meant to use the available loop label
|
LL | break 'while_let_loop 'while_let_loop;
| ^^^^^^^^^^^^^^^
error[E0571]: `break` with value from a `for` loop error[E0571]: `break` with value from a `for` loop
--> $DIR/loop-break-value.rs:56:9 --> $DIR/loop-break-value.rs:56:9
| |
LL | for _ in &[1,2,3] {
| ----------------- you can't `break` with a value in a `for` loop
LL | break (); LL | break ();
| ^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `for` loop help: use `break` on its own without a value inside this `for` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
@ -86,10 +113,13 @@ LL | break;
error[E0571]: `break` with value from a `for` loop error[E0571]: `break` with value from a `for` loop
--> $DIR/loop-break-value.rs:57:9 --> $DIR/loop-break-value.rs:57:9
| |
LL | for _ in &[1,2,3] {
| ----------------- you can't `break` with a value in a `for` loop
LL | break ();
LL | break [()]; LL | break [()];
| ^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `for` loop help: use `break` on its own without a value inside this `for` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
@ -97,24 +127,20 @@ LL | break;
error[E0571]: `break` with value from a `for` loop error[E0571]: `break` with value from a `for` loop
--> $DIR/loop-break-value.rs:64:13 --> $DIR/loop-break-value.rs:64:13
| |
LL | 'for_loop: for _ in &[1,2,3] {
| ---------------------------- you can't `break` with a value in a `for` loop
...
LL | break 'for_loop Some(17); LL | break 'for_loop Some(17);
| ^^^^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
| |
help: instead, use `break` on its own without a value inside this `for` loop help: use `break` on its own without a value inside this `for` loop
| |
LL | break; LL | break;
| ^^^^^ | ^^^^^
help: alternatively, you might have meant to use the available loop label
error[E0571]: `break` with value from a `for` loop
--> $DIR/loop-break-value.rs:95:9
| |
LL | break LOOP; LL | break 'for_loop 'for_loop;
| ^^^^^^^^^^ can only break with a value inside `loop` or breakable block | ^^^^^^^^^
|
help: instead, use `break` on its own without a value inside this `for` loop
|
LL | break;
| ^^^^^
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/loop-break-value.rs:4:31 --> $DIR/loop-break-value.rs:4:31
@ -173,7 +199,7 @@ LL | break;
| expected integer, found `()` | expected integer, found `()`
| help: give it a value of the expected type: `break value` | help: give it a value of the expected type: `break value`
error: aborting due to 18 previous errors; 1 warning emitted error: aborting due to 17 previous errors; 1 warning emitted
Some errors have detailed explanations: E0308, E0425, E0571. Some errors have detailed explanations: E0308, E0425, E0571.
For more information about an error, try `rustc --explain E0308`. For more information about an error, try `rustc --explain E0308`.