Repository: rust-analyzer/rust-analyzer (rust)

Issue:
`&raw` and `&raw mut` not recognised by parser
```rust
ptr::write(&raw mut (*inner).data, data);
```
> Syntax Error: expected COMMA

Pull request description:
Support raw_ref_op's raw reference operator
Fixes #4642.

This syntax (and its semantics) are implemented in rustc behind the `raw_ref_op` feature.

It is not entirely clear whether this is the syntax that will become stable, but [it seems like](https://github.com/rust-lang/rust/pull/72279) rust-analyzer must still support this unstable syntax to support future stable rust.

Also fixes a random inference failure involving a direct coercion from `&[T, _]` to `*const [T]`.

Patch:
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index e08d62dd68..905c0cf5da 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -28,7 +28,7 @@ use crate::{
     },
     item_scope::BuiltinShadowMode,
     path::{GenericArgs, Path},
-    type_ref::{Mutability, TypeRef},
+    type_ref::{Mutability, Rawness, TypeRef},
     AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
     StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
 };
@@ -378,8 +378,21 @@ impl ExprCollector<'_> {
             }
             ast::Expr::RefExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
-                let mutability = Mutability::from_mutable(e.mut_token().is_some());
-                self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr)
+                let raw_tok = e.raw_token().is_some();
+                let mutability = if raw_tok {
+                    if e.mut_token().is_some() {
+                        Mutability::Mut
+                    } else if e.const_token().is_some() {
+                        Mutability::Shared
+                    } else {
+                        unreachable!("parser only remaps to raw_token() if matching mutability token follows")
+                    }
+                } else {
+                    Mutability::from_mutable(e.mut_token().is_some())
+                };
+                let rawness = Rawness::from_raw(raw_tok);
+
+                self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr)
             }
             ast::Expr::PrefixExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index a0cdad529b..f25c6f9580 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -19,7 +19,7 @@ use ra_syntax::ast::RangeOp;
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt},
     path::{GenericArgs, Path},
-    type_ref::{Mutability, TypeRef},
+    type_ref::{Mutability, Rawness, TypeRef},
 };
 
 pub type ExprId = Idx<Expr>;
