Skip to content

Commit 04fe839

Browse files
committed
Increase accuracy of if condition misparse suggestion
Look at the expression that was parsed when trying to recover from a bad `if` condition to determine what was likely intended by the user beyond "maybe this was meant to be an `else` body". ``` error: expected `{`, found `map` --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:4:30 | LL | for _ in [1, 2, 3].iter()map(|x| x) {} | ^^^ expected `{` | help: you might have meant to write a method call | LL | for _ in [1, 2, 3].iter().map(|x| x) {} | + ```
1 parent 917a50a commit 04fe839

8 files changed

+385
-17
lines changed

compiler/rustc_parse/src/parser/stmt.rs

+89-9
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ impl<'a> Parser<'a> {
475475
}
476476

477477
fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
478+
let prev = self.prev_token.span;
478479
let sp = self.token.span;
479480
let mut e = self.dcx().struct_span_err(sp, msg);
480481
let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
@@ -514,15 +515,94 @@ impl<'a> Parser<'a> {
514515
} else {
515516
stmt.span
516517
};
517-
e.multipart_suggestion(
518-
"try placing this code inside a block",
519-
vec![
520-
(stmt_span.shrink_to_lo(), "{ ".to_string()),
521-
(stmt_span.shrink_to_hi(), " }".to_string()),
522-
],
523-
// Speculative; has been misleading in the past (#46836).
524-
Applicability::MaybeIncorrect,
525-
);
518+
match (&self.token.kind, &stmt.kind) {
519+
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
520+
if let ExprKind::Call(..) = expr.kind =>
521+
{
522+
// for _ in x y() {}
523+
e.span_suggestion_verbose(
524+
prev.between(sp),
525+
"you might have meant to write a method call",
526+
".".to_string(),
527+
Applicability::MaybeIncorrect,
528+
);
529+
}
530+
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
531+
if let ExprKind::Field(..) = expr.kind =>
532+
{
533+
// for _ in x y.z {}
534+
e.span_suggestion_verbose(
535+
prev.between(sp),
536+
"you might have meant to write a field access",
537+
".".to_string(),
538+
Applicability::MaybeIncorrect,
539+
);
540+
}
541+
(token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr))
542+
if let ExprKind::Struct(expr) = &expr.kind
543+
&& let None = expr.qself
544+
&& expr.path.segments.len() == 1 =>
545+
{
546+
// This is specific to "mistyped `if` condition followed by empty body"
547+
//
548+
// for _ in x y {}
549+
e.span_suggestion_verbose(
550+
prev.between(sp),
551+
"you might have meant to write a field access",
552+
".".to_string(),
553+
Applicability::MaybeIncorrect,
554+
);
555+
}
556+
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
557+
if let ExprKind::Lit(lit) = expr.kind
558+
&& let None = lit.suffix
559+
&& let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
560+
{
561+
// for _ in x 0 {}
562+
// for _ in x 0.0 {}
563+
e.span_suggestion_verbose(
564+
prev.between(sp),
565+
format!("you might have meant to write a field access"),
566+
".".to_string(),
567+
Applicability::MaybeIncorrect,
568+
);
569+
}
570+
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
571+
if let ExprKind::Loop(..)
572+
| ExprKind::If(..)
573+
| ExprKind::While(..)
574+
| ExprKind::Match(..)
575+
| ExprKind::ForLoop { .. }
576+
| ExprKind::TryBlock(..)
577+
| ExprKind::Ret(..)
578+
| ExprKind::Closure(..)
579+
| ExprKind::Struct(..)
580+
| ExprKind::Try(..) = expr.kind =>
581+
{
582+
// These are more likely to have been meant as a block body.
583+
e.multipart_suggestion(
584+
"try placing this code inside a block",
585+
vec![
586+
(stmt_span.shrink_to_lo(), "{ ".to_string()),
587+
(stmt_span.shrink_to_hi(), " }".to_string()),
588+
],
589+
// Speculative; has been misleading in the past (#46836).
590+
Applicability::MaybeIncorrect,
591+
);
592+
}
593+
(token::OpenDelim(Delimiter::Brace), _) => {}
594+
(_, _) => {
595+
e.multipart_suggestion(
596+
"try placing this code inside a block",
597+
vec![
598+
(stmt_span.shrink_to_lo(), "{ ".to_string()),
599+
(stmt_span.shrink_to_hi(), " }".to_string()),
600+
],
601+
// Speculative; has been misleading in the past (#46836).
602+
Applicability::MaybeIncorrect,
603+
);
604+
}
605+
}
526606
}
527607
Err(e) => {
528608
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);

