From 80a1536c7ab73c867d5a60f4441058e7e2231d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 22 Jan 2023 12:05:36 +0100 Subject: [PATCH] recover more unbraced const args --- .../rustc_parse/src/parser/diagnostics.rs | 22 ++++ compiler/rustc_parse/src/parser/path.rs | 48 +++++--- .../const-generics/bad-const-generic-exprs.rs | 34 +++++- .../bad-const-generic-exprs.stderr | 106 +++++++++++++++++- 4 files changed, 188 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4c918c6702ed9..6596a06afab65 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2353,6 +2353,28 @@ impl<'a> Parser<'a> { Err(err) } + /// Try to recover from an unbraced const argument whose first token [could begin a type][ty]. + /// + /// [ty]: token::Token::can_begin_type + pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty( + &mut self, + mut snapshot: SnapshotParser<'a>, + ) -> Option> { + match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) { + // Since we don't know the exact reason why we failed to parse the type or the + // expression, employ a simple heuristic to weed out some pathological cases. + Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => { + self.restore_snapshot(snapshot); + Some(expr) + } + Ok(_) => None, + Err(err) => { + err.cancel(); + None + } + } + } + /// Creates a dummy const argument, and reports that the expression must be enclosed in braces pub fn dummy_const_arg_needs_braces( &self, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 5333d3b8587dd..2e706a00cf7f3 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -675,22 +675,42 @@ impl<'a> Parser<'a> { GenericArg::Const(self.parse_const_arg()?) } else if self.check_type() { // Parse type argument. - let is_const_fn = - self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)); - let mut snapshot = self.create_snapshot_for_diagnostic(); + + // Proactively create a parser snapshot enabling us to rewind and try to reparse the + // input as a const expression in case we fail to parse a type. If we successfully + // do so, we will report an error that it needs to be wrapped in braces. + let mut snapshot = None; + if self.may_recover() && self.token.can_begin_expr() { + snapshot = Some(self.create_snapshot_for_diagnostic()); + } + match self.parse_ty() { - Ok(ty) => GenericArg::Type(ty), + Ok(ty) => { + // Since the type parser recovers from some malformed slice and array types and + // successfully returns a type, we need to look for `TyKind::Err`s in the + // type to determine if error recovery has occurred and if the input is not a + // syntactically valid type after all. + if let ast::TyKind::Slice(inner_ty) | ast::TyKind::Array(inner_ty, _) = &ty.kind + && let ast::TyKind::Err = inner_ty.kind + && let Some(snapshot) = snapshot + && let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot) + { + return Ok(Some(self.dummy_const_arg_needs_braces( + self.struct_span_err(expr.span, "invalid const generic expression"), + expr.span, + ))); + } + + GenericArg::Type(ty) + } Err(err) => { - if is_const_fn { - match (*snapshot).parse_expr_res(Restrictions::CONST_EXPR, None) { - Ok(expr) => { - self.restore_snapshot(snapshot); - return Ok(Some(self.dummy_const_arg_needs_braces(err, expr.span))); - } - Err(err) => { - err.cancel(); - } - } + if let Some(snapshot) = snapshot + && let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot) + { + return Ok(Some(self.dummy_const_arg_needs_braces( + err, + expr.span, + ))); } // Try to recover from possible `const` arg without braces. return self.recover_const_arg(start, err).map(Some); diff --git a/tests/ui/const-generics/bad-const-generic-exprs.rs b/tests/ui/const-generics/bad-const-generic-exprs.rs index ca91643edf727..423752ca25eba 100644 --- a/tests/ui/const-generics/bad-const-generic-exprs.rs +++ b/tests/ui/const-generics/bad-const-generic-exprs.rs @@ -13,10 +13,34 @@ fn main() { let _: Wow; //~^ ERROR expected one of //~| HELP expressions must be enclosed in braces to be used as const generic arguments - - // FIXME(compiler-errors): This one is still unsatisfying, - // and probably a case I could see someone typing by accident.. + let _: Wow<[]>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments let _: Wow<[12]>; - //~^ ERROR expected type, found - //~| ERROR type provided when a constant was expected + //~^ ERROR expected type + //~| ERROR invalid const generic expression + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<[0, 1, 3]>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<[0xff; 8]>; + //~^ ERROR expected type + //~| ERROR invalid const generic expression + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<[1, 2]>; // Regression test for issue #81698. + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<&0>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<("", 0)>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<(1 + 2) * 3>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + // FIXME(fmease): This one is pretty bad. + let _: Wow; + //~^ ERROR expected one of + //~| HELP you might have meant to end the type parameters here } diff --git a/tests/ui/const-generics/bad-const-generic-exprs.stderr b/tests/ui/const-generics/bad-const-generic-exprs.stderr index 24668b08b8a56..17a63a96fe4fe 100644 --- a/tests/ui/const-generics/bad-const-generic-exprs.stderr +++ b/tests/ui/const-generics/bad-const-generic-exprs.stderr @@ -42,18 +42,118 @@ help: expressions must be enclosed in braces to be used as const generic argumen LL | let _: Wow<{ A.0 }>; | + + +error: expected type, found `]` + --> $DIR/bad-const-generic-exprs.rs:16:17 + | +LL | let _: Wow<[]>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [] }>; + | + + + error: expected type, found `12` --> $DIR/bad-const-generic-exprs.rs:19:17 | LL | let _: Wow<[12]>; | ^^ expected type -error[E0747]: type provided when a constant was expected +error: invalid const generic expression --> $DIR/bad-const-generic-exprs.rs:19:16 | LL | let _: Wow<[12]>; | ^^^^ + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [12] }>; + | + + + +error: expected type, found `0` + --> $DIR/bad-const-generic-exprs.rs:23:17 + | +LL | let _: Wow<[0, 1, 3]>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [0, 1, 3] }>; + | + + + +error: expected type, found `0xff` + --> $DIR/bad-const-generic-exprs.rs:26:17 + | +LL | let _: Wow<[0xff; 8]>; + | ^^^^ expected type + +error: invalid const generic expression + --> $DIR/bad-const-generic-exprs.rs:26:16 + | +LL | let _: Wow<[0xff; 8]>; + | ^^^^^^^^^ + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [0xff; 8] }>; + | + + + +error: expected type, found `1` + --> $DIR/bad-const-generic-exprs.rs:30:17 + | +LL | let _: Wow<[1, 2]>; // Regression test for issue #81698. + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [1, 2] }>; // Regression test for issue #81698. + | + + + +error: expected type, found `0` + --> $DIR/bad-const-generic-exprs.rs:33:17 + | +LL | let _: Wow<&0>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ &0 }>; + | + + + +error: expected type, found `""` + --> $DIR/bad-const-generic-exprs.rs:36:17 + | +LL | let _: Wow<("", 0)>; + | ^^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ ("", 0) }>; + | + + + +error: expected type, found `1` + --> $DIR/bad-const-generic-exprs.rs:39:17 + | +LL | let _: Wow<(1 + 2) * 3>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ (1 + 2) * 3 }>; + | + + + +error: expected one of `,` or `>`, found `0` + --> $DIR/bad-const-generic-exprs.rs:43:17 + | +LL | let _: Wow; + | - ^ expected one of `,` or `>` + | | + | while parsing the type for `_` + | +help: you might have meant to end the type parameters here + | +LL | let _: Wow0>; + | + -error: aborting due to 6 previous errors +error: aborting due to 15 previous errors -For more information about this error, try `rustc --explain E0747`.