Repository: dtolnay/syn
Pull request #1743 (2024-10-07 21:33:36)

Add Expr::RawAddr
`&raw const place` / `&raw mut place` syntax is stabilizing in Rust 1.82.


Patch:
diff --git a/src/classify.rs b/src/classify.rs
index 42732e6d..c125fc21 100644
--- a/src/classify.rs
+++ b/src/classify.rs
@@ -54,6 +54,7 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool {
         | Expr::Paren(_)
         | Expr::Path(_)
         | Expr::Range(_)
+        | Expr::RawAddr(_)
         | Expr::Reference(_)
         | Expr::Repeat(_)
         | Expr::Return(_)
@@ -105,6 +106,7 @@ pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool {
                 (None, None) => stack.pop(),
             }
         }
+        Expr::RawAddr(e) => Some(&e.expr),
         Expr::Reference(e) => Some(&e.expr),
         Expr::Return(e) => {
             if e.expr.is_none() && stack.is_empty() {
@@ -246,6 +248,7 @@ pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool {
             | Expr::Match(_)
             | Expr::Paren(_)
             | Expr::Path(_)
+            | Expr::RawAddr(_)
             | Expr::Reference(_)
             | Expr::Repeat(_)
             | Expr::Return(_)
@@ -291,6 +294,7 @@ pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool {
                 Some(end) => expr = end,
                 None => return false,
             },
+            Expr::RawAddr(e) => expr = &e.expr,
             Expr::Reference(e) => expr = &e.expr,
             Expr::Return(e) => match &e.expr {
                 Some(e) => expr = e,
diff --git a/src/expr.rs b/src/expr.rs
index 409131b4..8b9f030a 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -202,6 +202,9 @@ ast_enum_of_structs! {
         /// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
         Range(ExprRange),
 
+        /// Address-of operation: `&raw const place` or `&raw mut place`.
+        RawAddr(ExprRawAddr),
+
         /// A referencing operation: `&a` or `&mut a`.
         Reference(ExprReference),
 
@@ -574,6 +577,18 @@ ast_struct! {
     }
 }
 
+ast_struct! {
+    /// Address-of operation: `&raw const place` or `&raw mut place`.
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    pub struct ExprRawAddr #full {
+        pub attrs: Vec<Attribute>,
+        pub and_token: Token![&],
+        pub raw: Token![raw],
+        pub mutability: PointerMutability,
+        pub expr: Box<Expr>,
+    }
+}
+
 ast_struct! {
     /// A referencing operation: `&a` or `&mut a`.
     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
@@ -904,6 +919,7 @@ impl Expr {
             | Expr::Paren(ExprParen { attrs, .. })
             | Expr::Path(ExprPath { attrs, .. })
             | Expr::Range(ExprRange { attrs, .. })
+            | Expr::RawAddr(ExprRawAddr { attrs, .. })
             | Expr::Reference(ExprReference { attrs, .. })
             | Expr::Repeat(ExprRepeat { attrs, .. })
             | Expr::Return(ExprReturn { attrs, .. })
@@ -1099,6 +1115,17 @@ ast_enum! {
     }
 }
 
+#[cfg(feature = "full")]
+ast_enum! {
+    /// Mutability of a raw pointer (`*const T`, `*mut T`), in which non-mutable
+    /// isn't the implicit default.
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    pub enum PointerMutability {
+        Const(Token![const]),
+        Mut(Token![mut]),
+    }
+}
+
 #[cfg(feature = "parsing")]
 pub(crate) mod parsing {
     #[cfg(feature = "full")]
@@ -1111,8 +1138,8 @@ pub(crate) mod parsing {
     use crate::expr::{
         Arm, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBlock, ExprBreak, ExprClosure,
         ExprConst, ExprContinue, ExprForLoop, ExprIf, ExprInfer, ExprLet, ExprLoop, ExprMatch,
-        ExprRange, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprUnsafe, ExprWhile, ExprYield,
-        Label, RangeLimits,
+        ExprRange, ExprRawAddr, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprUnsafe,
+        ExprWhile, ExprYield, Label, PointerMutability, RangeLimits,
     };
     use crate::expr::{
         Expr, ExprBinary, ExprCall, ExprCast, ExprField, ExprGroup, ExprIndex, ExprLit, ExprMacro,
@@ -1151,7 +1178,6 @@ pub(crate) mod parsing {
 
     mod kw {
         crate::custom_keyword!(builtin);
-        crate::custom_keyword!(raw);
     }
 
     // When we're parsing expressions which occur before blocks, like in an if
@@ -1468,7 +1494,7 @@ pub(crate) mod parsing {
 
         if input.peek(Token![&]) {
             let and_token: Token![&] = input.parse()?;
-            let raw: Option<kw::raw> = if input.peek(kw::raw)
+            let raw: Option<Token![raw]> = if input.peek(Token![raw])
                 && (input.peek2(Token![mut]) || input.peek2(Token![const]))
             {
                 Some(input.parse()?)
@@ -1476,12 +1502,23 @@ pub(crate) mod parsing {
                 None
             };
             let mutability: Option<Token![mut]> = input.parse()?;
-            if raw.is_some() && mutability.is_none() {
-                input.parse::<Token![const]>()?;
-            }
+            let const_token: Option<Token![const]> = if raw.is_some() && mutability.is_none() {
+                Some(input.parse()?)
+            } else {
+                None
+            };
             let expr = Box::new(unary_expr(input, allow_struct)?);
-            if raw.is_some() {
-                Ok(Expr::Verbatim(verbatim::between(&begin, input)))
+            if let Some(raw) = raw {
+                Ok(Expr::RawAddr(ExprRawAddr {
+                    attrs,
+                    and_token,
+                    raw,
+                    mutability: match mutability {
+                        Some(mut_token) => PointerMutability::Mut(mut_token),
+                        None => PointerMutability::Const(const_token.unwrap()),
+                    },
+                    expr,
+                }))
             } else {
                 Ok(Expr::Reference(ExprReference {
                     attrs,
@@ -2359,6 +2396,21 @@ pub(crate) mod parsing {
         }
     }
 
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    impl Parse for ExprRawAddr {
+        fn parse(input: ParseStream) -> Result<Self> {
+            let allow_struct = AllowStruct(true);
+            Ok(ExprRawAddr {
+                attrs: Vec::new(),
+                and_token: input.parse()?,
+                raw: input.parse()?,
+                mutability: input.parse()?,
+                expr: Box::new(unary_expr(input, allow_struct)?),
+            })
+        }
+    }
+
     #[cfg(feature = "full")]
     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
     impl Parse for ExprReference {
@@ -2970,6 +3022,21 @@ pub(crate) mod parsing {
         }
     }
 
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    impl Parse for PointerMutability {
+        fn parse(input: ParseStream) -> Result<Self> {
+            let lookahead = input.lookahead1();
+            if lookahead.peek(Token![const]) {
+                Ok(PointerMutability::Const(input.parse()?))
+            } else if lookahead.peek(Token![mut]) {
+                Ok(PointerMutability::Mut(input.parse()?))
+            } else {
+                Err(lookahead.error())
+            }
+        }
+    }
+
     fn check_cast(input: ParseStream) -> Result<()> {
         let kind = if input.peek(Token![.]) && !input.peek(Token![..]) {
             if input.peek2(Token![await]) {
@@ -3004,8 +3071,8 @@ pub(crate) mod printing {
     use crate::expr::{
         Arm, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBlock, ExprBreak, ExprClosure,
         ExprConst, ExprContinue, ExprForLoop, ExprIf, ExprInfer, ExprLet, ExprLoop, ExprMatch,
-        ExprRange, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprUnsafe, ExprWhile, ExprYield,
-        Label, RangeLimits,
+        ExprRange, ExprRawAddr, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprUnsafe,
+        ExprWhile, ExprYield, Label, PointerMutability, RangeLimits,
     };
     use crate::expr::{
         Expr, ExprBinary, ExprCall, ExprCast, ExprField, ExprGroup, ExprIndex, ExprLit, ExprMacro,
@@ -3131,6 +3198,8 @@ pub(crate) mod printing {
             Expr::Path(e) => e.to_tokens(tokens),
             #[cfg(feature = "full")]
             Expr::Range(e) => print_expr_range(e, tokens, fixup),
+            #[cfg(feature = "full")]
+            Expr::RawAddr(e) => print_expr_raw_addr(e, tokens, fixup),
             Expr::Reference(e) => print_expr_reference(e, tokens, fixup),
             #[cfg(feature = "full")]
             Expr::Repeat(e) => e.to_tokens(tokens),
@@ -3689,6 +3758,28 @@ pub(crate) mod printing {
         }
     }
 
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
+    impl ToTokens for ExprRawAddr {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            print_expr_raw_addr(self, tokens, FixupContext::NONE);
+        }
+    }
+
+    #[cfg(feature = "full")]
+    fn print_expr_raw_addr(e: &ExprRawAddr, tokens: &mut TokenStream, fixup: FixupContext) {
+        outer_attrs_to_tokens(&e.attrs, tokens);
+        e.and_token.to_tokens(tokens);
+        e.raw.to_tokens(tokens);
+        e.mutability.to_tokens(tokens);
+        print_subexpression(
+            &e.expr,
+            fixup.trailing_precedence(&e.expr) < Precedence::Prefix,
+            tokens,
+            fixup.subsequent_subexpression(),
+        );
+    }
+
     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
     impl ToTokens for ExprReference {
         fn to_tokens(&self, tokens: &mut TokenStream) {
@@ -3929,4 +4020,15 @@ pub(crate) mod printing {
             }
         }
     }
+
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
+    impl ToTokens for PointerMutability {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            match self {
+                PointerMutability::Const(const_token) => const_token.to_tokens(tokens),
+                PointerMutability::Mut(mut_token) => mut_token.to_tokens(tokens),
+            }
+        }
+    }
 }
diff --git a/src/gen/clone.rs b/src/gen/clone.rs
index 97575613..037da1bc 100644
--- a/src/gen/clone.rs
+++ b/src/gen/clone.rs
@@ -270,6 +270,8 @@ impl Clone for crate::Expr {
             crate::Expr::Path(v0) => crate::Expr::Path(v0.clone()),
             #[cfg(feature = "full")]
             crate::Expr::Range(v0) => crate::Expr::Range(v0.clone()),
+            #[cfg(feature = "full")]
+            crate::Expr::RawAddr(v0) => crate::Expr::RawAddr(v0.clone()),
             crate::Expr::Reference(v0) => crate::Expr::Reference(v0.clone()),
             #[cfg(feature = "full")]
             crate::Expr::Repeat(v0) => crate::Expr::Repeat(v0.clone()),
@@ -621,6 +623,19 @@ impl Clone for crate::ExprRange {
         }
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]
+impl Clone for crate::ExprRawAddr {
+    fn clone(&self) -> Self {
+        crate::ExprRawAddr {
+            attrs: self.attrs.clone(),
+            and_token: self.and_token.clone(),
+            raw: self.raw.clone(),
+            mutability: self.mutability.clone(),
+            expr: self.expr.clone(),
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]
 impl Clone for crate::ExprReference {
@@ -1647,6 +1662,20 @@ impl Clone for crate::PathSegment {
         }
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]
+impl Clone for crate::PointerMutability {
+    fn clone(&self) -> Self {
+        match self {
+            crate::PointerMutability::Const(v0) => {
+                crate::PointerMutability::Const(v0.clone())
+            }
+            crate::PointerMutability::Mut(v0) => {
+                crate::PointerMutability::Mut(v0.clone())
+            }
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]
 impl Clone for crate::PredicateLifetime {
diff --git a/src/gen/debug.rs b/src/gen/debug.rs
index fe1179c9..54a12dba 100644
--- a/src/gen/debug.rs
+++ b/src/gen/debug.rs
@@ -441,6 +441,8 @@ impl Debug for crate::Expr {
             crate::Expr::Path(v0) => v0.debug(formatter, "Path"),
             #[cfg(feature = "full")]
             crate::Expr::Range(v0) => v0.debug(formatter, "Range"),
+            #[cfg(feature = "full")]
+            crate::Expr::RawAddr(v0) => v0.debug(formatter, "RawAddr"),
             crate::Expr::Reference(v0) => v0.debug(formatter, "Reference"),
             #[cfg(feature = "full")]
             crate::Expr::Repeat(v0) => v0.debug(formatter, "Repeat"),
@@ -958,6 +960,25 @@ impl crate::ExprRange {
         formatter.finish()
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl Debug for crate::ExprRawAddr {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        self.debug(formatter, "ExprRawAddr")
+    }
+}
+#[cfg(feature = "full")]
+impl crate::ExprRawAddr {
+    fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result {
+        let mut formatter = formatter.debug_struct(name);
+        formatter.field("attrs", &self.attrs);
+        formatter.field("and_token", &self.and_token);
+        formatter.field("raw", &self.raw);
+        formatter.field("mutability", &self.mutability);
+        formatter.field("expr", &self.expr);
+        formatter.finish()
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
 impl Debug for crate::ExprReference {
@@ -2385,6 +2406,25 @@ impl Debug for crate::PathSegment {
         formatter.finish()
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl Debug for crate::PointerMutability {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("PointerMutability::")?;
+        match self {
+            crate::PointerMutability::Const(v0) => {
+                let mut formatter = formatter.debug_tuple("Const");
+                formatter.field(v0);
+                formatter.finish()
+            }
+            crate::PointerMutability::Mut(v0) => {
+                let mut formatter = formatter.debug_tuple("Mut");
+                formatter.field(v0);
+                formatter.finish()
+            }
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
 impl Debug for crate::PredicateLifetime {
diff --git a/src/gen/eq.rs b/src/gen/eq.rs
index 936293c3..cffa5d29 100644
--- a/src/gen/eq.rs
+++ b/src/gen/eq.rs
@@ -297,6 +297,10 @@ impl PartialEq for crate::Expr {
             (crate::Expr::Path(self0), crate::Expr::Path(other0)) => self0 == other0,
             #[cfg(feature = "full")]
             (crate::Expr::Range(self0), crate::Expr::Range(other0)) => self0 == other0,
+            #[cfg(feature = "full")]
+            (crate::Expr::RawAddr(self0), crate::Expr::RawAddr(other0)) => {
+                self0 == other0
+            }
             (crate::Expr::Reference(self0), crate::Expr::Reference(other0)) => {
                 self0 == other0
             }
@@ -610,6 +614,17 @@ impl PartialEq for crate::ExprRange {
             && self.limits == other.limits && self.end == other.end
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl Eq for crate::ExprRawAddr {}
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl PartialEq for crate::ExprRawAddr {
+    fn eq(&self, other: &Self) -> bool {
+        self.attrs == other.attrs && self.mutability == other.mutability
+            && self.expr == other.expr
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
 impl Eq for crate::ExprReference {}
@@ -1622,6 +1637,22 @@ impl PartialEq for crate::PathSegment {
         self.ident == other.ident && self.arguments == other.arguments
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl Eq for crate::PointerMutability {}
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl PartialEq for crate::PointerMutability {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (crate::PointerMutability::Const(_), crate::PointerMutability::Const(_)) => {
+                true
+            }
+            (crate::PointerMutability::Mut(_), crate::PointerMutability::Mut(_)) => true,
+            _ => false,
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
 impl Eq for crate::PredicateLifetime {}
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index c3de0966..ab794056 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -269,6 +269,11 @@ pub trait Fold {
     fn fold_expr_range(&mut self, i: crate::ExprRange) -> crate::ExprRange {
         fold_expr_range(self, i)
     }
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    fn fold_expr_raw_addr(&mut self, i: crate::ExprRawAddr) -> crate::ExprRawAddr {
+        fold_expr_raw_addr(self, i)
+    }
     #[cfg(any(feature = "derive", feature = "full"))]
     #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
     fn fold_expr_reference(&mut self, i: crate::ExprReference) -> crate::ExprReference {
@@ -723,6 +728,14 @@ pub trait Fold {
     fn fold_path_segment(&mut self, i: crate::PathSegment) -> crate::PathSegment {
         fold_path_segment(self, i)
     }
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    fn fold_pointer_mutability(
+        &mut self,
+        i: crate::PointerMutability,
+    ) -> crate::PointerMutability {
+        fold_pointer_mutability(self, i)
+    }
     #[cfg(any(feature = "derive", feature = "full"))]
     #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
     fn fold_predicate_lifetime(
@@ -1339,6 +1352,9 @@ where
         crate::Expr::Range(_binding_0) => {
             crate::Expr::Range(full!(f.fold_expr_range(_binding_0)))
         }
+        crate::Expr::RawAddr(_binding_0) => {
+            crate::Expr::RawAddr(full!(f.fold_expr_raw_addr(_binding_0)))
+        }
         crate::Expr::Reference(_binding_0) => {
             crate::Expr::Reference(f.fold_expr_reference(_binding_0))
         }
@@ -1734,6 +1750,20 @@ where
         end: (node.end).map(|it| Box::new(f.fold_expr(*it))),
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+pub fn fold_expr_raw_addr<F>(f: &mut F, node: crate::ExprRawAddr) -> crate::ExprRawAddr
+where
+    F: Fold + ?Sized,
+{
+    crate::ExprRawAddr {
+        attrs: fold_vec(node.attrs, f, F::fold_attribute),
+        and_token: node.and_token,
+        raw: node.raw,
+        mutability: f.fold_pointer_mutability(node.mutability),
+        expr: Box::new(f.fold_expr(*node.expr)),
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
 pub fn fold_expr_reference<F>(
@@ -3060,6 +3090,24 @@ where
         arguments: f.fold_path_arguments(node.arguments),
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+pub fn fold_pointer_mutability<F>(
+    f: &mut F,
+    node: crate::PointerMutability,
+) -> crate::PointerMutability
+where
+    F: Fold + ?Sized,
+{
+    match node {
+        crate::PointerMutability::Const(_binding_0) => {
+            crate::PointerMutability::Const(_binding_0)
+        }
+        crate::PointerMutability::Mut(_binding_0) => {
+            crate::PointerMutability::Mut(_binding_0)
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
 pub fn fold_predicate_lifetime<F>(
diff --git a/src/gen/hash.rs b/src/gen/hash.rs
index 4dedba4a..51345ead 100644
--- a/src/gen/hash.rs
+++ b/src/gen/hash.rs
@@ -456,59 +456,64 @@ impl Hash for crate::Expr {
                 state.write_u8(26u8);
                 v0.hash(state);
             }
-            crate::Expr::Reference(v0) => {
+            #[cfg(feature = "full")]
+            crate::Expr::RawAddr(v0) => {
                 state.write_u8(27u8);
                 v0.hash(state);
             }
+            crate::Expr::Reference(v0) => {
+                state.write_u8(28u8);
+                v0.hash(state);
+            }
             #[cfg(feature = "full")]
             crate::Expr::Repeat(v0) => {
-                state.write_u8(28u8);
+                state.write_u8(29u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             crate::Expr::Return(v0) => {
-                state.write_u8(29u8);
+                state.write_u8(30u8);
                 v0.hash(state);
             }
             crate::Expr::Struct(v0) => {
-                state.write_u8(30u8);
+                state.write_u8(31u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             crate::Expr::Try(v0) => {
-                state.write_u8(31u8);
+                state.write_u8(32u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             crate::Expr::TryBlock(v0) => {
-                state.write_u8(32u8);
+                state.write_u8(33u8);
                 v0.hash(state);
             }
             crate::Expr::Tuple(v0) => {
-                state.write_u8(33u8);
+                state.write_u8(34u8);
                 v0.hash(state);
             }
             crate::Expr::Unary(v0) => {
-                state.write_u8(34u8);
+                state.write_u8(35u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             crate::Expr::Unsafe(v0) => {
-                state.write_u8(35u8);
+                state.write_u8(36u8);
                 v0.hash(state);
             }
             crate::Expr::Verbatim(v0) => {
-                state.write_u8(36u8);
+                state.write_u8(37u8);
                 TokenStreamHelper(v0).hash(state);
             }
             #[cfg(feature = "full")]
             crate::Expr::While(v0) => {
-                state.write_u8(37u8);
+                state.write_u8(38u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             crate::Expr::Yield(v0) => {
-                state.write_u8(38u8);
+                state.write_u8(39u8);
                 v0.hash(state);
             }
             #[cfg(not(feature = "full"))]
@@ -843,6 +848,18 @@ impl Hash for crate::ExprRange {
         self.end.hash(state);
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl Hash for crate::ExprRawAddr {
+    fn hash<H>(&self, state: &mut H)
+    where
+        H: Hasher,
+    {
+        self.attrs.hash(state);
+        self.mutability.hash(state);
+        self.expr.hash(state);
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
 impl Hash for crate::ExprReference {
@@ -2086,6 +2103,23 @@ impl Hash for crate::PathSegment {
         self.arguments.hash(state);
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
+impl Hash for crate::PointerMutability {
+    fn hash<H>(&self, state: &mut H)
+    where
+        H: Hasher,
+    {
+        match self {
+            crate::PointerMutability::Const(_) => {
+                state.write_u8(0u8);
+            }
+            crate::PointerMutability::Mut(_) => {
+                state.write_u8(1u8);
+            }
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))]
 impl Hash for crate::PredicateLifetime {
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index 6c96a3f3..419fb593 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -264,6 +264,11 @@ pub trait Visit<'ast> {
     fn visit_expr_range(&mut self, i: &'ast crate::ExprRange) {
         visit_expr_range(self, i);
     }
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    fn visit_expr_raw_addr(&mut self, i: &'ast crate::ExprRawAddr) {
+        visit_expr_raw_addr(self, i);
+    }
     #[cfg(any(feature = "derive", feature = "full"))]
     #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
     fn visit_expr_reference(&mut self, i: &'ast crate::ExprReference) {
@@ -685,6 +690,11 @@ pub trait Visit<'ast> {
     fn visit_path_segment(&mut self, i: &'ast crate::PathSegment) {
         visit_path_segment(self, i);
     }
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    fn visit_pointer_mutability(&mut self, i: &'ast crate::PointerMutability) {
+        visit_pointer_mutability(self, i);
+    }
     #[cfg(any(feature = "derive", feature = "full"))]
     #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
     fn visit_predicate_lifetime(&mut self, i: &'ast crate::PredicateLifetime) {
@@ -1355,6 +1365,9 @@ where
         crate::Expr::Range(_binding_0) => {
             full!(v.visit_expr_range(_binding_0));
         }
+        crate::Expr::RawAddr(_binding_0) => {
+            full!(v.visit_expr_raw_addr(_binding_0));
+        }
         crate::Expr::Reference(_binding_0) => {
             v.visit_expr_reference(_binding_0);
         }
@@ -1786,6 +1799,20 @@ where
         v.visit_expr(&**it);
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+pub fn visit_expr_raw_addr<'ast, V>(v: &mut V, node: &'ast crate::ExprRawAddr)
+where
+    V: Visit<'ast> + ?Sized,
+{
+    for it in &node.attrs {
+        v.visit_attribute(it);
+    }
+    skip!(node.and_token);
+    skip!(node.raw);
+    v.visit_pointer_mutability(&node.mutability);
+    v.visit_expr(&*node.expr);
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
 pub fn visit_expr_reference<'ast, V>(v: &mut V, node: &'ast crate::ExprReference)
@@ -3142,6 +3169,21 @@ where
     v.visit_ident(&node.ident);
     v.visit_path_arguments(&node.arguments);
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+pub fn visit_pointer_mutability<'ast, V>(v: &mut V, node: &'ast crate::PointerMutability)
+where
+    V: Visit<'ast> + ?Sized,
+{
+    match node {
+        crate::PointerMutability::Const(_binding_0) => {
+            skip!(_binding_0);
+        }
+        crate::PointerMutability::Mut(_binding_0) => {
+            skip!(_binding_0);
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
 pub fn visit_predicate_lifetime<'ast, V>(v: &mut V, node: &'ast crate::PredicateLifetime)
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index b70a4227..997ef266 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -265,6 +265,11 @@ pub trait VisitMut {
     fn visit_expr_range_mut(&mut self, i: &mut crate::ExprRange) {
         visit_expr_range_mut(self, i);
     }
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    fn visit_expr_raw_addr_mut(&mut self, i: &mut crate::ExprRawAddr) {
+        visit_expr_raw_addr_mut(self, i);
+    }
     #[cfg(any(feature = "derive", feature = "full"))]
     #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
     fn visit_expr_reference_mut(&mut self, i: &mut crate::ExprReference) {
@@ -686,6 +691,11 @@ pub trait VisitMut {
     fn visit_path_segment_mut(&mut self, i: &mut crate::PathSegment) {
         visit_path_segment_mut(self, i);
     }
+    #[cfg(feature = "full")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+    fn visit_pointer_mutability_mut(&mut self, i: &mut crate::PointerMutability) {
+        visit_pointer_mutability_mut(self, i);
+    }
     #[cfg(any(feature = "derive", feature = "full"))]
     #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
     fn visit_predicate_lifetime_mut(&mut self, i: &mut crate::PredicateLifetime) {
@@ -1356,6 +1366,9 @@ where
         crate::Expr::Range(_binding_0) => {
             full!(v.visit_expr_range_mut(_binding_0));
         }
+        crate::Expr::RawAddr(_binding_0) => {
+            full!(v.visit_expr_raw_addr_mut(_binding_0));
+        }
         crate::Expr::Reference(_binding_0) => {
             v.visit_expr_reference_mut(_binding_0);
         }
@@ -1787,6 +1800,20 @@ where
         v.visit_expr_mut(&mut **it);
     }
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+pub fn visit_expr_raw_addr_mut<V>(v: &mut V, node: &mut crate::ExprRawAddr)
+where
+    V: VisitMut + ?Sized,
+{
+    for it in &mut node.attrs {
+        v.visit_attribute_mut(it);
+    }
+    skip!(node.and_token);
+    skip!(node.raw);
+    v.visit_pointer_mutability_mut(&mut node.mutability);
+    v.visit_expr_mut(&mut *node.expr);
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
 pub fn visit_expr_reference_mut<V>(v: &mut V, node: &mut crate::ExprReference)
@@ -3142,6 +3169,21 @@ where
     v.visit_ident_mut(&mut node.ident);
     v.visit_path_arguments_mut(&mut node.arguments);
 }
+#[cfg(feature = "full")]
+#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
+pub fn visit_pointer_mutability_mut<V>(v: &mut V, node: &mut crate::PointerMutability)
+where
+    V: VisitMut + ?Sized,
+{
+    match node {
+        crate::PointerMutability::Const(_binding_0) => {
+            skip!(_binding_0);
+        }
+        crate::PointerMutability::Mut(_binding_0) => {
+            skip!(_binding_0);
+        }
+    }
+}
 #[cfg(any(feature = "derive", feature = "full"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))]
 pub fn visit_predicate_lifetime_mut<V>(v: &mut V, node: &mut crate::PredicateLifetime)
diff --git a/src/lib.rs b/src/lib.rs
index ea127778..5b2f8693 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -363,7 +363,7 @@ pub use crate::error::{Error, Result};
 mod expr;
 #[cfg(feature = "full")]
 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
-pub use crate::expr::{Arm, Label, RangeLimits};
+pub use crate::expr::{Arm, Label, PointerMutability, RangeLimits};
 #[cfg(any(feature = "full", feature = "derive"))]
 #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
 pub use crate::expr::{
@@ -375,8 +375,8 @@ pub use crate::expr::{
 pub use crate::expr::{
     ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBlock, ExprBreak, ExprClosure, ExprConst,
     ExprContinue, ExprForLoop, ExprGroup, ExprIf, ExprInfer, ExprLet, ExprLoop, ExprMatch,
-    ExprRange, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprTuple, ExprUnsafe, ExprWhile,
-    ExprYield,
+    ExprRange, ExprRawAddr, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprTuple, ExprUnsafe,
+    ExprWhile, ExprYield,
 };
 
 #[cfg(feature = "parsing")]
diff --git a/src/precedence.rs b/src/precedence.rs
index 1a26f195..936a0c51 100644
--- a/src/precedence.rs
+++ b/src/precedence.rs
@@ -102,7 +102,7 @@ impl Precedence {
             Expr::Binary(e) => Precedence::of_binop(&e.op),
             Expr::Let(_) => Precedence::Let,
             Expr::Cast(_) => Precedence::Cast,
-            Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix,
+            Expr::RawAddr(_) | Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix,
 
             Expr::Array(_)
             | Expr::Async(_)
diff --git a/src/stmt.rs b/src/stmt.rs
index 51d00058..ac8238a9 100644
--- a/src/stmt.rs
+++ b/src/stmt.rs
@@ -367,6 +367,7 @@ pub(crate) mod parsing {
                 | Expr::Paren(_)
                 | Expr::Path(_)
                 | Expr::Range(_)
+                | Expr::RawAddr(_)
                 | Expr::Reference(_)
                 | Expr::Repeat(_)
                 | Expr::Return(_)
diff --git a/src/token.rs b/src/token.rs
index 061e8806..64627132 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -721,6 +721,7 @@ define_keywords! {
     "override"    pub struct Override
     "priv"        pub struct Priv
     "pub"         pub struct Pub
+    "raw"         pub struct Raw
     "ref"         pub struct Ref
     "return"      pub struct Return
     "Self"        pub struct SelfType
@@ -899,6 +900,7 @@ macro_rules! Token {
     [override]    => { $crate::token::Override };
     [priv]        => { $crate::token::Priv };
     [pub]         => { $crate::token::Pub };
+    [raw]         => { $crate::token::Raw };
     [ref]         => { $crate::token::Ref };
     [return]      => { $crate::token::Return };
     [Self]        => { $crate::token::SelfType };
diff --git a/syn.json b/syn.json
index d14299d3..e425ca17 100644
--- a/syn.json
+++ b/syn.json
@@ -775,6 +775,11 @@
             "syn": "ExprRange"
           }
         ],
+        "RawAddr": [
+          {
+            "syn": "ExprRawAddr"
+          }
+        ],
         "Reference": [
           {
             "syn": "ExprReference"
@@ -1619,6 +1624,35 @@
         }
       }
     },
+    {
+      "ident": "ExprRawAddr",
+      "features": {
+        "any": [
+          "full"
+        ]
+      },
+      "fields": {
+        "attrs": {
+          "vec": {
+            "syn": "Attribute"
+          }
+        },
+        "and_token": {
+          "token": "And"
+        },
+        "raw": {
+          "token": "Raw"
+        },
+        "mutability": {
+          "syn": "PointerMutability"
+        },
+        "expr": {
+          "box": {
+            "syn": "Expr"
+          }
+        }
+      }
+    },
     {
       "ident": "ExprReference",
       "features": {
@@ -4152,6 +4186,26 @@
         }
       }
     },
+    {
+      "ident": "PointerMutability",
+      "features": {
+        "any": [
+          "full"
+        ]
+      },
+      "variants": {
+        "Const": [
+          {
+            "token": "Const"
+          }
+        ],
+        "Mut": [
+          {
+            "token": "Mut"
+          }
+        ]
+      }
+    },
     {
       "ident": "PredicateLifetime",
       "features": {
@@ -5544,6 +5598,7 @@
     "Pub": "pub",
     "Question": "?",
     "RArrow": "->",
+    "Raw": "raw",
     "Ref": "ref",
     "Return": "return",
     "SelfType": "Self",


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index 9f726683..d900ece2 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -856,6 +856,15 @@ impl Debug for Lite<syn::Expr> {
                 }
                 formatter.finish()
             }
+            syn::Expr::RawAddr(_val) => {
+                let mut formatter = formatter.debug_struct("Expr::RawAddr");
+                if !_val.attrs.is_empty() {
+                    formatter.field("attrs", Lite(&_val.attrs));
+                }
+                formatter.field("mutability", Lite(&_val.mutability));
+                formatter.field("expr", Lite(&_val.expr));
+                formatter.finish()
+            }
             syn::Expr::Reference(_val) => {
                 let mut formatter = formatter.debug_struct("Expr::Reference");
                 if !_val.attrs.is_empty() {
@@ -1513,6 +1522,17 @@ impl Debug for Lite<syn::ExprRange> {
         formatter.finish()
     }
 }
+impl Debug for Lite<syn::ExprRawAddr> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let mut formatter = formatter.debug_struct("ExprRawAddr");
+        if !self.value.attrs.is_empty() {
+            formatter.field("attrs", Lite(&self.value.attrs));
+        }
+        formatter.field("mutability", Lite(&self.value.mutability));
+        formatter.field("expr", Lite(&self.value.expr));
+        formatter.finish()
+    }
+}
 impl Debug for Lite<syn::ExprReference> {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let mut formatter = formatter.debug_struct("ExprReference");
@@ -3532,6 +3552,20 @@ impl Debug for Lite<syn::PathSegment> {
         formatter.finish()
     }
 }
+impl Debug for Lite<syn::PointerMutability> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match &self.value {
+            syn::PointerMutability::Const(_val) => {
+                formatter.write_str("PointerMutability::Const")?;
+                Ok(())
+            }
+            syn::PointerMutability::Mut(_val) => {
+                formatter.write_str("PointerMutability::Mut")?;
+                Ok(())
+            }
+        }
+    }
+}
 impl Debug for Lite<syn::PredicateLifetime> {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let mut formatter = formatter.debug_struct("PredicateLifetime");
@@ -5011,6 +5045,11 @@ impl Debug for Lite<syn::token::RArrow> {
         formatter.write_str("Token![->]")
     }
 }
+impl Debug for Lite<syn::token::Raw> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("Token![raw]")
+    }
+}
 impl Debug for Lite<syn::token::Ref> {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         formatter.write_str("Token![ref]")
diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs
index cc48587a..7231bd64 100644
--- a/tests/test_precedence.rs
+++ b/tests/test_precedence.rs
@@ -186,9 +186,9 @@ fn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
 
 fn librustc_parenthesize(mut librustc_expr: P<ast::Expr>) -> P<ast::Expr> {
     use rustc_ast::ast::{
-        AssocItem, AssocItemKind, Attribute, BinOpKind, Block, BorrowKind, BoundConstness, Expr,
-        ExprField, ExprKind, GenericArg, GenericBound, Local, LocalKind, Pat, PolyTraitRef, Stmt,
-        StmtKind, StructExpr, StructRest, TraitBoundModifiers, Ty,
+        AssocItem, AssocItemKind, Attribute, BinOpKind, Block, BoundConstness, Expr, ExprField,
+        ExprKind, GenericArg, GenericBound, Local, LocalKind, Pat, PolyTraitRef, Stmt, StmtKind,
+        StructExpr, StructRest, TraitBoundModifiers, Ty,
     };
     use rustc_ast::mut_visit::{walk_flat_map_item, MutVisitor};
     use rustc_ast::visit::{AssocCtxt, BoundKind};
@@ -240,7 +240,7 @@ fn librustc_parenthesize(mut librustc_expr: P<ast::Expr>) -> P<ast::Expr> {
 
     fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
         match &mut e.kind {
-            ExprKind::AddrOf(BorrowKind::Raw, ..) | ExprKind::Become(..) => {}
+            ExprKind::Become(..) => {}
             ExprKind::Struct(expr) => {
                 let StructExpr {
                     qself,
diff --git a/tests/test_stmt.rs b/tests/test_stmt.rs
index e30c29cf..b2a49b05 100644
--- a/tests/test_stmt.rs
+++ b/tests/test_stmt.rs
@@ -21,7 +21,18 @@ fn test_raw_operator() {
     Stmt::Local {
         pat: Pat::Wild,
         init: Some(LocalInit {
-            expr: Expr::Verbatim(`& raw const x`),
+            expr: Expr::RawAddr {
+                mutability: PointerMutability::Const,
+                expr: Expr::Path {
+                    path: Path {
+                        segments: [
+                            PathSegment {
+                                ident: "x",
+                            },
+                        ],
+                    },
+                },
+            },
         }),
     }
     "#);