@@ -110,6 +110,7 @@ pub enum Expr {
     },
     Ref {
         expr: ExprId,
+        rawness: Rawness,
         mutability: Mutability,
     },
     Box {
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
index 5bdad9efde..86a77b7046 100644
--- a/crates/ra_hir_def/src/type_ref.rs
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -35,6 +35,22 @@ impl Mutability {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum Rawness {
+    RawPtr,
+    Ref,
+}
+
+impl Rawness {
+    pub fn from_raw(is_raw: bool) -> Rawness {
+        if is_raw {
+            Rawness::RawPtr
+        } else {
+            Rawness::Ref
+        }
+    }
+}
+
 /// Compare ty::Ty
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeRef {
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index b28724f0e9..54bab3476c 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -17,8 +17,8 @@ use crate::{
     autoderef, method_resolution, op,
     traits::InEnvironment,
     utils::{generics, variant_data, Generics},
-    ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef,
-    Ty, TypeCtor, Uncertain,
+    ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
+    TraitRef, Ty, TypeCtor, Uncertain,
 };
 
 use super::{
@@ -350,19 +350,28 @@ impl<'a> InferenceContext<'a> {
                 // FIXME check the cast...
                 cast_ty
             }
-            Expr::Ref { expr, mutability } => {
-                let expectation =
-                    if let Some((exp_inner, exp_mutability)) = &expected.ty.as_reference() {
-                        if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared {
-                            // FIXME: throw type error - expected mut reference but found shared ref,
-                            // which cannot be coerced
-                        }
-                        Expectation::rvalue_hint(Ty::clone(exp_inner))
-                    } else {
-                        Expectation::none()
-                    };
+            Expr::Ref { expr, rawness, mutability } => {
+                let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) =
+                    &expected.ty.as_reference_or_ptr()
+                {
+                    if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared {
+                        // FIXME: throw type error - expected mut reference but found shared ref,
+                        // which cannot be coerced
+                    }
+                    if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
+                        // FIXME: throw type error - expected reference but found ptr,
+                        // which cannot be coerced
+                    }
+                    Expectation::rvalue_hint(Ty::clone(exp_inner))
+                } else {
+                    Expectation::none()
+                };
                 let inner_ty = self.infer_expr_inner(*expr, &expectation);
-                Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
+                let ty = match rawness {
+                    Rawness::RawPtr => TypeCtor::RawPtr(*mutability),
+                    Rawness::Ref => TypeCtor::Ref(*mutability),
+                };
+                Ty::apply_one(ty, inner_ty)
             }
             Expr::Box { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 93cb45a648..9fa8d3bdc3 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -49,8 +49,10 @@ use std::sync::Arc;
 use std::{iter, mem};
 
 use hir_def::{
-    expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId,
-    HasModule, Lookup, TraitId, TypeAliasId, TypeParamId,
+    expr::ExprId,
+    type_ref::{Mutability, Rawness},
+    AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId,
+    TypeParamId,
 };
 use ra_db::{impl_intern_key, salsa, CrateId};
 
@@ -709,6 +711,18 @@ impl Ty {
         }
     }
 
+    pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
+        match self {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => {
+                Some((parameters.as_single(), Rawness::Ref, *mutability))
+            }
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(mutability), parameters }) => {
+                Some((parameters.as_single(), Rawness::RawPtr, *mutability))
+            }
+            _ => None,
+        }
+    }
+
     pub fn strip_references(&self) -> &Ty {
         let mut t: &Ty = self;
 
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index 34f0397686..d6e8df32a1 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -325,13 +325,27 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
     let kind = match p.current() {
         // test ref_expr
         // fn foo() {
+        //     // reference operator
         //     let _ = &1;
         //     let _ = &mut &f();
+        //     let _ = &raw;
+        //     let _ = &raw.0;
+        //     // raw reference operator
+        //     let _ = &raw mut foo;
+        //     let _ = &raw const foo;
         // }
         T![&] => {
             m = p.start();
             p.bump(T![&]);
-            p.eat(T![mut]);
+            if p.at(IDENT)
+                && p.at_contextual_kw("raw")
+                && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const]))
+            {
+                p.bump_remap(T![raw]);
+                p.bump_any();
+            } else {
+                p.eat(T![mut]);
+            }
             REF_EXPR
         }
         // test unary_expr
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cf6067e57c..255402fbce 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1235,6 +1235,8 @@ impl CastExpr {
 /// ```
 /// ❰ &foo ❱;
 /// ❰ &mut bar ❱;
+/// ❰ &raw const bar ❱;
+/// ❰ &raw mut bar ❱;
 /// ```
 ///
 /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators)
@@ -1247,6 +1249,7 @@ impl RefExpr {
     pub fn amp_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) }
     pub fn raw_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![raw]) }
     pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
+    pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
     pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
 }
 /// Prefix operator call. This is either `!` or `*` or `-`.
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 394a7bc889..d4621930ed 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -1153,10 +1153,12 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
         /// ```
         /// ❰ &foo ❱;
         /// ❰ &mut bar ❱;
+        /// ❰ &raw const bar ❱;
+        /// ❰ &raw mut bar ❱;
         /// ```
         ///
         /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators)
-        struct RefExpr: AttrsOwner { T![&], T![raw], T![mut], Expr }
+        struct RefExpr: AttrsOwner { T![&], T![raw], T![mut], T![const], Expr }
 
         /// Prefix operator call. This is either `!` or `*` or `-`.
         ///


Test patch:
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 2cc4f4bf96..6f777ed8c9 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -116,15 +116,20 @@ fn infer_let_stmt_coerce() {
     assert_snapshot!(
         infer(r#"
 fn test() {
-    let x: &[i32] = &[1];
+    let x: &[isize] = &[1];
+    let x: *const [isize] = &[1];
 }
 "#),
         @r###"
-    11..40 '{     ...[1]; }': ()
-    21..22 'x': &[i32]
-    33..37 '&[1]': &[i32; _]
-    34..37 '[1]': [i32; _]
-    35..36 '1': i32
+    11..76 '{     ...[1]; }': ()
+    21..22 'x': &[isize]
+    35..39 '&[1]': &[isize; _]
+    36..39 '[1]': [isize; _]
+    37..38 '1': isize
+    49..50 'x': *const [isize]
+    69..73 '&[1]': &[isize; _]
+    70..73 '[1]': [isize; _]
+    71..72 '1': isize
     "###);
 }
 
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index fd2208af28..f1db341604 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -384,6 +384,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
     );
 }
 
