about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs11
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs21
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs12
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs6
-rw-r--r--tests/ui/parser/recover/raw-no-const-mut.rs31
-rw-r--r--tests/ui/parser/recover/raw-no-const-mut.stderr109
6 files changed, 189 insertions, 1 deletions
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 bb63e91aecf..a4978b5a0fe 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 14bc98f66da..df44b3cc23c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -827,6 +827,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 a50a9e224cf..551b9e2f137 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`.