diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2024-11-14 20:29:24 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2024-11-16 20:03:31 +0000 |
| commit | 04fe8391775683e58d861f28678bf80940c91f44 (patch) | |
| tree | fcb880c349d5a63a1ddf9c1d08699ec83a79dab9 | |
| parent | 917a50a03931a9861c19a46f3e2a02a28f1da936 (diff) | |
| download | rust-04fe8391775683e58d861f28678bf80940c91f44.tar.gz rust-04fe8391775683e58d861f28678bf80940c91f44.zip | |
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) {}
| +
```
8 files changed, 385 insertions, 17 deletions
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index b7cdae3e3e1..e8877b8c541 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -475,6 +475,7 @@ impl<'a> Parser<'a> { } fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> { + let prev = self.prev_token.span; let sp = self.token.span; let mut e = self.dcx().struct_span_err(sp, msg); let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon; @@ -514,15 +515,94 @@ impl<'a> Parser<'a> { } else { stmt.span }; - e.multipart_suggestion( - "try placing this code inside a block", - vec![ - (stmt_span.shrink_to_lo(), "{ ".to_string()), - (stmt_span.shrink_to_hi(), " }".to_string()), - ], - // Speculative; has been misleading in the past (#46836). - Applicability::MaybeIncorrect, - ); + match (&self.token.kind, &stmt.kind) { + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Call(..) = expr.kind => + { + // for _ in x y() {} + e.span_suggestion_verbose( + prev.between(sp), + "you might have meant to write a method call", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Field(..) = expr.kind => + { + // for _ in x y.z {} + e.span_suggestion_verbose( + prev.between(sp), + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Struct(expr) = &expr.kind + && let None = expr.qself + && expr.path.segments.len() == 1 => + { + // This is specific to "mistyped `if` condition followed by empty body" + // + // for _ in x y {} + e.span_suggestion_verbose( + prev.between(sp), + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Lit(lit) = expr.kind + && let None = lit.suffix + && let token::LitKind::Integer | token::LitKind::Float = lit.kind => + { + // for _ in x 0 {} + // for _ in x 0.0 {} + e.span_suggestion_verbose( + prev.between(sp), + format!("you might have meant to write a field access"), + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Loop(..) + | ExprKind::If(..) + | ExprKind::While(..) + | ExprKind::Match(..) + | ExprKind::ForLoop { .. } + | ExprKind::TryBlock(..) + | ExprKind::Ret(..) + | ExprKind::Closure(..) + | ExprKind::Struct(..) + | ExprKind::Try(..) = expr.kind => + { + // These are more likely to have been meant as a block body. + e.multipart_suggestion( + "try placing this code inside a block", + vec![ + (stmt_span.shrink_to_lo(), "{ ".to_string()), + (stmt_span.shrink_to_hi(), " }".to_string()), + ], + // Speculative; has been misleading in the past (#46836). + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), _) => {} + (_, _) => { + e.multipart_suggestion( + "try placing this code inside a block", + vec![ + (stmt_span.shrink_to_lo(), "{ ".to_string()), + (stmt_span.shrink_to_hi(), " }".to_string()), + ], + // Speculative; has been misleading in the past (#46836). + Applicability::MaybeIncorrect, + ); + } + } } Err(e) => { self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); diff --git a/tests/ui/issues/issue-39848.stderr b/tests/ui/issues/issue-39848.stderr index a6c6c61f170..1ffed2d4a1d 100644 --- a/tests/ui/issues/issue-39848.stderr +++ b/tests/ui/issues/issue-39848.stderr @@ -16,10 +16,10 @@ LL | if $tgt.has_$field() {} LL | get_opt!(bar, foo); | ------------------ in this macro invocation = note: this error originates in the macro `get_opt` (in Nightly builds, run with -Z macro-backtrace for more info) -help: try placing this code inside a block +help: you might have meant to write a method call | -LL | if $tgt.has_{ $field() } {} - | + + +LL | if $tgt.has_.$field() {} + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/else-no-if.stderr b/tests/ui/parser/else-no-if.stderr index 2e3e8f6b50e..c86791ec896 100644 --- a/tests/ui/parser/else-no-if.stderr +++ b/tests/ui/parser/else-no-if.stderr @@ -75,11 +75,6 @@ error: expected `{`, found `falsy` | LL | } else falsy! {} { | ^^^^^ expected `{` - | -help: try placing this code inside a block - | -LL | } else { falsy! {} } { - | + + error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:54:12 diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed new file mode 100644 index 00000000000..ea9dc4cf6cc --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed @@ -0,0 +1,39 @@ +//@ run-rustfix +#![allow(dead_code)] +fn main() { + for _ in [1, 2, 3].iter().map(|x| x) {} + //~^ ERROR expected `{`, found `map` + //~| HELP you might have meant to write a method call +} +fn foo5() { + let x = (vec![1, 2, 3],); + for _ in x.0 {} + //~^ ERROR expected `{`, found `0` + //~| HELP you might have meant to write a field access +} +fn foo6() { + let x = ((vec![1, 2, 3],),); + for _ in x.0.0 {} + //~^ ERROR expected `{`, found `0.0` + //~| HELP you might have meant to write a field access +} +fn foo7() { + let x = Some(vec![1, 2, 3]); + for _ in x.unwrap() {} + //~^ ERROR expected `{`, found `unwrap` + //~| HELP you might have meant to write a method call +} +fn foo8() { + let x = S { a: A { b: vec![1, 2, 3] } }; + for _ in x.a.b {} + //~^ ERROR expected `{`, found `a` + //~| HELP you might have meant to write a field access +} + +struct S { + a: A, +} + +struct A { + b: Vec<i32>, +} diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs new file mode 100644 index 00000000000..1833f458a8a --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs @@ -0,0 +1,39 @@ +//@ run-rustfix +#![allow(dead_code)] +fn main() { + for _ in [1, 2, 3].iter()map(|x| x) {} + //~^ ERROR expected `{`, found `map` + //~| HELP you might have meant to write a method call +} +fn foo5() { + let x = (vec![1, 2, 3],); + for _ in x 0 {} + //~^ ERROR expected `{`, found `0` + //~| HELP you might have meant to write a field access +} +fn foo6() { + let x = ((vec![1, 2, 3],),); + for _ in x 0.0 {} + //~^ ERROR expected `{`, found `0.0` + //~| HELP you might have meant to write a field access +} +fn foo7() { + let x = Some(vec![1, 2, 3]); + for _ in x unwrap() {} + //~^ ERROR expected `{`, found `unwrap` + //~| HELP you might have meant to write a method call +} +fn foo8() { + let x = S { a: A { b: vec![1, 2, 3] } }; + for _ in x a.b {} + //~^ ERROR expected `{`, found `a` + //~| HELP you might have meant to write a field access +} + +struct S { + a: A, +} + +struct A { + b: Vec<i32>, +} diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr new file mode 100644 index 00000000000..87f76efffa6 --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr @@ -0,0 +1,57 @@ +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) {} + | + + +error: expected `{`, found `0` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:10:16 + | +LL | for _ in x 0 {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0 {} + | + + +error: expected `{`, found `0.0` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:16:16 + | +LL | for _ in x 0.0 {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0.0 {} + | + + +error: expected `{`, found `unwrap` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:22:16 + | +LL | for _ in x unwrap() {} + | ^^^^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in x.unwrap() {} + | + + +error: expected `{`, found `a` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:28:16 + | +LL | for _ in x a.b {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.a.b {} + | + + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs new file mode 100644 index 00000000000..c4a9ed66a83 --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs @@ -0,0 +1,57 @@ +fn main() { + for _ in [1, 2, 3].iter()map(|x| x) {} + //~^ ERROR expected `{`, found `map` + //~| HELP you might have meant to write a method call +} +fn foo1() { + for _ in 1.3f64 cos() {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a method call +} +fn foo2() { + for _ in 1.3 cos {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a field access +} +fn foo3() { + for _ in 1 cos() {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a method call +} +fn foo4() { + for _ in 1 cos {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a field access +} +fn foo5() { + let x = (vec![1, 2, 3],); + for _ in x 0 {} + //~^ ERROR expected `{`, found `0` + //~| HELP you might have meant to write a field access +} +fn foo6() { + let x = ((vec![1, 2, 3],),); + for _ in x 0.0 {} + //~^ ERROR expected `{`, found `0.0` + //~| HELP you might have meant to write a field access +} +fn foo7() { + let x = Some(vec![1, 2, 3]); + for _ in x unwrap() {} + //~^ ERROR expected `{`, found `unwrap` + //~| HELP you might have meant to write a method call +} +fn foo8() { + let x = S { a: A { b: vec![1, 2, 3] } }; + for _ in x a.b {} + //~^ ERROR expected `{`, found `a` + //~| HELP you might have meant to write a field access +} + +struct S { + a: A, +} + +struct A { + b: Vec<i32>, +} diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr new file mode 100644 index 00000000000..bfb72b95682 --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr @@ -0,0 +1,101 @@ +error: expected `{`, found `map` + --> $DIR/missing-dot-on-if-condition-expression.rs:2: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) {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:7:21 + | +LL | for _ in 1.3f64 cos() {} + | ^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in 1.3f64.cos() {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:12:18 + | +LL | for _ in 1.3 cos {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in 1.3.cos {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:17:16 + | +LL | for _ in 1 cos() {} + | ^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in 1.cos() {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:22:16 + | +LL | for _ in 1 cos {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in 1.cos {} + | + + +error: expected `{`, found `0` + --> $DIR/missing-dot-on-if-condition-expression.rs:28:16 + | +LL | for _ in x 0 {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0 {} + | + + +error: expected `{`, found `0.0` + --> $DIR/missing-dot-on-if-condition-expression.rs:34:16 + | +LL | for _ in x 0.0 {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0.0 {} + | + + +error: expected `{`, found `unwrap` + --> $DIR/missing-dot-on-if-condition-expression.rs:40:16 + | +LL | for _ in x unwrap() {} + | ^^^^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in x.unwrap() {} + | + + +error: expected `{`, found `a` + --> $DIR/missing-dot-on-if-condition-expression.rs:46:16 + | +LL | for _ in x a.b {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.a.b {} + | + + +error: aborting due to 9 previous errors + |