+#[test]
+fn infer_raw_ref() {
+    assert_snapshot!(
+        infer(r#"
+fn test(a: i32) {
+    &raw mut a;
+    &raw const a;
+}
+"#),
+        @r###"
+    9..10 'a': i32
+    17..54 '{     ...t a; }': ()
+    23..33 '&raw mut a': *mut i32
+    32..33 'a': i32
+    39..51 '&raw const a': *const i32
+    50..51 'a': i32
+    "###
+    );
+}
+
 #[test]
 fn infer_literals() {
     assert_snapshot!(
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast
index 7fe96e17db..58bdf7e34c 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..52
-  FN_DEF@0..51
+SOURCE_FILE@0..200
+  FN_DEF@0..199
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
     NAME@3..6
@@ -8,47 +8,131 @@ SOURCE_FILE@0..52
       L_PAREN@6..7 "("
       R_PAREN@7..8 ")"
     WHITESPACE@8..9 " "
-    BLOCK_EXPR@9..51
+    BLOCK_EXPR@9..199
       L_CURLY@9..10 "{"
       WHITESPACE@10..15 "\n    "
-      LET_STMT@15..26
-        LET_KW@15..18 "let"
-        WHITESPACE@18..19 " "
-        PLACEHOLDER_PAT@19..20
-          UNDERSCORE@19..20 "_"
-        WHITESPACE@20..21 " "
-        EQ@21..22 "="
-        WHITESPACE@22..23 " "
-        REF_EXPR@23..25
-          AMP@23..24 "&"
-          LITERAL@24..25
-            INT_NUMBER@24..25 "1"
-        SEMICOLON@25..26 ";"
-      WHITESPACE@26..31 "\n    "
-      LET_STMT@31..49
-        LET_KW@31..34 "let"
-        WHITESPACE@34..35 " "
-        PLACEHOLDER_PAT@35..36
-          UNDERSCORE@35..36 "_"
-        WHITESPACE@36..37 " "
-        EQ@37..38 "="
-        WHITESPACE@38..39 " "
-        REF_EXPR@39..48
-          AMP@39..40 "&"
-          MUT_KW@40..43 "mut"
-          WHITESPACE@43..44 " "
-          REF_EXPR@44..48
-            AMP@44..45 "&"
-            CALL_EXPR@45..48
-              PATH_EXPR@45..46
-                PATH@45..46
-                  PATH_SEGMENT@45..46
-                    NAME_REF@45..46
-                      IDENT@45..46 "f"
-              ARG_LIST@46..48
-                L_PAREN@46..47 "("
-                R_PAREN@47..48 ")"
-        SEMICOLON@48..49 ";"
-      WHITESPACE@49..50 "\n"
-      R_CURLY@50..51 "}"
-  WHITESPACE@51..52 "\n"
+      COMMENT@15..36 "// reference operator"
+      WHITESPACE@36..41 "\n    "
+      LET_STMT@41..52
+        LET_KW@41..44 "let"
+        WHITESPACE@44..45 " "
+        PLACEHOLDER_PAT@45..46
+          UNDERSCORE@45..46 "_"
+        WHITESPACE@46..47 " "
+        EQ@47..48 "="
+        WHITESPACE@48..49 " "
+        REF_EXPR@49..51
+          AMP@49..50 "&"
+          LITERAL@50..51
+            INT_NUMBER@50..51 "1"
+        SEMICOLON@51..52 ";"
+      WHITESPACE@52..57 "\n    "
+      LET_STMT@57..75
+        LET_KW@57..60 "let"
+        WHITESPACE@60..61 " "
+        PLACEHOLDER_PAT@61..62
+          UNDERSCORE@61..62 "_"
+        WHITESPACE@62..63 " "
+        EQ@63..64 "="
+        WHITESPACE@64..65 " "
+        REF_EXPR@65..74
+          AMP@65..66 "&"
+          MUT_KW@66..69 "mut"
+          WHITESPACE@69..70 " "
+          REF_EXPR@70..74
+            AMP@70..71 "&"
+            CALL_EXPR@71..74
+              PATH_EXPR@71..72
+                PATH@71..72
+                  PATH_SEGMENT@71..72
+                    NAME_REF@71..72
+                      IDENT@71..72 "f"
+              ARG_LIST@72..74
+                L_PAREN@72..73 "("
+                R_PAREN@73..74 ")"
+        SEMICOLON@74..75 ";"
+      WHITESPACE@75..80 "\n    "
+      LET_STMT@80..93
+        LET_KW@80..83 "let"
+        WHITESPACE@83..84 " "
+        PLACEHOLDER_PAT@84..85
+          UNDERSCORE@84..85 "_"
+        WHITESPACE@85..86 " "
+        EQ@86..87 "="
+        WHITESPACE@87..88 " "
+        REF_EXPR@88..92
+          AMP@88..89 "&"
+          PATH_EXPR@89..92
+            PATH@89..92
+              PATH_SEGMENT@89..92
+                NAME_REF@89..92
+                  IDENT@89..92 "raw"
+        SEMICOLON@92..93 ";"
+      WHITESPACE@93..98 "\n    "
+      LET_STMT@98..113
+        LET_KW@98..101 "let"
+        WHITESPACE@101..102 " "
+        PLACEHOLDER_PAT@102..103
+          UNDERSCORE@102..103 "_"
+        WHITESPACE@103..104 " "
+        EQ@104..105 "="
+        WHITESPACE@105..106 " "
+        REF_EXPR@106..112
+          AMP@106..107 "&"
+          FIELD_EXPR@107..112
+            PATH_EXPR@107..110
+              PATH@107..110
+                PATH_SEGMENT@107..110
+                  NAME_REF@107..110
+                    IDENT@107..110 "raw"
+            DOT@110..111 "."
+            NAME_REF@111..112
+              INT_NUMBER@111..112 "0"
+        SEMICOLON@112..113 ";"
+      WHITESPACE@113..118 "\n    "
+      COMMENT@118..143 "// raw reference oper ..."
+      WHITESPACE@143..148 "\n    "
+      LET_STMT@148..169
+        LET_KW@148..151 "let"
+        WHITESPACE@151..152 " "
+        PLACEHOLDER_PAT@152..153
+          UNDERSCORE@152..153 "_"
+        WHITESPACE@153..154 " "
+        EQ@154..155 "="
+        WHITESPACE@155..156 " "
+        REF_EXPR@156..168
+          AMP@156..157 "&"
+          RAW_KW@157..160 "raw"
+          WHITESPACE@160..161 " "
+          MUT_KW@161..164 "mut"
+          WHITESPACE@164..165 " "
+          PATH_EXPR@165..168
+            PATH@165..168
+              PATH_SEGMENT@165..168
+                NAME_REF@165..168
+                  IDENT@165..168 "foo"
+        SEMICOLON@168..169 ";"
+      WHITESPACE@169..174 "\n    "
+      LET_STMT@174..197
+        LET_KW@174..177 "let"
+        WHITESPACE@177..178 " "
+        PLACEHOLDER_PAT@178..179
+          UNDERSCORE@178..179 "_"
+        WHITESPACE@179..180 " "
+        EQ@180..181 "="
+        WHITESPACE@181..182 " "
+        REF_EXPR@182..196
+          AMP@182..183 "&"
+          RAW_KW@183..186 "raw"
+          WHITESPACE@186..187 " "
+          CONST_KW@187..192 "const"
+          WHITESPACE@192..193 " "
+          PATH_EXPR@193..196
+            PATH@193..196
+              PATH_SEGMENT@193..196
+                NAME_REF@193..196
+                  IDENT@193..196 "foo"
+        SEMICOLON@196..197 ";"
+      WHITESPACE@197..198 "\n"
+      R_CURLY@198..199 "}"
+  WHITESPACE@199..200 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs
index 2dac6be95e..c5262f4469 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs
@@ -1,4 +1,10 @@
 fn foo() {
+    // reference operator
     let _ = &1;
     let _ = &mut &f();
+    let _ = &raw;
+    let _ = &raw.0;
+    // raw reference operator
+    let _ = &raw mut foo;
+    let _ = &raw const foo;
 }