Repository: dtolnay/syn
Pull request #1369 (2023-02-06 04:11:36)

Unsafe modules and unsafe extern blocks


Patch:
diff --git a/src/gen/clone.rs b/src/gen/clone.rs
index 73e7d7a1..ba9814a4 100644
--- a/src/gen/clone.rs
+++ b/src/gen/clone.rs
@@ -1139,6 +1139,7 @@ impl Clone for ItemForeignMod {
     fn clone(&self) -> Self {
         ItemForeignMod {
             attrs: self.attrs.clone(),
+            unsafety: self.unsafety.clone(),
             abi: self.abi.clone(),
             brace_token: self.brace_token.clone(),
             items: self.items.clone(),
@@ -1181,6 +1182,7 @@ impl Clone for ItemMod {
         ItemMod {
             attrs: self.attrs.clone(),
             vis: self.vis.clone(),
+            unsafety: self.unsafety.clone(),
             mod_token: self.mod_token.clone(),
             ident: self.ident.clone(),
             content: self.content.clone(),
diff --git a/src/gen/debug.rs b/src/gen/debug.rs
index c94b2dc7..66224a4e 100644
--- a/src/gen/debug.rs
+++ b/src/gen/debug.rs
@@ -1635,6 +1635,7 @@ impl Debug for ItemForeignMod {
             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("unsafety", &self.unsafety);
                 formatter.field("abi", &self.abi);
                 formatter.field("brace_token", &self.brace_token);
                 formatter.field("items", &self.items);
@@ -1692,6 +1693,7 @@ impl Debug for ItemMod {
                 let mut formatter = formatter.debug_struct(name);
                 formatter.field("attrs", &self.attrs);
                 formatter.field("vis", &self.vis);
+                formatter.field("unsafety", &self.unsafety);
                 formatter.field("mod_token", &self.mod_token);
                 formatter.field("ident", &self.ident);
                 formatter.field("content", &self.content);
diff --git a/src/gen/eq.rs b/src/gen/eq.rs
index 4aa0bf7b..63c9909f 100644
--- a/src/gen/eq.rs
+++ b/src/gen/eq.rs
@@ -1104,7 +1104,8 @@ impl Eq for ItemForeignMod {}
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
 impl PartialEq for ItemForeignMod {
     fn eq(&self, other: &Self) -> bool {
-        self.attrs == other.attrs && self.abi == other.abi && self.items == other.items
+        self.attrs == other.attrs && self.unsafety == other.unsafety
+            && self.abi == other.abi && self.items == other.items
     }
 }
 #[cfg(feature = "full")]
@@ -1138,7 +1139,8 @@ impl Eq for ItemMod {}
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
 impl PartialEq for ItemMod {
     fn eq(&self, other: &Self) -> bool {
-        self.attrs == other.attrs && self.vis == other.vis && self.ident == other.ident
+        self.attrs == other.attrs && self.vis == other.vis
+            && self.unsafety == other.unsafety && self.ident == other.ident
             && self.content == other.content && self.semi == other.semi
     }
 }
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index 4f647203..230007b2 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -2029,6 +2029,7 @@ where
 {
     ItemForeignMod {
         attrs: FoldHelper::lift(node.attrs, |it| f.fold_attribute(it)),
+        unsafety: (node.unsafety).map(|it| Token![unsafe](tokens_helper(f, &it.span))),
         abi: f.fold_abi(node.abi),
         brace_token: Brace(tokens_helper(f, &node.brace_token.span)),
         items: FoldHelper::lift(node.items, |it| f.fold_foreign_item(it)),
@@ -2077,6 +2078,7 @@ where
     ItemMod {
         attrs: FoldHelper::lift(node.attrs, |it| f.fold_attribute(it)),
         vis: f.fold_visibility(node.vis),
+        unsafety: (node.unsafety).map(|it| Token![unsafe](tokens_helper(f, &it.span))),
         mod_token: Token![mod](tokens_helper(f, &node.mod_token.span)),
         ident: f.fold_ident(node.ident),
         content: (node.content)
diff --git a/src/gen/hash.rs b/src/gen/hash.rs
index 5940f767..f2acf2d3 100644
--- a/src/gen/hash.rs
+++ b/src/gen/hash.rs
@@ -1496,6 +1496,7 @@ impl Hash for ItemForeignMod {
         H: Hasher,
     {
         self.attrs.hash(state);
+        self.unsafety.hash(state);
         self.abi.hash(state);
         self.items.hash(state);
     }
@@ -1538,6 +1539,7 @@ impl Hash for ItemMod {
     {
         self.attrs.hash(state);
         self.vis.hash(state);
+        self.unsafety.hash(state);
         self.ident.hash(state);
         self.content.hash(state);
         self.semi.hash(state);
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index f6fadaf1..b57358ca 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -2276,6 +2276,9 @@ where
     for it in &node.attrs {
         v.visit_attribute(it);
     }
+    if let Some(it) = &node.unsafety {
+        tokens_helper(v, &it.span);
+    }
     v.visit_abi(&node.abi);
     tokens_helper(v, &node.brace_token.span);
     for it in &node.items {
@@ -2336,6 +2339,9 @@ where
         v.visit_attribute(it);
     }
     v.visit_visibility(&node.vis);
+    if let Some(it) = &node.unsafety {
+        tokens_helper(v, &it.span);
+    }
     tokens_helper(v, &node.mod_token.span);
     v.visit_ident(&node.ident);
     if let Some(it) = &node.content {
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index 7057d2d3..b961c40f 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -2279,6 +2279,9 @@ where
     for it in &mut node.attrs {
         v.visit_attribute_mut(it);
     }
+    if let Some(it) = &mut node.unsafety {
+        tokens_helper(v, &mut it.span);
+    }
     v.visit_abi_mut(&mut node.abi);
     tokens_helper(v, &mut node.brace_token.span);
     for it in &mut node.items {
@@ -2339,6 +2342,9 @@ where
         v.visit_attribute_mut(it);
     }
     v.visit_visibility_mut(&mut node.vis);
+    if let Some(it) = &mut node.unsafety {
+        tokens_helper(v, &mut it.span);
+    }
     tokens_helper(v, &mut node.mod_token.span);
     v.visit_ident_mut(&mut node.ident);
     if let Some(it) = &mut node.content {
diff --git a/src/item.rs b/src/item.rs
index f73f4aec..7351555c 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -147,6 +147,7 @@ ast_struct! {
     #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
     pub struct ItemForeignMod {
         pub attrs: Vec<Attribute>,
+        pub unsafety: Option<Token![unsafe]>,
         pub abi: Abi,
         pub brace_token: token::Brace,
         pub items: Vec<ForeignItem>,
@@ -190,6 +191,7 @@ ast_struct! {
     pub struct ItemMod {
         pub attrs: Vec<Attribute>,
         pub vis: Visibility,
+        pub unsafety: Option<Token![unsafe]>,
         pub mod_token: Token![mod],
         pub ident: Ident,
         pub content: Option<(token::Brace, Vec<Item>)>,
@@ -1012,15 +1014,9 @@ pub(crate) mod parsing {
                         Ok(Item::Verbatim(verbatim::between(begin, input)))
                     }
                 } else if lookahead.peek(Token![extern]) {
-                    input.parse::<Visibility>()?;
-                    input.parse::<Token![unsafe]>()?;
-                    input.parse::<ItemForeignMod>()?;
-                    Ok(Item::Verbatim(verbatim::between(begin, input)))
+                    input.parse().map(Item::ForeignMod)
                 } else if lookahead.peek(Token![mod]) {
-                    input.parse::<Visibility>()?;
-                    input.parse::<Token![unsafe]>()?;
-                    input.parse::<ItemMod>()?;
-                    Ok(Item::Verbatim(verbatim::between(begin, input)))
+                    input.parse().map(Item::Mod)
                 } else {
                     Err(lookahead.error())
                 }
@@ -1618,6 +1614,7 @@ pub(crate) mod parsing {
         fn parse(input: ParseStream) -> Result<Self> {
             let mut attrs = input.call(Attribute::parse_outer)?;
             let vis: Visibility = input.parse()?;
+            let unsafety: Option<Token![unsafe]> = input.parse()?;
             let mod_token: Token![mod] = input.parse()?;
             let ident: Ident = input.parse()?;
 
@@ -1626,6 +1623,7 @@ pub(crate) mod parsing {
                 Ok(ItemMod {
                     attrs,
                     vis,
+                    unsafety,
                     mod_token,
                     ident,
                     content: None,
@@ -1644,6 +1642,7 @@ pub(crate) mod parsing {
                 Ok(ItemMod {
                     attrs,
                     vis,
+                    unsafety,
                     mod_token,
                     ident,
                     content: Some((brace_token, items)),
@@ -1659,6 +1658,7 @@ pub(crate) mod parsing {
     impl Parse for ItemForeignMod {
         fn parse(input: ParseStream) -> Result<Self> {
             let mut attrs = input.call(Attribute::parse_outer)?;
+            let unsafety: Option<Token![unsafe]> = input.parse()?;
             let abi: Abi = input.parse()?;
 
             let content;
@@ -1671,6 +1671,7 @@ pub(crate) mod parsing {
 
             Ok(ItemForeignMod {
                 attrs,
+                unsafety,
                 abi,
                 brace_token,
                 items,
@@ -2784,6 +2785,7 @@ mod printing {
         fn to_tokens(&self, tokens: &mut TokenStream) {
             tokens.append_all(self.attrs.outer());
             self.vis.to_tokens(tokens);
+            self.unsafety.to_tokens(tokens);
             self.mod_token.to_tokens(tokens);
             self.ident.to_tokens(tokens);
             if let Some((brace, items)) = &self.content {
@@ -2801,6 +2803,7 @@ mod printing {
     impl ToTokens for ItemForeignMod {
         fn to_tokens(&self, tokens: &mut TokenStream) {
             tokens.append_all(self.attrs.outer());
+            self.unsafety.to_tokens(tokens);
             self.abi.to_tokens(tokens);
             self.brace_token.surround(tokens, |tokens| {
                 tokens.append_all(self.attrs.inner());
diff --git a/syn.json b/syn.json
index 97dcf153..4d328cba 100644
--- a/syn.json
+++ b/syn.json
@@ -2838,6 +2838,11 @@
             "syn": "Attribute"
           }
         },
+        "unsafety": {
+          "option": {
+            "token": "Unsafe"
+          }
+        },
         "abi": {
           "syn": "Abi"
         },
@@ -2956,6 +2961,11 @@
         "vis": {
           "syn": "Visibility"
         },
+        "unsafety": {
+          "option": {
+            "token": "Unsafe"
+          }
+        },
         "mod_token": {
           "token": "Mod"
         },


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index 480237e1..98d35b79 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -2283,6 +2283,9 @@ impl Debug for Lite<syn::Item> {
                 if !_val.attrs.is_empty() {
                     formatter.field("attrs", Lite(&_val.attrs));
                 }
+                if _val.unsafety.is_some() {
+                    formatter.field("unsafety", &Present);
+                }
                 formatter.field("abi", Lite(&_val.abi));
                 if !_val.items.is_empty() {
                     formatter.field("items", Lite(&_val.items));
@@ -2360,6 +2363,9 @@ impl Debug for Lite<syn::Item> {
                     formatter.field("attrs", Lite(&_val.attrs));
                 }
                 formatter.field("vis", Lite(&_val.vis));
+                if _val.unsafety.is_some() {
+                    formatter.field("unsafety", &Present);
+                }
                 formatter.field("ident", Lite(&_val.ident));
                 if let Some(val) = &_val.content {
                     #[derive(RefCast)]
@@ -2580,6 +2586,9 @@ impl Debug for Lite<syn::ItemForeignMod> {
         if !self.value.attrs.is_empty() {
             formatter.field("attrs", Lite(&self.value.attrs));
         }
+        if self.value.unsafety.is_some() {
+            formatter.field("unsafety", &Present);
+        }
         formatter.field("abi", Lite(&self.value.abi));
         if !self.value.items.is_empty() {
             formatter.field("items", Lite(&self.value.items));
@@ -2663,6 +2672,9 @@ impl Debug for Lite<syn::ItemMod> {
             formatter.field("attrs", Lite(&self.value.attrs));
         }
         formatter.field("vis", Lite(&self.value.vis));
+        if self.value.unsafety.is_some() {
+            formatter.field("unsafety", &Present);
+        }
         formatter.field("ident", Lite(&self.value.ident));
         if let Some(val) = &self.value.content {
             #[derive(RefCast)]