From 44fd3b4d4648c9ea2dfa052fe5d3dbb2dfb4f492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 21:00:13 +0000 Subject: [PATCH 01/13] Make `parse_pat_ident` not recover bad name --- compiler/rustc_parse/src/parser/pat.rs | 2 +- .../edition-keywords-2018-2015-parsing.stderr | 57 +----------------- .../edition-keywords-2018-2018-parsing.stderr | 57 +----------------- tests/ui/issues/issue-66706.stderr | 4 +- tests/ui/parser/issues/issue-104088.stderr | 26 +-------- tests/ui/parser/mut-patterns.stderr | 40 +------------ ...ecover-parens-around-match-arm-head.stderr | 45 ++------------ tests/ui/self/self_type_keyword.stderr | 58 +------------------ 8 files changed, 15 insertions(+), 274 deletions(-) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b05868e235af6..ff36ac952adab 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -839,7 +839,7 @@ impl<'a> Parser<'a> { binding_annotation: BindingAnnotation, syntax_loc: Option, ) -> PResult<'a, PatKind> { - let ident = self.parse_ident()?; + let ident = self.parse_ident_common(false)?; if self.may_recover() && !matches!(syntax_loc, Some(PatternLocation::FunctionParameter)) diff --git a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr index 1a4a94e973327..20a58236855ea 100644 --- a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr @@ -9,60 +9,5 @@ help: escape `async` to use it as an identifier LL | let mut r#async = 1; | ++ -error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:26:13 - | -LL | module::async(); - | ^^^^^ expected identifier, found keyword - | -help: escape `async` to use it as an identifier - | -LL | module::r#async(); - | ++ - -error: no rules expected the token `r#async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:20:31 - | -LL | r#async = consumes_async!(r#async); - | ^^^^^^^ no rules expected this token in macro call - | -note: while trying to match `async` - --> $DIR/auxiliary/edition-kw-macro-2015.rs:17:6 - | -LL | (async) => (1) - | ^^^^^ - -error: no rules expected the token `async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:21:35 - | -LL | r#async = consumes_async_raw!(async); - | ^^^^^ no rules expected this token in macro call - | -note: while trying to match `r#async` - --> $DIR/auxiliary/edition-kw-macro-2015.rs:22:6 - | -LL | (r#async) => (1) - | ^^^^^^^ - -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` - --> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23 - | -LL | ($i: ident) => ($i) - | ^ expected one of `move`, `|`, or `||` - | - ::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8 - | -LL | if passes_ident!(async) == 1 {} - | -------------------- in this macro invocation - -error[E0308]: mismatched types - --> $DIR/edition-keywords-2018-2015-parsing.rs:29:33 - | -LL | let _recovery_witness: () = 0; - | -- ^ expected `()`, found integer - | | - | expected due to this - -error: aborting due to 6 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr index 19eb7ac98239e..e904165a5ce50 100644 --- a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr @@ -9,60 +9,5 @@ help: escape `async` to use it as an identifier LL | let mut r#async = 1; | ++ -error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:26:13 - | -LL | module::async(); - | ^^^^^ expected identifier, found keyword - | -help: escape `async` to use it as an identifier - | -LL | module::r#async(); - | ++ - -error: no rules expected the token `r#async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:20:31 - | -LL | r#async = consumes_async!(r#async); - | ^^^^^^^ no rules expected this token in macro call - | -note: while trying to match `async` - --> $DIR/auxiliary/edition-kw-macro-2018.rs:17:6 - | -LL | (async) => (1) - | ^^^^^ - -error: no rules expected the token `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:21:35 - | -LL | r#async = consumes_async_raw!(async); - | ^^^^^ no rules expected this token in macro call - | -note: while trying to match `r#async` - --> $DIR/auxiliary/edition-kw-macro-2018.rs:22:6 - | -LL | (r#async) => (1) - | ^^^^^^^ - -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` - --> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23 - | -LL | ($i: ident) => ($i) - | ^ expected one of `move`, `|`, or `||` - | - ::: $DIR/edition-keywords-2018-2018-parsing.rs:24:8 - | -LL | if passes_ident!(async) == 1 {} - | -------------------- in this macro invocation - -error[E0308]: mismatched types - --> $DIR/edition-keywords-2018-2018-parsing.rs:29:33 - | -LL | let _recovery_witness: () = 0; - | -- ^ expected `()`, found integer - | | - | expected due to this - -error: aborting due to 6 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-66706.stderr b/tests/ui/issues/issue-66706.stderr index 8a30c0cad39c5..ffdd61e7723be 100644 --- a/tests/ui/issues/issue-66706.stderr +++ b/tests/ui/issues/issue-66706.stderr @@ -24,7 +24,9 @@ error: expected identifier, found reserved identifier `_` --> $DIR/issue-66706.rs:18:26 | LL | [0; match [|f @ &ref _| () ] {} ] - | ^ expected identifier, found reserved identifier + | ----- ^ expected identifier, found reserved identifier + | | + | while parsing this `match` expression error[E0282]: type annotations needed --> $DIR/issue-66706.rs:2:11 diff --git a/tests/ui/parser/issues/issue-104088.stderr b/tests/ui/parser/issues/issue-104088.stderr index 8b751759d69a6..ff24b0057f654 100644 --- a/tests/ui/parser/issues/issue-104088.stderr +++ b/tests/ui/parser/issues/issue-104088.stderr @@ -22,30 +22,6 @@ help: identifiers cannot start with a number LL | let 23name = 123; | ^^ -error: expected identifier, found `2x` - --> $DIR/issue-104088.rs:12:9 - | -LL | let 2x: i32 = 123; - | ^^ expected identifier - | -help: identifiers cannot start with a number - --> $DIR/issue-104088.rs:12:9 - | -LL | let 2x: i32 = 123; - | ^ - -error: expected identifier, found `1x` - --> $DIR/issue-104088.rs:15:9 - | -LL | let 1x = 123; - | ^^ expected identifier - | -help: identifiers cannot start with a number - --> $DIR/issue-104088.rs:15:9 - | -LL | let 1x = 123; - | ^ - error[E0308]: mismatched types --> $DIR/issue-104088.rs:5:12 | @@ -54,6 +30,6 @@ LL | if let 2e1 = 123 { | | | expected integer, found floating-point number -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/mut-patterns.stderr b/tests/ui/parser/mut-patterns.stderr index 66985c9f5e4f8..d91440d7859ed 100644 --- a/tests/ui/parser/mut-patterns.stderr +++ b/tests/ui/parser/mut-patterns.stderr @@ -72,43 +72,5 @@ help: escape `become` to use it as an identifier LL | let mut mut yield(r#become, await) = r#yield(0, 0); | ++ -error: expected identifier, found keyword `await` - --> $DIR/mut-patterns.rs:28:31 - | -LL | let mut mut yield(become, await) = r#yield(0, 0); - | ^^^^^ expected identifier, found keyword - | -help: escape `await` to use it as an identifier - | -LL | let mut mut yield(become, r#await) = r#yield(0, 0); - | ++ - -error: `mut` must be attached to each individual binding - --> $DIR/mut-patterns.rs:28:9 - | -LL | let mut mut yield(become, await) = r#yield(0, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `r#yield(mut r#become, mut r#await)` - | - = note: `mut` may be followed by `variable` and `variable @ pattern` - -error: `mut` must be attached to each individual binding - --> $DIR/mut-patterns.rs:37:9 - | -LL | let mut W(mut a, W(b, W(ref c, W(d, B { box f })))) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `W(mut a, W(mut b, W(ref c, W(mut d, B { box mut f }))))` - | - = note: `mut` may be followed by `variable` and `variable @ pattern` - -error: expected identifier, found `x` - --> $DIR/mut-patterns.rs:44:21 - | -LL | let mut $p = 0; - | ^^ expected identifier -... -LL | foo!(x); - | ------- in this macro invocation - | - = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 13 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr index 6542f440e4b09..701e2cc41b615 100644 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr @@ -1,49 +1,14 @@ -error: expected identifier, found keyword `if` - --> $DIR/recover-parens-around-match-arm-head.rs:4:12 - | -LL | (0 if true) => { - | ^^ expected identifier, found keyword - error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` --> $DIR/recover-parens-around-match-arm-head.rs:4:12 | LL | (0 if true) => { - | -^^ expected one of `)`, `,`, `...`, `..=`, `..`, or `|` - | | - | help: missing `,` - -error: expected one of `)`, `,`, `@`, or `|`, found keyword `true` - --> $DIR/recover-parens-around-match-arm-head.rs:4:15 - | -LL | (0 if true) => { - | -^^^^ expected one of `)`, `,`, `@`, or `|` - | | - | help: missing `,` + | ^^ expected one of `)`, `,`, `...`, `..=`, `..`, or `|` -error[E0308]: mismatched types - --> $DIR/recover-parens-around-match-arm-head.rs:4:9 +error: expected one of `.`, `=>`, `?`, or an operator, found `)` + --> $DIR/recover-parens-around-match-arm-head.rs:4:19 | -LL | let x = match val { - | --- this expression has type `{integer}` LL | (0 if true) => { - | ^^^^^^^^^^^ expected integer, found `(_, _, _)` - | - = note: expected type `{integer}` - found tuple `(_, _, _)` - -error[E0308]: mismatched types - --> $DIR/recover-parens-around-match-arm-head.rs:13:19 - | -LL | let _y: u32 = x; - | --- ^ expected `u32`, found `u8` - | | - | expected due to this - | -help: you can convert a `u8` to a `u32` - | -LL | let _y: u32 = x.into(); - | +++++++ + | ^ expected one of `.`, `=>`, `?`, or an operator -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/self/self_type_keyword.stderr b/tests/ui/self/self_type_keyword.stderr index fed853a7e1fd8..cdafae381aca8 100644 --- a/tests/ui/self/self_type_keyword.stderr +++ b/tests/ui/self/self_type_keyword.stderr @@ -10,26 +10,6 @@ error: expected identifier, found keyword `Self` LL | ref Self => (), | ^^^^ expected identifier, found keyword -error: `mut` must be followed by a named binding - --> $DIR/self_type_keyword.rs:16:9 - | -LL | mut Self => (), - | ^^^^ help: remove the `mut` prefix - | - = note: `mut` may be followed by `variable` and `variable @ pattern` - -error: expected identifier, found keyword `Self` - --> $DIR/self_type_keyword.rs:19:17 - | -LL | ref mut Self => (), - | ^^^^ expected identifier, found keyword - -error: expected identifier, found keyword `Self` - --> $DIR/self_type_keyword.rs:23:15 - | -LL | Foo { Self } => (), - | ^^^^ expected identifier, found keyword - error: expected identifier, found keyword `Self` --> $DIR/self_type_keyword.rs:31:26 | @@ -54,24 +34,6 @@ error: lifetimes cannot use keyword names LL | struct Bar<'Self>; | ^^^^^ -error: cannot find macro `Self` in this scope - --> $DIR/self_type_keyword.rs:21:9 - | -LL | Self!() => (), - | ^^^^ - -error[E0531]: cannot find unit struct, unit variant or constant `Self` in this scope - --> $DIR/self_type_keyword.rs:16:13 - | -LL | mut Self => (), - | ^^^^ not found in this scope - | -note: unit struct `foo::Self` exists but is inaccessible - --> $DIR/self_type_keyword.rs:2:3 - | -LL | struct Self; - | ^^^^^^^^^^^^ not accessible - error[E0392]: parameter `'Self` is never used --> $DIR/self_type_keyword.rs:6:12 | @@ -80,22 +42,6 @@ LL | struct Bar<'Self>; | = help: consider removing `'Self`, referring to it in a field, or using a marker such as `PhantomData` -error[E0308]: mismatched types - --> $DIR/self_type_keyword.rs:23:9 - | -LL | match 15 { - | -- this expression has type `{integer}` -... -LL | Foo { Self } => (), - | ^^^^^^^^^^^^ expected integer, found `Foo` - -error[E0026]: struct `Foo` does not have a field named `Self` - --> $DIR/self_type_keyword.rs:23:15 - | -LL | Foo { Self } => (), - | ^^^^ struct `Foo` does not have this field - -error: aborting due to 14 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0026, E0308, E0392, E0531. -For more information about an error, try `rustc --explain E0026`. +For more information about this error, try `rustc --explain E0392`. From ed084a93433d214edae3ee739444cbd442baf6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 21:06:20 +0000 Subject: [PATCH 02/13] When parsing patterns, bubble all errors except reserved idents that aren't likely to appear in for head or match arm --- compiler/rustc_parse/src/parser/pat.rs | 14 ++++- .../edition-keywords-2018-2015-parsing.stderr | 57 +++++++++++++++++- .../edition-keywords-2018-2018-parsing.stderr | 57 +++++++++++++++++- tests/ui/parser/mut-patterns.rs | 2 +- tests/ui/parser/mut-patterns.stderr | 40 ++++++++++++- tests/ui/self/self_type_keyword.stderr | 58 ++++++++++++++++++- 6 files changed, 221 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index ff36ac952adab..ec7c312d03f00 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -141,7 +141,19 @@ impl<'a> Parser<'a> { }; // Parse the first pattern (`p_0`). - let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?; + let mut first_pat = match self.parse_pat_no_top_alt(expected, syntax_loc) { + Ok(pat) => pat, + Err(mut err) + if self.token.is_reserved_ident() + && !self.token.is_keyword(kw::In) + && !self.token.is_keyword(kw::If) => + { + err.emit(); + self.bump(); + self.mk_pat(self.token.span, PatKind::Wild) + } + Err(err) => return Err(err), + }; if rc == RecoverComma::Yes { self.maybe_recover_unexpected_comma( first_pat.span, diff --git a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr index 20a58236855ea..1a4a94e973327 100644 --- a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr @@ -9,5 +9,60 @@ help: escape `async` to use it as an identifier LL | let mut r#async = 1; | ++ -error: aborting due to previous error +error: expected identifier, found keyword `async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:26:13 + | +LL | module::async(); + | ^^^^^ expected identifier, found keyword + | +help: escape `async` to use it as an identifier + | +LL | module::r#async(); + | ++ + +error: no rules expected the token `r#async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:20:31 + | +LL | r#async = consumes_async!(r#async); + | ^^^^^^^ no rules expected this token in macro call + | +note: while trying to match `async` + --> $DIR/auxiliary/edition-kw-macro-2015.rs:17:6 + | +LL | (async) => (1) + | ^^^^^ + +error: no rules expected the token `async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:21:35 + | +LL | r#async = consumes_async_raw!(async); + | ^^^^^ no rules expected this token in macro call + | +note: while trying to match `r#async` + --> $DIR/auxiliary/edition-kw-macro-2015.rs:22:6 + | +LL | (r#async) => (1) + | ^^^^^^^ + +error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` + --> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23 + | +LL | ($i: ident) => ($i) + | ^ expected one of `move`, `|`, or `||` + | + ::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8 + | +LL | if passes_ident!(async) == 1 {} + | -------------------- in this macro invocation + +error[E0308]: mismatched types + --> $DIR/edition-keywords-2018-2015-parsing.rs:29:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr index e904165a5ce50..19eb7ac98239e 100644 --- a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr @@ -9,5 +9,60 @@ help: escape `async` to use it as an identifier LL | let mut r#async = 1; | ++ -error: aborting due to previous error +error: expected identifier, found keyword `async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:26:13 + | +LL | module::async(); + | ^^^^^ expected identifier, found keyword + | +help: escape `async` to use it as an identifier + | +LL | module::r#async(); + | ++ + +error: no rules expected the token `r#async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:20:31 + | +LL | r#async = consumes_async!(r#async); + | ^^^^^^^ no rules expected this token in macro call + | +note: while trying to match `async` + --> $DIR/auxiliary/edition-kw-macro-2018.rs:17:6 + | +LL | (async) => (1) + | ^^^^^ + +error: no rules expected the token `async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:21:35 + | +LL | r#async = consumes_async_raw!(async); + | ^^^^^ no rules expected this token in macro call + | +note: while trying to match `r#async` + --> $DIR/auxiliary/edition-kw-macro-2018.rs:22:6 + | +LL | (r#async) => (1) + | ^^^^^^^ + +error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` + --> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23 + | +LL | ($i: ident) => ($i) + | ^ expected one of `move`, `|`, or `||` + | + ::: $DIR/edition-keywords-2018-2018-parsing.rs:24:8 + | +LL | if passes_ident!(async) == 1 {} + | -------------------- in this macro invocation + +error[E0308]: mismatched types + --> $DIR/edition-keywords-2018-2018-parsing.rs:29:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/mut-patterns.rs b/tests/ui/parser/mut-patterns.rs index 8b83d6ab2f8c8..f2d2df0af29d9 100644 --- a/tests/ui/parser/mut-patterns.rs +++ b/tests/ui/parser/mut-patterns.rs @@ -27,7 +27,7 @@ pub fn main() { struct r#yield(u8, u8); let mut mut yield(become, await) = r#yield(0, 0); //~^ ERROR `mut` on a binding may not be repeated - //~| ERROR `mut` must be attached to each individual binding + //~| ERROR `mut` must be followed by a named binding //~| ERROR expected identifier, found reserved keyword `yield` //~| ERROR expected identifier, found reserved keyword `become` //~| ERROR expected identifier, found keyword `await` diff --git a/tests/ui/parser/mut-patterns.stderr b/tests/ui/parser/mut-patterns.stderr index d91440d7859ed..6559cf09cdfcf 100644 --- a/tests/ui/parser/mut-patterns.stderr +++ b/tests/ui/parser/mut-patterns.stderr @@ -72,5 +72,43 @@ help: escape `become` to use it as an identifier LL | let mut mut yield(r#become, await) = r#yield(0, 0); | ++ -error: aborting due to 9 previous errors +error: expected identifier, found keyword `await` + --> $DIR/mut-patterns.rs:28:31 + | +LL | let mut mut yield(become, await) = r#yield(0, 0); + | ^^^^^ expected identifier, found keyword + | +help: escape `await` to use it as an identifier + | +LL | let mut mut yield(become, r#await) = r#yield(0, 0); + | ++ + +error: `mut` must be followed by a named binding + --> $DIR/mut-patterns.rs:28:9 + | +LL | let mut mut yield(become, await) = r#yield(0, 0); + | ^^^^^^^^ help: remove the `mut` prefix + | + = note: `mut` may be followed by `variable` and `variable @ pattern` + +error: `mut` must be attached to each individual binding + --> $DIR/mut-patterns.rs:37:9 + | +LL | let mut W(mut a, W(b, W(ref c, W(d, B { box f })))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `mut` to each binding: `W(mut a, W(mut b, W(ref c, W(mut d, B { box mut f }))))` + | + = note: `mut` may be followed by `variable` and `variable @ pattern` + +error: expected identifier, found `x` + --> $DIR/mut-patterns.rs:44:21 + | +LL | let mut $p = 0; + | ^^ expected identifier +... +LL | foo!(x); + | ------- in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors diff --git a/tests/ui/self/self_type_keyword.stderr b/tests/ui/self/self_type_keyword.stderr index cdafae381aca8..fed853a7e1fd8 100644 --- a/tests/ui/self/self_type_keyword.stderr +++ b/tests/ui/self/self_type_keyword.stderr @@ -10,6 +10,26 @@ error: expected identifier, found keyword `Self` LL | ref Self => (), | ^^^^ expected identifier, found keyword +error: `mut` must be followed by a named binding + --> $DIR/self_type_keyword.rs:16:9 + | +LL | mut Self => (), + | ^^^^ help: remove the `mut` prefix + | + = note: `mut` may be followed by `variable` and `variable @ pattern` + +error: expected identifier, found keyword `Self` + --> $DIR/self_type_keyword.rs:19:17 + | +LL | ref mut Self => (), + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `Self` + --> $DIR/self_type_keyword.rs:23:15 + | +LL | Foo { Self } => (), + | ^^^^ expected identifier, found keyword + error: expected identifier, found keyword `Self` --> $DIR/self_type_keyword.rs:31:26 | @@ -34,6 +54,24 @@ error: lifetimes cannot use keyword names LL | struct Bar<'Self>; | ^^^^^ +error: cannot find macro `Self` in this scope + --> $DIR/self_type_keyword.rs:21:9 + | +LL | Self!() => (), + | ^^^^ + +error[E0531]: cannot find unit struct, unit variant or constant `Self` in this scope + --> $DIR/self_type_keyword.rs:16:13 + | +LL | mut Self => (), + | ^^^^ not found in this scope + | +note: unit struct `foo::Self` exists but is inaccessible + --> $DIR/self_type_keyword.rs:2:3 + | +LL | struct Self; + | ^^^^^^^^^^^^ not accessible + error[E0392]: parameter `'Self` is never used --> $DIR/self_type_keyword.rs:6:12 | @@ -42,6 +80,22 @@ LL | struct Bar<'Self>; | = help: consider removing `'Self`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to 7 previous errors +error[E0308]: mismatched types + --> $DIR/self_type_keyword.rs:23:9 + | +LL | match 15 { + | -- this expression has type `{integer}` +... +LL | Foo { Self } => (), + | ^^^^^^^^^^^^ expected integer, found `Foo` + +error[E0026]: struct `Foo` does not have a field named `Self` + --> $DIR/self_type_keyword.rs:23:15 + | +LL | Foo { Self } => (), + | ^^^^ struct `Foo` does not have this field + +error: aborting due to 14 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0026, E0308, E0392, E0531. +For more information about an error, try `rustc --explain E0026`. From 075c599188db71474eb2ad3c8b89287f79ac57e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 21:35:21 +0000 Subject: [PATCH 03/13] More accurate span for unnecessary parens suggestion --- compiler/rustc_parse/src/errors.rs | 6 ++-- .../rustc_parse/src/parser/diagnostics.rs | 29 ++++--------------- compiler/rustc_parse/src/parser/expr.rs | 5 +++- ...recover-for-loop-parens-around-head.stderr | 2 +- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e5d4cb6f4da27..714ec7f2742a7 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1275,12 +1275,10 @@ pub(crate) struct ParenthesesInForHead { #[derive(Subdiagnostic)] #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { - #[suggestion_part(code = "{left_snippet}")] + #[suggestion_part(code = " ")] pub left: Span, - pub left_snippet: String, - #[suggestion_part(code = "{right_snippet}")] + #[suggestion_part(code = " ")] pub right: Span, - pub right_snippet: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ecb840f067eb3..0d42035e74bab 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2001,37 +2001,18 @@ impl<'a> Parser<'a> { pub(super) fn recover_parens_around_for_head( &mut self, pat: P, - begin_paren: Option, + begin_paren: Option<(Span, Span)>, ) -> P { match (&self.token.kind, begin_paren) { - (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => { + (token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => { + let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); self.bump(); - - let sm = self.sess.source_map(); - let left = begin_par_sp; - let right = self.prev_token.span; - let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) - && !snip.ends_with(' ') - { - " ".to_string() - } else { - "".to_string() - }; - - let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) - && !snip.starts_with(' ') - { - " ".to_string() - } else { - "".to_string() - }; - self.sess.emit_err(ParenthesesInForHead { - span: vec![left, right], + span: vec![begin_par_sp, self.prev_token.span], // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet }, + sugg: ParenthesesInForHeadSugg { left, right }, }); // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e334837c1d41f..8d04769a56846 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2615,7 +2615,10 @@ impl<'a> Parser<'a> { // This is used below for recovery in case of `for ( $stuff ) $block` // in which case we will suggest `for $stuff $block`. let begin_paren = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => Some(self.token.span), + token::OpenDelim(Delimiter::Parenthesis) => Some(( + self.token.span, + self.prev_token.span.between(self.look_ahead(1, |t| t.span)), + )), _ => None, }; diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr index 3bad29f20afc5..58c83b6568052 100644 --- a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr @@ -13,7 +13,7 @@ LL | for ( elem in vec ) { help: remove parentheses in `for` loop | LL - for ( elem in vec ) { -LL + for elem in vec { +LL + for elem in vec { | error[E0308]: mismatched types From 1994abed74b3f39297f49a9caed1513eb12b21b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 23:19:14 +0000 Subject: [PATCH 04/13] Bubble parse error when expecting `)` --- compiler/rustc_parse/src/parser/mod.rs | 3 +++ .../lint/issue-103435-extra-parentheses.fixed | 2 +- .../issue-103435-extra-parentheses.stderr | 14 +------------ tests/ui/parser/issues/issue-68890.stderr | 14 +++++-------- tests/ui/parser/recover/recover-enum2.stderr | 14 ++++++++++++- ...recover-for-loop-parens-around-head.stderr | 21 +------------------ ...ecover-parens-around-match-arm-head.stderr | 8 +------ 7 files changed, 25 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 9bd436f01ac70..c680a950584dd 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -875,6 +875,9 @@ impl<'a> Parser<'a> { if self.token == token::Colon { // we will try to recover in `maybe_recover_struct_lit_bad_delims` return Err(expect_err); + } else if let [token::CloseDelim(Delimiter::Parenthesis)] = kets + { + return Err(expect_err); } else { expect_err.emit(); break; diff --git a/tests/ui/lint/issue-103435-extra-parentheses.fixed b/tests/ui/lint/issue-103435-extra-parentheses.fixed index 2b01b414baa6e..3c23ec3b8b9d1 100644 --- a/tests/ui/lint/issue-103435-extra-parentheses.fixed +++ b/tests/ui/lint/issue-103435-extra-parentheses.fixed @@ -12,7 +12,7 @@ fn main() { //~^ ERROR unnecessary parentheses around `if` condition // reported by parser - for _x in 1..10 {} + for(_x in 1..10){} //~^ ERROR expected one of //~| ERROR unexpected parentheses surrounding } diff --git a/tests/ui/lint/issue-103435-extra-parentheses.stderr b/tests/ui/lint/issue-103435-extra-parentheses.stderr index 29c41c91050b9..15b906f256e08 100644 --- a/tests/ui/lint/issue-103435-extra-parentheses.stderr +++ b/tests/ui/lint/issue-103435-extra-parentheses.stderr @@ -4,18 +4,6 @@ error: expected one of `)`, `,`, `@`, or `|`, found keyword `in` LL | for(_x in 1..10){} | ^^ expected one of `)`, `,`, `@`, or `|` -error: unexpected parentheses surrounding `for` loop head - --> $DIR/issue-103435-extra-parentheses.rs:15:8 - | -LL | for(_x in 1..10){} - | ^ ^ - | -help: remove parentheses in `for` loop - | -LL - for(_x in 1..10){} -LL + for _x in 1..10 {} - | - error: unnecessary parentheses around pattern --> $DIR/issue-103435-extra-parentheses.rs:5:11 | @@ -57,5 +45,5 @@ LL - if(2 == 1){} LL + if 2 == 1 {} | -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/parser/issues/issue-68890.stderr b/tests/ui/parser/issues/issue-68890.stderr index 0d7b53a67c581..72332e85f1820 100644 --- a/tests/ui/parser/issues/issue-68890.stderr +++ b/tests/ui/parser/issues/issue-68890.stderr @@ -8,15 +8,11 @@ error: expected one of `)`, `+`, or `,`, found `a` --> $DIR/issue-68890.rs:1:15 | LL | enum e{A((?'a a+?+l))} - | ^ expected one of `)`, `+`, or `,` - -error: expected item, found `)` - --> $DIR/issue-68890.rs:1:21 - | -LL | enum e{A((?'a a+?+l))} - | ^ expected item + | - ^ expected one of `)`, `+`, or `,` + | | + | while parsing this enum | - = note: for a full list of items that can appear in modules, see + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/recover/recover-enum2.stderr b/tests/ui/parser/recover/recover-enum2.stderr index 7634bca921c86..cecb26218eba3 100644 --- a/tests/ui/parser/recover/recover-enum2.stderr +++ b/tests/ui/parser/recover/recover-enum2.stderr @@ -9,8 +9,20 @@ LL | abc: {}, error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{` --> $DIR/recover-enum2.rs:25:22 | +LL | enum Test4 { + | ----- while parsing this enum LL | Nope(i32 {}) | ^ expected one of 7 possible tokens + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + +error: expected item, found `}` + --> $DIR/recover-enum2.rs:28:1 + | +LL | } + | ^ expected item + | + = note: for a full list of items that can appear in modules, see -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr index 58c83b6568052..b1540e19f4468 100644 --- a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr @@ -4,24 +4,5 @@ error: expected one of `)`, `,`, `@`, or `|`, found keyword `in` LL | for ( elem in vec ) { | ^^ expected one of `)`, `,`, `@`, or `|` -error: unexpected parentheses surrounding `for` loop head - --> $DIR/recover-for-loop-parens-around-head.rs:10:9 - | -LL | for ( elem in vec ) { - | ^ ^ - | -help: remove parentheses in `for` loop - | -LL - for ( elem in vec ) { -LL + for elem in vec { - | - -error[E0308]: mismatched types - --> $DIR/recover-for-loop-parens-around-head.rs:13:38 - | -LL | const RECOVERY_WITNESS: () = 0; - | ^ expected `()`, found integer - -error: aborting due to 3 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr index 701e2cc41b615..526d3017b3caf 100644 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr @@ -4,11 +4,5 @@ error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` LL | (0 if true) => { | ^^ expected one of `)`, `,`, `...`, `..=`, `..`, or `|` -error: expected one of `.`, `=>`, `?`, or an operator, found `)` - --> $DIR/recover-parens-around-match-arm-head.rs:4:19 - | -LL | (0 if true) => { - | ^ expected one of `.`, `=>`, `?`, or an operator - -error: aborting due to 2 previous errors +error: aborting due to previous error From db39068ad7060bf2375535e50aeb3e42d9f939bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 23:24:42 +0000 Subject: [PATCH 05/13] Change enum parse recovery --- compiler/rustc_parse/src/parser/item.rs | 43 ++++++++++++++++---- tests/ui/parser/issues/issue-68890.stderr | 10 ++++- tests/ui/parser/recover/recover-enum2.stderr | 12 +----- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 23886b1208bb4..5e73472c842e6 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1415,8 +1415,8 @@ impl<'a> Parser<'a> { self.bump(); (thin_vec![], false) } else { - self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant()).map_err( - |mut err| { + self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant(id.span)) + .map_err(|mut err| { err.span_label(id.span, "while parsing this enum"); if self.token == token::Colon { let snapshot = self.create_snapshot_for_diagnostic(); @@ -1436,17 +1436,17 @@ impl<'a> Parser<'a> { } self.restore_snapshot(snapshot); } - self.recover_stmt(); + self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); + self.bump(); // } err - }, - )? + })? }; let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() }; Ok((id, ItemKind::Enum(enum_definition, generics))) } - fn parse_enum_variant(&mut self) -> PResult<'a, Option> { + fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option> { self.recover_diff_marker(); let variant_attrs = self.parse_outer_attributes()?; self.recover_diff_marker(); @@ -1476,10 +1476,37 @@ impl<'a> Parser<'a> { let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) { // Parse a struct variant. let (fields, recovered) = - this.parse_record_struct_body("struct", ident.span, false)?; + match this.parse_record_struct_body("struct", ident.span, false) { + Ok((fields, recovered)) => (fields, recovered), + Err(mut err) => { + if this.token == token::Colon { + // We handle `enum` to `struct` suggestion in the caller. + return Err(err); + } + this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); + this.bump(); // } + err.span_label(span, "while parsing this enum"); + err.emit(); + (thin_vec![], true) + } + }; VariantData::Struct(fields, recovered) } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) { - VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID) + let body = match this.parse_tuple_struct_body() { + Ok(body) => body, + Err(mut err) => { + if this.token == token::Colon { + // We handle `enum` to `struct` suggestion in the caller. + return Err(err); + } + this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); + this.bump(); // ) + err.span_label(span, "while parsing this enum"); + err.emit(); + thin_vec![] + } + }; + VariantData::Tuple(body, DUMMY_NODE_ID) } else { VariantData::Unit(DUMMY_NODE_ID) }; diff --git a/tests/ui/parser/issues/issue-68890.stderr b/tests/ui/parser/issues/issue-68890.stderr index 72332e85f1820..914c90565f501 100644 --- a/tests/ui/parser/issues/issue-68890.stderr +++ b/tests/ui/parser/issues/issue-68890.stderr @@ -11,8 +11,14 @@ LL | enum e{A((?'a a+?+l))} | - ^ expected one of `)`, `+`, or `,` | | | while parsing this enum + +error: expected item, found `)` + --> $DIR/issue-68890.rs:1:21 + | +LL | enum e{A((?'a a+?+l))} + | ^ expected item | - = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + = note: for a full list of items that can appear in modules, see -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/parser/recover/recover-enum2.stderr b/tests/ui/parser/recover/recover-enum2.stderr index cecb26218eba3..71c2804c3a7fa 100644 --- a/tests/ui/parser/recover/recover-enum2.stderr +++ b/tests/ui/parser/recover/recover-enum2.stderr @@ -13,16 +13,6 @@ LL | enum Test4 { | ----- while parsing this enum LL | Nope(i32 {}) | ^ expected one of 7 possible tokens - | - = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` - -error: expected item, found `}` - --> $DIR/recover-enum2.rs:28:1 - | -LL | } - | ^ expected item - | - = note: for a full list of items that can appear in modules, see -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From c47318983bcad308e0cf438fcd7d7104db607b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 23:31:13 +0000 Subject: [PATCH 06/13] Account for `(pat if expr) => {}` When encountering match arm (pat if expr) => {}, recover and suggest removing parentheses. Fix #100825. --- compiler/rustc_parse/messages.ftl | 3 + compiler/rustc_parse/src/errors.rs | 18 +++ compiler/rustc_parse/src/parser/expr.rs | 125 ++++++++++++------ .../recover-parens-around-match-arm-head.rs | 5 +- ...ecover-parens-around-match-arm-head.stderr | 28 +++- 5 files changed, 132 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index eadd2892d93ec..1c3c433d8b731 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -777,6 +777,9 @@ parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in patter parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head .suggestion = remove parentheses in `for` loop +parse_unexpected_parentheses_in_match_arm_pattern = unexpected parentheses surrounding `match` arm pattern + .suggestion = remove parentheses surrounding the pattern + parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters .note = you cannot use `Self` as a generic parameter because it is reserved for associated items diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 714ec7f2742a7..03e047b297dc5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1281,6 +1281,24 @@ pub(crate) struct ParenthesesInForHeadSugg { pub right: Span, } +#[derive(Diagnostic)] +#[diag(parse_unexpected_parentheses_in_match_arm_pattern)] +pub(crate) struct ParenthesesInMatchPat { + #[primary_span] + pub span: Vec, + #[subdiagnostic] + pub sugg: ParenthesesInMatchPatSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct ParenthesesInMatchPatSugg { + #[suggestion_part(code = "")] + pub left: Span, + #[suggestion_part(code = "")] + pub right: Span, +} + #[derive(Diagnostic)] #[diag(parse_doc_comment_on_param_type)] pub(crate) struct DocCommentOnParamType { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8d04769a56846..8a4b7b5b99eaa 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -10,7 +10,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{GenBlockKind, Path, PathSegment}; +use ast::{GenBlockKind, Pat, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -2856,47 +2856,10 @@ impl<'a> Parser<'a> { } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { - // Used to check the `let_chains` and `if_let_guard` features mostly by scanning - // `&&` tokens. - fn check_let_expr(expr: &Expr) -> (bool, bool) { - match &expr.kind { - ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { - let lhs_rslt = check_let_expr(lhs); - let rhs_rslt = check_let_expr(rhs); - (lhs_rslt.0 || rhs_rslt.0, false) - } - ExprKind::Let(..) => (true, true), - _ => (false, true), - } - } let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; - let pat = this.parse_pat_allow_top_alt( - None, - RecoverComma::Yes, - RecoverColon::Yes, - CommaRecoveryMode::EitherTupleOrPipe, - )?; - let guard = if this.eat_keyword(kw::If) { - let if_span = this.prev_token.span; - let mut cond = this.parse_match_guard_condition()?; - - CondChecker::new(this).visit_expr(&mut cond); - - let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); - if has_let_expr { - if does_not_have_bin_op { - // Remove the last feature gating of a `let` expression since it's stable. - this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); - } - let span = if_span.to(cond.span); - this.sess.gated_spans.gate(sym::if_let_guard, span); - } - Some(cond) - } else { - None - }; + let (pat, guard) = this.parse_match_arm_pat_and_guard()?; let arrow_span = this.token.span; if let Err(mut err) = this.expect(&token::FatArrow) { // We might have a `=>` -> `=` or `->` typo (issue #89396). @@ -3026,6 +2989,90 @@ impl<'a> Parser<'a> { }) } + fn parse_match_arm_guard(&mut self) -> PResult<'a, Option>> { + // Used to check the `let_chains` and `if_let_guard` features mostly by scanning + // `&&` tokens. + fn check_let_expr(expr: &Expr) -> (bool, bool) { + match &expr.kind { + ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { + let lhs_rslt = check_let_expr(lhs); + let rhs_rslt = check_let_expr(rhs); + (lhs_rslt.0 || rhs_rslt.0, false) + } + ExprKind::Let(..) => (true, true), + _ => (false, true), + } + } + if !self.eat_keyword(kw::If) { + // No match arm guard present. + return Ok(None); + } + + let if_span = self.prev_token.span; + let mut cond = self.parse_match_guard_condition()?; + + CondChecker::new(self).visit_expr(&mut cond); + + let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); + if has_let_expr { + if does_not_have_bin_op { + // Remove the last feature gating of a `let` expression since it's stable. + self.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + } + let span = if_span.to(cond.span); + self.sess.gated_spans.gate(sym::if_let_guard, span); + } + Ok(Some(cond)) + } + + fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P, Option>)> { + if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) { + // Detect and recover from `($pat if $cond) => $arm`. + let left = self.token.span; + match self.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + ) { + Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), + Err(err) + if let prev_sp = self.prev_token.span + && let true = self.eat_keyword(kw::If) => + { + // We know for certain we've found `($pat if` so far. + let mut cond = match self.parse_match_guard_condition() { + Ok(cond) => cond, + Err(cond_err) => { + cond_err.cancel(); + return Err(err); + } + }; + err.cancel(); + CondChecker::new(self).visit_expr(&mut cond); + self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); + self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + let right = self.prev_token.span; + self.sess.emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) + } + Err(err) => Err(err), + } + } else { + // Regular parser flow: + let pat = self.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::EitherTupleOrPipe, + )?; + Ok((pat, self.parse_match_arm_guard()?)) + } + } + fn parse_match_guard_condition(&mut self) -> PResult<'a, P> { self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err( |mut err| { diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs index 9ed733bf07947..382d99cd79d5d 100644 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs @@ -2,10 +2,7 @@ fn main() { let val = 42; let x = match val { (0 if true) => { - //~^ ERROR expected identifier, found keyword `if` - //~| ERROR expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` - //~| ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `true` - //~| ERROR mismatched types + //~^ ERROR unexpected parentheses surrounding `match` arm pattern 42u8 } _ => 0u8, diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr index 526d3017b3caf..0d1143fbc99f4 100644 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr @@ -1,8 +1,28 @@ -error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` - --> $DIR/recover-parens-around-match-arm-head.rs:4:12 +error: unexpected parentheses surrounding `match` arm pattern + --> $DIR/recover-parens-around-match-arm-head.rs:4:9 | LL | (0 if true) => { - | ^^ expected one of `)`, `,`, `...`, `..=`, `..`, or `|` + | ^ ^ + | +help: remove parentheses surrounding the pattern + | +LL - (0 if true) => { +LL + 0 if true => { + | + +error[E0308]: mismatched types + --> $DIR/recover-parens-around-match-arm-head.rs:10:19 + | +LL | let _y: u32 = x; + | --- ^ expected `u32`, found `u8` + | | + | expected due to this + | +help: you can convert a `u8` to a `u32` + | +LL | let _y: u32 = x.into(); + | +++++++ -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`. From 0ff331bc78f55d637d0b943686d783642a017512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Nov 2023 23:41:49 +0000 Subject: [PATCH 07/13] Change how `for (x in foo) {}` is handled Use the same approach used for match arm patterns. --- .../rustc_parse/src/parser/diagnostics.rs | 43 ++--------- compiler/rustc_parse/src/parser/expr.rs | 73 ++++++++++++++----- .../lint/issue-103435-extra-parentheses.fixed | 5 +- .../ui/lint/issue-103435-extra-parentheses.rs | 7 +- .../issue-103435-extra-parentheses.stderr | 18 +++-- tests/ui/parser/recover/recover-enum2.rs | 3 +- tests/ui/parser/recover/recover-enum2.stderr | 19 ++++- .../recover-for-loop-parens-around-head.fixed | 15 ++++ .../recover-for-loop-parens-around-head.rs | 8 +- ...recover-for-loop-parens-around-head.stderr | 28 +++++-- 10 files changed, 138 insertions(+), 81 deletions(-) create mode 100644 tests/ui/parser/recover/recover-for-loop-parens-around-head.fixed diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0d42035e74bab..8921c1c6a03fd 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -11,12 +11,12 @@ use crate::errors::{ DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon, - IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, - PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, - StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, - StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, - TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, + SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, + StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, + SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::fluent_generated as fluent; @@ -1994,37 +1994,6 @@ impl<'a> Parser<'a> { } } - /// Recovers a situation like `for ( $pat in $expr )` - /// and suggest writing `for $pat in $expr` instead. - /// - /// This should be called before parsing the `$block`. - pub(super) fn recover_parens_around_for_head( - &mut self, - pat: P, - begin_paren: Option<(Span, Span)>, - ) -> P { - match (&self.token.kind, begin_paren) { - (token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => { - let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); - self.bump(); - self.sess.emit_err(ParenthesesInForHead { - span: vec![begin_par_sp, self.prev_token.span], - // With e.g. `for (x) in y)` this would replace `(x) in y)` - // with `x) in y)` which is syntactically invalid. - // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { left, right }, - }); - - // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. - pat.and_then(|pat| match pat.kind { - PatKind::Paren(pat) => pat, - _ => P(pat), - }) - } - _ => pat, - } - } - pub(super) fn recover_seq_parse_error( &mut self, delim: Delimiter, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8a4b7b5b99eaa..88c0c9703dbb8 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2609,33 +2609,66 @@ impl<'a> Parser<'a> { } } - /// Parses `for in ` (`for` token already eaten). - fn parse_expr_for(&mut self, opt_label: Option