tests/ui/issues/issue-39848.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ LL | if $tgt.has_$field() {}
1616
LL | get_opt!(bar, foo);
1717
| ------------------ in this macro invocation
1818
= note: this error originates in the macro `get_opt` (in Nightly builds, run with -Z macro-backtrace for more info)
19-
help: try placing this code inside a block
19+
help: you might have meant to write a method call
2020
|
21-
LL | if $tgt.has_{ $field() } {}
22-
| + +
21+
LL | if $tgt.has_.$field() {}
22+
| +
2323

2424
error: aborting due to 1 previous error
2525

tests/ui/parser/else-no-if.stderr

-5
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,6 @@ error: expected `{`, found `falsy`
7575
|
7676
LL | } else falsy! {} {
7777
| ^^^^^ expected `{`
78-
|
79-
help: try placing this code inside a block
80-
|
81-
LL | } else { falsy! {} } {
82-
| + +
8378

8479
error: expected `{`, found `falsy`
8580
--> $DIR/else-no-if.rs:54:12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//@ run-rustfix
2+
#![allow(dead_code)]
3+
fn main() {
4+
for _ in [1, 2, 3].iter().map(|x| x) {}
5+
//~^ ERROR expected `{`, found `map`
6+
//~| HELP you might have meant to write a method call
7+
}
8+
fn foo5() {
9+
let x = (vec![1, 2, 3],);
10+
for _ in x.0 {}
11+
//~^ ERROR expected `{`, found `0`
12+
//~| HELP you might have meant to write a field access
13+
}
14+
fn foo6() {
15+
let x = ((vec![1, 2, 3],),);
16+
for _ in x.0.0 {}
17+
//~^ ERROR expected `{`, found `0.0`
18+
//~| HELP you might have meant to write a field access
19+
}
20+
fn foo7() {
21+
let x = Some(vec![1, 2, 3]);
22+
for _ in x.unwrap() {}
23+
//~^ ERROR expected `{`, found `unwrap`
24+
//~| HELP you might have meant to write a method call
25+
}
26+
fn foo8() {
27+
let x = S { a: A { b: vec![1, 2, 3] } };
28+
for _ in x.a.b {}
29+
//~^ ERROR expected `{`, found `a`
30+
//~| HELP you might have meant to write a field access
31+
}
32+
33+
struct S {
34+
a: A,
35+
}
36+
37+
struct A {
38+
b: Vec<i32>,
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//@ run-rustfix
2+
#![allow(dead_code)]
3+
fn main() {
4+
for _ in [1, 2, 3].iter()map(|x| x) {}
5+
//~^ ERROR expected `{`, found `map`
6+
//~| HELP you might have meant to write a method call
7+
}
8+
fn foo5() {
9+
let x = (vec![1, 2, 3],);
10+
for _ in x 0 {}
11+
//~^ ERROR expected `{`, found `0`
12+
//~| HELP you might have meant to write a field access
13+
}
14+
fn foo6() {
15+
let x = ((vec![1, 2, 3],),);
16+
for _ in x 0.0 {}
17+
//~^ ERROR expected `{`, found `0.0`
18+
//~| HELP you might have meant to write a field access
19+
}
20+
fn foo7() {
21+
let x = Some(vec![1, 2, 3]);
22+
for _ in x unwrap() {}
23+
//~^ ERROR expected `{`, found `unwrap`
24+
//~| HELP you might have meant to write a method call
25+
}
26+
fn foo8() {
27+
let x = S { a: A { b: vec![1, 2, 3] } };
28+
for _ in x a.b {}
29+
//~^ ERROR expected `{`, found `a`
30+
//~| HELP you might have meant to write a field access
31+
}
32+
33+
struct S {
34+
a: A,
35+
}
36+
37+
struct A {
38+
b: Vec<i32>,
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
error: expected `{`, found `map`
2+
--> $DIR/missing-dot-on-if-condition-expression-fixable.rs:4:30
3+
|
4+
LL | for _ in [1, 2, 3].iter()map(|x| x) {}
5+
| ^^^ expected `{`
6+
|
7+
help: you might have meant to write a method call
8+
|
9+
LL | for _ in [1, 2, 3].iter().map(|x| x) {}
10+
| +
11+
12+
error: expected `{`, found `0`
13+
--> $DIR/missing-dot-on-if-condition-expression-fixable.rs:10:16
14+
|
15+
LL | for _ in x 0 {}
16+
| ^ expected `{`
17+
|
18+
help: you might have meant to write a field access
19+
|
20+
LL | for _ in x.0 {}
21+
| +
22+
23+
error: expected `{`, found `0.0`
24+
--> $DIR/missing-dot-on-if-condition-expression-fixable.rs:16:16
25+
|
26+
LL | for _ in x 0.0 {}
27+
| ^^^ expected `{`
28+
|
29+
help: you might have meant to write a field access
30+
|
31+
LL | for _ in x.0.0 {}
32+
| +
33+
34+
error: expected `{`, found `unwrap`
35+
--> $DIR/missing-dot-on-if-condition-expression-fixable.rs:22:16
36+
|
37+
LL | for _ in x unwrap() {}
38+
| ^^^^^^ expected `{`
39+
|
40+
help: you might have meant to write a method call
41+
|
42+
LL | for _ in x.unwrap() {}
43+
| +
44+
45+
error: expected `{`, found `a`
46+
--> $DIR/missing-dot-on-if-condition-expression-fixable.rs:28:16
47+
|
48+
LL | for _ in x a.b {}
49+
| ^ expected `{`
50+
|
51+
help: you might have meant to write a field access
52+
|
53+
LL | for _ in x.a.b {}
54+
| +
55+
56+
error: aborting due to 5 previous errors
57+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
fn main() {
2+
for _ in [1, 2, 3].iter()map(|x| x) {}
3+
//~^ ERROR expected `{`, found `map`
4+
//~| HELP you might have meant to write a method call
5+
}
6+
fn foo1() {
7+
for _ in 1.3f64 cos() {}
8+
//~^ ERROR expected `{`, found `cos`
9+
//~| HELP you might have meant to write a method call
10+
}
11+
fn foo2() {
12+
for _ in 1.3 cos {}
13+
//~^ ERROR expected `{`, found `cos`
14+
//~| HELP you might have meant to write a field access
15+
}
16+
fn foo3() {
17+
for _ in 1 cos() {}
18+
//~^ ERROR expected `{`, found `cos`
19+
//~| HELP you might have meant to write a method call
20+
}
21+
fn foo4() {
22+
for _ in 1 cos {}
23+
//~^ ERROR expected `{`, found `cos`
24+
//~| HELP you might have meant to write a field access
25+
}
26+
fn foo5() {
27+
let x = (vec![1, 2, 3],);
28+
for _ in x 0 {}
29+
//~^ ERROR expected `{`, found `0`
30+
//~| HELP you might have meant to write a field access
31+
}
32+
fn foo6() {
33+
let x = ((vec![1, 2, 3],),);
34+
for _ in x 0.0 {}
35+
//~^ ERROR expected `{`, found `0.0`
36+
//~| HELP you might have meant to write a field access
37+
}
38+
fn foo7() {
39+
let x = Some(vec![1, 2, 3]);
40+
for _ in x unwrap() {}
41+
//~^ ERROR expected `{`, found `unwrap`
42+
//~| HELP you might have meant to write a method call
43+
}
44+
fn foo8() {
45+
let x = S { a: A { b: vec![1, 2, 3] } };
46+
for _ in x a.b {}
47+
//~^ ERROR expected `{`, found `a`
48+
//~| HELP you might have meant to write a field access
49+
}
50+
51+
struct S {
52+
a: A,
53+
}
54+
55+
struct A {
56+
b: Vec<i32>,
57+
}

0 commit comments

Comments
 (0)