diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3fb88c9599d..1532ca77f71 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -120,6 +120,17 @@ impl Path { Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None } } + pub fn is_ident(&self, name: Symbol) -> bool { + if let [segment] = self.segments.as_ref() + && segment.args.is_none() + && segment.ident.name == name + { + true + } else { + false + } + } + pub fn is_global(&self) -> bool { self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ef044fe9d63..2f026a77a0f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -609,6 +609,8 @@ impl<'a> Parser<'a> { // FIXME: translation requires list formatting (for `expect`) let mut err = self.dcx().struct_span_err(self.token.span, msg_exp); + self.label_expected_raw_ref(&mut err); + // Look for usages of '=>' where '>=' was probably intended if self.token == token::FatArrow && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le)) @@ -750,6 +752,25 @@ impl<'a> Parser<'a> { Err(err) } + /// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`. + /// + /// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this + /// label may need added to other diagnostics emission paths as needed. + pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) { + if self.prev_token.is_keyword(kw::Raw) + && self.expected_token_types.contains(TokenType::KwMut) + && self.expected_token_types.contains(TokenType::KwConst) + && self.token.can_begin_expr() + { + err.span_suggestions( + self.prev_token.span.shrink_to_hi(), + "`&raw` must be followed by `const` or `mut` to be a raw reference expression", + [" const".to_string(), " mut".to_string()], + Applicability::MaybeIncorrect, + ); + } + } + /// Checks if the current token or the previous token are misspelled keywords /// and adds a helpful suggestion. fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d52e36fcfac..10e16974f15 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -829,6 +829,18 @@ impl<'a> Parser<'a> { if let Some(lt) = lifetime { self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span)); } + + // Add expected tokens if we parsed `&raw` as an expression. + // This will make sure we see "expected `const`, `mut`", and + // guides recovery in case we write `&raw expr`. + if borrow_kind == ast::BorrowKind::Ref + && mutbl == ast::Mutability::Not + && matches!(&expr.kind, ExprKind::Path(None, p) if p.is_ident(kw::Raw)) + { + self.expected_token_types.insert(TokenType::KwMut); + self.expected_token_types.insert(TokenType::KwConst); + } + Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index e00fd40ecee..f3e6b2be96d 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -518,7 +518,11 @@ impl<'a> Parser<'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; + self.label_expected_raw_ref(&mut e); + + let do_not_suggest_help = self.token.is_keyword(kw::In) + || self.token == token::Colon + || self.prev_token.is_keyword(kw::Raw); // Check to see if the user has written something like // diff --git a/tests/ui/parser/recover/raw-no-const-mut.rs b/tests/ui/parser/recover/raw-no-const-mut.rs new file mode 100644 index 00000000000..d0ae69cc308 --- /dev/null +++ b/tests/ui/parser/recover/raw-no-const-mut.rs @@ -0,0 +1,31 @@ +fn a() { + let x = &raw 1; + //~^ ERROR expected one of +} + +fn b() { + [&raw const 1, &raw 2] + //~^ ERROR expected one of + //~| ERROR cannot find value `raw` in this scope + //~| ERROR cannot take address of a temporary +} + +fn c() { + if x == &raw z {} + //~^ ERROR expected `{` +} + +fn d() { + f(&raw 2); + //~^ ERROR expected one of + //~| ERROR cannot find value `raw` in this scope + //~| ERROR cannot find function `f` in this scope +} + +fn e() { + let x; + x = &raw 1; + //~^ ERROR expected one of +} + +fn main() {} diff --git a/tests/ui/parser/recover/raw-no-const-mut.stderr b/tests/ui/parser/recover/raw-no-const-mut.stderr new file mode 100644 index 00000000000..65032c80795 --- /dev/null +++ b/tests/ui/parser/recover/raw-no-const-mut.stderr @@ -0,0 +1,109 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `else`, `mut`, `{`, or an operator, found `1` + --> $DIR/raw-no-const-mut.rs:2:18 + | +LL | let x = &raw 1; + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | let x = &raw const 1; + | +++++ +LL | let x = &raw mut 1; + | +++ + +error: expected one of `!`, `,`, `.`, `::`, `?`, `]`, `const`, `mut`, `{`, or an operator, found `2` + --> $DIR/raw-no-const-mut.rs:7:25 + | +LL | [&raw const 1, &raw 2] + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | [&raw const 1, &raw const 2] + | +++++ +LL | [&raw const 1, &raw mut 2] + | +++ +help: missing `,` + | +LL | [&raw const 1, &raw, 2] + | + + +error: expected `{`, found `z` + --> $DIR/raw-no-const-mut.rs:14:18 + | +LL | if x == &raw z {} + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/raw-no-const-mut.rs:14:8 + | +LL | if x == &raw z {} + | ^^^^^^^^^ +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | if x == &raw const z {} + | +++++ +LL | if x == &raw mut z {} + | +++ + +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `2` + --> $DIR/raw-no-const-mut.rs:19:12 + | +LL | f(&raw 2); + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | f(&raw const 2); + | +++++ +LL | f(&raw mut 2); + | +++ +help: missing `,` + | +LL | f(&raw, 2); + | + + +error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `mut`, `{`, `}`, or an operator, found `1` + --> $DIR/raw-no-const-mut.rs:27:14 + | +LL | x = &raw 1; + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | x = &raw const 1; + | +++++ +LL | x = &raw mut 1; + | +++ + +error[E0425]: cannot find value `raw` in this scope + --> $DIR/raw-no-const-mut.rs:7:21 + | +LL | [&raw const 1, &raw 2] + | ^^^ not found in this scope + +error[E0425]: cannot find value `raw` in this scope + --> $DIR/raw-no-const-mut.rs:19:8 + | +LL | f(&raw 2); + | ^^^ not found in this scope + +error[E0745]: cannot take address of a temporary + --> $DIR/raw-no-const-mut.rs:7:17 + | +LL | [&raw const 1, &raw 2] + | ^ temporary value + +error[E0425]: cannot find function `f` in this scope + --> $DIR/raw-no-const-mut.rs:19:5 + | +LL | fn a() { + | ------ similarly named function `a` defined here +... +LL | f(&raw 2); + | ^ help: a function with a similar name exists: `a` + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0425, E0745. +For more information about an error, try `rustc --explain E0425`.