From 6b52ac240a7b855630f766b58c5def50b6a94071 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 13 Jan 2022 23:03:10 -0800 Subject: [PATCH] Suggest {} around more bad const generic exprs --- .../rustc_parse/src/parser/diagnostics.rs | 39 +++++++----- compiler/rustc_parse/src/parser/path.rs | 19 +++++- .../const-generics/bad-const-generic-exprs.rs | 22 +++++++ .../bad-const-generic-exprs.stderr | 59 +++++++++++++++++++ 4 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 src/test/ui/const-generics/bad-const-generic-exprs.rs create mode 100644 src/test/ui/const-generics/bad-const-generic-exprs.stderr diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f1c2dcf10e869..50310b28f9a73 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2030,7 +2030,7 @@ impl<'a> Parser<'a> { start: Span, mut err: DiagnosticBuilder<'a, ErrorReported>, ) -> PResult<'a, GenericArg> { - let is_op = AssocOp::from_token(&self.token) + let is_op_or_dot = AssocOp::from_token(&self.token) .and_then(|op| { if let AssocOp::Greater | AssocOp::Less @@ -2046,17 +2046,18 @@ impl<'a> Parser<'a> { Some(op) } }) - .is_some(); + .is_some() + || self.token.kind == TokenKind::Dot; // This will be true when a trait object type `Foo +` or a path which was a `const fn` with // type params has been parsed. let was_op = matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt); - if !is_op && !was_op { + if !is_op_or_dot && !was_op { // We perform these checks and early return to avoid taking a snapshot unnecessarily. return Err(err); } let snapshot = self.clone(); - if is_op { + if is_op_or_dot { self.bump(); } match self.parse_expr_res(Restrictions::CONST_EXPR, None) { @@ -2080,18 +2081,7 @@ impl<'a> Parser<'a> { // | // LL | let sr: Vec<{ (u32, _, _) = vec![] }; // | ^ ^ - err.multipart_suggestion( - "expressions must be enclosed in braces to be used as const generic \ - arguments", - vec![ - (start.shrink_to_lo(), "{ ".to_string()), - (expr.span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MaybeIncorrect, - ); - let value = self.mk_expr_err(start.to(expr.span)); - err.emit(); - return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); + return Ok(self.dummy_const_arg_needs_braces(err, start.to(expr.span))); } } Err(err) => { @@ -2102,6 +2092,23 @@ impl<'a> Parser<'a> { Err(err) } + /// Creates a dummy const argument, and reports that the expression must be enclosed in braces + pub fn dummy_const_arg_needs_braces( + &self, + mut err: DiagnosticBuilder<'a, ErrorReported>, + span: Span, + ) -> GenericArg { + err.multipart_suggestion( + "expressions must be enclosed in braces to be used as const generic \ + arguments", + vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())], + Applicability::MaybeIncorrect, + ); + let value = self.mk_expr_err(span); + err.emit(); + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) + } + /// Get the diagnostics for the cases where `move async` is found. /// /// `move_async_span` starts at the 'm' of the move keyword and ends with the 'c' of the async keyword diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index b5857e0597055..0ffc9d0935524 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,5 +1,5 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; -use super::{Parser, TokenType}; +use super::{Parser, Restrictions, TokenType}; use crate::maybe_whole; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; @@ -634,7 +634,22 @@ impl<'a> Parser<'a> { } else if self.token.is_keyword(kw::Const) { return self.recover_const_param_declaration(ty_generics); } else { - return Ok(None); + // Fall back by trying to parse a const-expr expression. If we successfully do so, + // then we should report an error that it needs to be wrapped in braces. + let snapshot = self.clone(); + match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + Ok(expr) => { + return Ok(Some(self.dummy_const_arg_needs_braces( + self.struct_span_err(expr.span, "invalid const generic expression"), + expr.span, + ))); + } + Err(err) => { + *self = snapshot; + err.cancel(); + return Ok(None); + } + } }; Ok(Some(arg)) } diff --git a/src/test/ui/const-generics/bad-const-generic-exprs.rs b/src/test/ui/const-generics/bad-const-generic-exprs.rs new file mode 100644 index 0000000000000..ca91643edf727 --- /dev/null +++ b/src/test/ui/const-generics/bad-const-generic-exprs.rs @@ -0,0 +1,22 @@ +struct Wow; + +fn main() { + let _: Wow; + //~^ ERROR invalid const generic expression + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<|| ()>; + //~^ ERROR invalid const generic expression + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow; + //~^ ERROR expected one of + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + 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<[12]>; + //~^ ERROR expected type, found + //~| ERROR type provided when a constant was expected +} diff --git a/src/test/ui/const-generics/bad-const-generic-exprs.stderr b/src/test/ui/const-generics/bad-const-generic-exprs.stderr new file mode 100644 index 0000000000000..24668b08b8a56 --- /dev/null +++ b/src/test/ui/const-generics/bad-const-generic-exprs.stderr @@ -0,0 +1,59 @@ +error: invalid const generic expression + --> $DIR/bad-const-generic-exprs.rs:4:16 + | +LL | let _: Wow; + | ^^^^^^^^^^ + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ if true {} }>; + | + + + +error: invalid const generic expression + --> $DIR/bad-const-generic-exprs.rs:7:16 + | +LL | let _: Wow<|| ()>; + | ^^^^^ + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ || () }>; + | + + + +error: expected one of `,` or `>`, found `.` + --> $DIR/bad-const-generic-exprs.rs:10:17 + | +LL | let _: Wow; + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ A.b }>; + | + + + +error: expected one of `,` or `>`, found `.` + --> $DIR/bad-const-generic-exprs.rs:13:17 + | +LL | let _: Wow; + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ A.0 }>; + | + + + +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 + --> $DIR/bad-const-generic-exprs.rs:19:16 + | +LL | let _: Wow<[12]>; + | ^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0747`.