From c1ee73b55904970e75913801ea46c785868e29e2 Mon Sep 17 00:00:00 2001 From: Burkhard Mittelbach Date: Tue, 20 Jan 2026 17:34:04 +0100 Subject: [PATCH] expose "safe fn" in extern block as ForeignItemFn --- src/gen/clone.rs | 32 ++++++++ src/gen/debug.rs | 39 +++++++++ src/gen/eq.rs | 31 +++++++ src/gen/fold.rs | 60 +++++++++++++- src/gen/hash.rs | 38 +++++++++ src/gen/token.css | 7 ++ src/gen/visit.rs | 53 +++++++++++- src/gen/visit_mut.rs | 53 +++++++++++- src/item.rs | 189 +++++++++++++++++++++++++++++++++++-------- src/lib.rs | 13 +-- src/token.rs | 2 + syn.json | 80 +++++++++++++++++- tests/debug/gen.rs | 72 +++++++++++++++++ 13 files changed, 627 insertions(+), 42 deletions(-) diff --git a/src/gen/clone.rs b/src/gen/clone.rs index be2b698422..bf94f3bc44 100644 --- a/src/gen/clone.rs +++ b/src/gen/clone.rs @@ -879,6 +879,19 @@ impl Clone for crate::FnArg { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] +impl Clone for crate::ForeignFnSafety { + fn clone(&self) -> Self { + match self { + crate::ForeignFnSafety::Unsafe(v0) => { + crate::ForeignFnSafety::Unsafe(v0.clone()) + } + crate::ForeignFnSafety::Safe(v0) => crate::ForeignFnSafety::Safe(v0.clone()), + crate::ForeignFnSafety::None => crate::ForeignFnSafety::None, + } + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] impl Clone for crate::ForeignItem { fn clone(&self) -> Self { match self { @@ -943,6 +956,25 @@ impl Clone for crate::ForeignItemType { } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] +impl Clone for crate::ForeignSignature { + fn clone(&self) -> Self { + crate::ForeignSignature { + constness: self.constness.clone(), + asyncness: self.asyncness.clone(), + safety: self.safety.clone(), + abi: self.abi.clone(), + fn_token: self.fn_token.clone(), + ident: self.ident.clone(), + generics: self.generics.clone(), + paren_token: self.paren_token.clone(), + inputs: self.inputs.clone(), + variadic: self.variadic.clone(), + output: self.output.clone(), + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] impl Clone for crate::GenericArgument { diff --git a/src/gen/debug.rs b/src/gen/debug.rs index bf0784d1c4..9bf2b048fc 100644 --- a/src/gen/debug.rs +++ b/src/gen/debug.rs @@ -1318,6 +1318,26 @@ impl Debug for crate::FnArg { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Debug for crate::ForeignFnSafety { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("ForeignFnSafety::")?; + match self { + crate::ForeignFnSafety::Unsafe(v0) => { + let mut formatter = formatter.debug_tuple("Unsafe"); + formatter.field(v0); + formatter.finish() + } + crate::ForeignFnSafety::Safe(v0) => { + let mut formatter = formatter.debug_tuple("Safe"); + formatter.field(v0); + formatter.finish() + } + crate::ForeignFnSafety::None => formatter.write_str("None"), + } + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for crate::ForeignItem { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("ForeignItem::")?; @@ -1411,6 +1431,25 @@ impl crate::ForeignItemType { formatter.finish() } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Debug for crate::ForeignSignature { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = formatter.debug_struct("ForeignSignature"); + formatter.field("constness", &self.constness); + formatter.field("asyncness", &self.asyncness); + formatter.field("safety", &self.safety); + formatter.field("abi", &self.abi); + formatter.field("fn_token", &self.fn_token); + formatter.field("ident", &self.ident); + formatter.field("generics", &self.generics); + formatter.field("paren_token", &self.paren_token); + formatter.field("inputs", &self.inputs); + formatter.field("variadic", &self.variadic); + formatter.field("output", &self.output); + formatter.finish() + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for crate::GenericArgument { diff --git a/src/gen/eq.rs b/src/gen/eq.rs index 5ba15722b7..c21cd07782 100644 --- a/src/gen/eq.rs +++ b/src/gen/eq.rs @@ -874,6 +874,23 @@ impl PartialEq for crate::FnArg { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Eq for crate::ForeignFnSafety {} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl PartialEq for crate::ForeignFnSafety { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (crate::ForeignFnSafety::Unsafe(_), crate::ForeignFnSafety::Unsafe(_)) => { + true + } + (crate::ForeignFnSafety::Safe(_), crate::ForeignFnSafety::Safe(_)) => true, + (crate::ForeignFnSafety::None, crate::ForeignFnSafety::None) => true, + _ => false, + } + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Eq for crate::ForeignItem {} #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] @@ -944,6 +961,20 @@ impl PartialEq for crate::ForeignItemType { && self.generics == other.generics } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Eq for crate::ForeignSignature {} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl PartialEq for crate::ForeignSignature { + fn eq(&self, other: &Self) -> bool { + self.constness == other.constness && self.asyncness == other.asyncness + && self.safety == other.safety && self.abi == other.abi + && self.ident == other.ident && self.generics == other.generics + && self.inputs == other.inputs && self.variadic == other.variadic + && self.output == other.output + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Eq for crate::GenericArgument {} diff --git a/src/gen/fold.rs b/src/gen/fold.rs index b71421f47a..4841fe8ebb 100644 --- a/src/gen/fold.rs +++ b/src/gen/fold.rs @@ -393,6 +393,14 @@ pub trait Fold { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn fold_foreign_fn_safety( + &mut self, + i: crate::ForeignFnSafety, + ) -> crate::ForeignFnSafety { + fold_foreign_fn_safety(self, i) + } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] fn fold_foreign_item(&mut self, i: crate::ForeignItem) -> crate::ForeignItem { fold_foreign_item(self, i) } @@ -425,6 +433,14 @@ pub trait Fold { ) -> crate::ForeignItemType { fold_foreign_item_type(self, i) } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn fold_foreign_signature( + &mut self, + i: crate::ForeignSignature, + ) -> crate::ForeignSignature { + fold_foreign_signature(self, i) + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn fold_generic_argument( @@ -2083,6 +2099,25 @@ where } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn fold_foreign_fn_safety( + f: &mut F, + node: crate::ForeignFnSafety, +) -> crate::ForeignFnSafety +where + F: Fold + ?Sized, +{ + match node { + crate::ForeignFnSafety::Unsafe(_binding_0) => { + crate::ForeignFnSafety::Unsafe(_binding_0) + } + crate::ForeignFnSafety::Safe(_binding_0) => { + crate::ForeignFnSafety::Safe(_binding_0) + } + crate::ForeignFnSafety::None => crate::ForeignFnSafety::None, + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] pub fn fold_foreign_item(f: &mut F, node: crate::ForeignItem) -> crate::ForeignItem where F: Fold + ?Sized, @@ -2117,7 +2152,7 @@ where crate::ForeignItemFn { attrs: f.fold_attributes(node.attrs), vis: f.fold_visibility(node.vis), - sig: f.fold_signature(node.sig), + sig: f.fold_foreign_signature(node.sig), semi_token: node.semi_token, } } @@ -2174,6 +2209,29 @@ where semi_token: node.semi_token, } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn fold_foreign_signature( + f: &mut F, + node: crate::ForeignSignature, +) -> crate::ForeignSignature +where + F: Fold + ?Sized, +{ + crate::ForeignSignature { + constness: node.constness, + asyncness: node.asyncness, + safety: f.fold_foreign_fn_safety(node.safety), + abi: (node.abi).map(|it| f.fold_abi(it)), + fn_token: node.fn_token, + ident: f.fold_ident(node.ident), + generics: f.fold_generics(node.generics), + paren_token: node.paren_token, + inputs: crate::punctuated::fold(node.inputs, f, F::fold_fn_arg), + variadic: (node.variadic).map(|it| f.fold_variadic(it)), + output: f.fold_return_type(node.output), + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn fold_generic_argument( diff --git a/src/gen/hash.rs b/src/gen/hash.rs index 508af526c1..77e8d1430e 100644 --- a/src/gen/hash.rs +++ b/src/gen/hash.rs @@ -1141,6 +1141,26 @@ impl Hash for crate::FnArg { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Hash for crate::ForeignFnSafety { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + match self { + crate::ForeignFnSafety::Unsafe(_) => { + state.write_u8(0u8); + } + crate::ForeignFnSafety::Safe(_) => { + state.write_u8(1u8); + } + crate::ForeignFnSafety::None => { + state.write_u8(2u8); + } + } + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Hash for crate::ForeignItem { fn hash(&self, state: &mut H) where @@ -1221,6 +1241,24 @@ impl Hash for crate::ForeignItemType { self.generics.hash(state); } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Hash for crate::ForeignSignature { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.constness.hash(state); + self.asyncness.hash(state); + self.safety.hash(state); + self.abi.hash(state); + self.ident.hash(state); + self.generics.hash(state); + self.inputs.hash(state); + self.variadic.hash(state); + self.output.hash(state); + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Hash for crate::GenericArgument { diff --git a/src/gen/token.css b/src/gen/token.css index ed82ae1502..d1055689b5 100644 --- a/src/gen/token.css +++ b/src/gen/token.css @@ -70,6 +70,7 @@ a.struct[title="struct syn::token::RArrow"], a.struct[title="struct syn::token::Raw"], a.struct[title="struct syn::token::Ref"], a.struct[title="struct syn::token::Return"], +a.struct[title="struct syn::token::Safe"], a.struct[title="struct syn::token::SelfType"], a.struct[title="struct syn::token::SelfValue"], a.struct[title="struct syn::token::Semi"], @@ -175,6 +176,7 @@ a.struct[title="struct syn::token::RArrow"]::before, a.struct[title="struct syn::token::Raw"]::before, a.struct[title="struct syn::token::Ref"]::before, a.struct[title="struct syn::token::Return"]::before, +a.struct[title="struct syn::token::Safe"]::before, a.struct[title="struct syn::token::SelfType"]::before, a.struct[title="struct syn::token::SelfValue"]::before, a.struct[title="struct syn::token::Semi"]::before, @@ -496,6 +498,10 @@ a.struct[title="struct syn::token::Return"]::before { content: "Token![return]"; } +a.struct[title="struct syn::token::Safe"]::before { + content: "Token![safe]"; +} + a.struct[title="struct syn::token::SelfType"]::before { content: "Token![Self]"; } @@ -718,6 +724,7 @@ a.struct[title="struct syn::token::Pub"]::after, a.struct[title="struct syn::token::Raw"]::after, a.struct[title="struct syn::token::Ref"]::after, a.struct[title="struct syn::token::Return"]::after, +a.struct[title="struct syn::token::Safe"]::after, a.struct[title="struct syn::token::Static"]::after, a.struct[title="struct syn::token::Struct"]::after, a.struct[title="struct syn::token::Super"]::after, diff --git a/src/gen/visit.rs b/src/gen/visit.rs index cd258fcde1..fc8100fc05 100644 --- a/src/gen/visit.rs +++ b/src/gen/visit.rs @@ -376,6 +376,11 @@ pub trait Visit<'ast> { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_foreign_fn_safety(&mut self, i: &'ast crate::ForeignFnSafety) { + visit_foreign_fn_safety(self, i); + } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] fn visit_foreign_item(&mut self, i: &'ast crate::ForeignItem) { visit_foreign_item(self, i); } @@ -399,6 +404,11 @@ pub trait Visit<'ast> { fn visit_foreign_item_type(&mut self, i: &'ast crate::ForeignItemType) { visit_foreign_item_type(self, i); } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_foreign_signature(&mut self, i: &'ast crate::ForeignSignature) { + visit_foreign_signature(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn visit_generic_argument(&mut self, i: &'ast crate::GenericArgument) { @@ -2118,6 +2128,22 @@ where } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_foreign_fn_safety<'ast, V>(v: &mut V, node: &'ast crate::ForeignFnSafety) +where + V: Visit<'ast> + ?Sized, +{ + match node { + crate::ForeignFnSafety::Unsafe(_binding_0) => { + skip!(_binding_0); + } + crate::ForeignFnSafety::Safe(_binding_0) => { + skip!(_binding_0); + } + crate::ForeignFnSafety::None => {} + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] pub fn visit_foreign_item<'ast, V>(v: &mut V, node: &'ast crate::ForeignItem) where V: Visit<'ast> + ?Sized, @@ -2150,7 +2176,7 @@ where v.visit_attribute(it); } v.visit_visibility(&node.vis); - v.visit_signature(&node.sig); + v.visit_foreign_signature(&node.sig); skip!(node.semi_token); } #[cfg(feature = "full")] @@ -2200,6 +2226,31 @@ where v.visit_generics(&node.generics); skip!(node.semi_token); } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_foreign_signature<'ast, V>(v: &mut V, node: &'ast crate::ForeignSignature) +where + V: Visit<'ast> + ?Sized, +{ + skip!(node.constness); + skip!(node.asyncness); + v.visit_foreign_fn_safety(&node.safety); + if let Some(it) = &node.abi { + v.visit_abi(it); + } + skip!(node.fn_token); + v.visit_ident(&node.ident); + v.visit_generics(&node.generics); + skip!(node.paren_token); + for el in Punctuated::pairs(&node.inputs) { + let it = el.value(); + v.visit_fn_arg(it); + } + if let Some(it) = &node.variadic { + v.visit_variadic(it); + } + v.visit_return_type(&node.output); +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn visit_generic_argument<'ast, V>(v: &mut V, node: &'ast crate::GenericArgument) diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs index 6588182c4f..3e43b2964a 100644 --- a/src/gen/visit_mut.rs +++ b/src/gen/visit_mut.rs @@ -386,6 +386,11 @@ pub trait VisitMut { } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_foreign_fn_safety_mut(&mut self, i: &mut crate::ForeignFnSafety) { + visit_foreign_fn_safety_mut(self, i); + } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] fn visit_foreign_item_mut(&mut self, i: &mut crate::ForeignItem) { visit_foreign_item_mut(self, i); } @@ -409,6 +414,11 @@ pub trait VisitMut { fn visit_foreign_item_type_mut(&mut self, i: &mut crate::ForeignItemType) { visit_foreign_item_type_mut(self, i); } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_foreign_signature_mut(&mut self, i: &mut crate::ForeignSignature) { + visit_foreign_signature_mut(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn visit_generic_argument_mut(&mut self, i: &mut crate::GenericArgument) { @@ -2032,6 +2042,22 @@ where } #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_foreign_fn_safety_mut(v: &mut V, node: &mut crate::ForeignFnSafety) +where + V: VisitMut + ?Sized, +{ + match node { + crate::ForeignFnSafety::Unsafe(_binding_0) => { + skip!(_binding_0); + } + crate::ForeignFnSafety::Safe(_binding_0) => { + skip!(_binding_0); + } + crate::ForeignFnSafety::None => {} + } +} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] pub fn visit_foreign_item_mut(v: &mut V, node: &mut crate::ForeignItem) where V: VisitMut + ?Sized, @@ -2062,7 +2088,7 @@ where { v.visit_attributes_mut(&mut node.attrs); v.visit_visibility_mut(&mut node.vis); - v.visit_signature_mut(&mut node.sig); + v.visit_foreign_signature_mut(&mut node.sig); skip!(node.semi_token); } #[cfg(feature = "full")] @@ -2103,6 +2129,31 @@ where v.visit_generics_mut(&mut node.generics); skip!(node.semi_token); } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_foreign_signature_mut(v: &mut V, node: &mut crate::ForeignSignature) +where + V: VisitMut + ?Sized, +{ + skip!(node.constness); + skip!(node.asyncness); + v.visit_foreign_fn_safety_mut(&mut node.safety); + if let Some(it) = &mut node.abi { + v.visit_abi_mut(it); + } + skip!(node.fn_token); + v.visit_ident_mut(&mut node.ident); + v.visit_generics_mut(&mut node.generics); + skip!(node.paren_token); + for mut el in Punctuated::pairs_mut(&mut node.inputs) { + let it = el.value_mut(); + v.visit_fn_arg_mut(it); + } + if let Some(it) = &mut node.variadic { + v.visit_variadic_mut(it); + } + v.visit_return_type_mut(&mut node.output); +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn visit_generic_argument_mut(v: &mut V, node: &mut crate::GenericArgument) diff --git a/src/item.rs b/src/item.rs index d977609b16..c114a2679c 100644 --- a/src/item.rs +++ b/src/item.rs @@ -547,7 +547,7 @@ ast_struct! { pub struct ForeignItemFn { pub attrs: Vec, pub vis: Visibility, - pub sig: Signature, + pub sig: ForeignSignature, pub semi_token: Token![;], } } @@ -590,6 +590,51 @@ ast_struct! { } } +ast_struct! { + /// A function signature in a trait or implementation in an extern block: + /// `extern { unsafe fn initialize(&self) }`. + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + pub struct ForeignSignature { + pub constness: Option, + pub asyncness: Option, + pub safety: ForeignFnSafety, + pub abi: Option, + pub fn_token: Token![fn], + pub ident: Ident, + pub generics: Generics, + pub paren_token: token::Paren, + pub inputs: Punctuated, + pub variadic: Option, + pub output: ReturnType, + } +} + +impl ForeignSignature { + /// A method's `self` receiver, such as `&self` or `self: Box`. + pub fn receiver(&self) -> Option<&Receiver> { + let arg = self.inputs.first()?; + match arg { + FnArg::Receiver(receiver) => Some(receiver), + FnArg::Typed(_) => None, + } + } +} + +ast_enum! { + /// The safety of a `ForeignItemFn` + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + pub enum ForeignFnSafety { + /// The function is qualified as `unsafe` + Unsafe(Token![unsafe]), + /// The function is qualified as `safe` + Safe(Token![safe]), + /// The function is not qualified as `unsafe` or `safe` + None + } +} + + + ast_enum_of_structs! { /// An item declaration within the definition of a trait. /// @@ -914,12 +959,13 @@ pub(crate) mod parsing { use crate::generics::{self, Generics, TypeParamBound}; use crate::ident::Ident; use crate::item::{ - FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, - ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, ImplItemType, Item, ItemConst, - ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, - ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, - Signature, StaticMutability, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, - TraitItemType, UseGlob, UseGroup, UseName, UsePath, UseRename, UseTree, Variadic, + FnArg, ForeignFnSafety, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, + ForeignItemType, ForeignSignature, ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, + ImplItemType, Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, + ItemMacro, ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, + ItemUse, Receiver, Signature, StaticMutability, TraitItem, TraitItemConst, TraitItemFn, + TraitItemMacro, TraitItemType, UseGlob, UseGroup, UseName, UsePath, UseRename, UseTree, + Variadic, }; use crate::lifetime::Lifetime; use crate::lit::LitStr; @@ -1510,20 +1556,66 @@ pub(crate) mod parsing { impl Parse for Signature { fn parse(input: ParseStream) -> Result { let allow_safe = false; - parse_signature(input, allow_safe).map(Option::unwrap) + parse_signature(input, allow_safe).map(|(sig, _safety)| sig) } } - fn parse_signature(input: ParseStream, allow_safe: bool) -> Result> { + #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] + impl Parse for ForeignSignature { + fn parse(input: ParseStream) -> Result { + let allow_safe = true; + let ( + Signature { + constness, + asyncness, + unsafety, + abi, + fn_token, + ident, + generics, + paren_token, + inputs, + variadic, + output, + }, + safety, + ) = parse_signature(input, allow_safe)?; + + let safety = match (unsafety, safety) { + (Some(unsafety), None) => ForeignFnSafety::Unsafe(unsafety), + (None, Some(safety)) => ForeignFnSafety::Safe(safety), + (None, None) => ForeignFnSafety::None, + (Some(_), Some(_)) => unreachable!(), + }; + + Ok(ForeignSignature { + constness, + asyncness, + safety, + abi, + fn_token, + ident, + generics, + paren_token, + inputs, + variadic, + output, + }) + } + } + + fn parse_signature( + input: ParseStream, + allow_safe: bool, + ) -> Result<(Signature, Option)> { let constness: Option = input.parse()?; let asyncness: Option = input.parse()?; let unsafety: Option = input.parse()?; - let safe = allow_safe - && unsafety.is_none() - && token::parsing::peek_keyword(input.cursor(), "safe"); - if safe { - token::parsing::keyword(input, "safe")?; - } + let safety: Option = if allow_safe && unsafety.is_none() { + input.parse()? + } else { + None + }; let abi: Option = input.parse()?; let fn_token: Token![fn] = input.parse()?; let ident: Ident = input.parse()?; @@ -1536,10 +1628,8 @@ pub(crate) mod parsing { let output: ReturnType = input.parse()?; generics.where_clause = input.parse()?; - Ok(if safe { - None - } else { - Some(Signature { + Ok(( + Signature { constness, asyncness, unsafety, @@ -1551,8 +1641,9 @@ pub(crate) mod parsing { inputs, variadic, output, - }) - }) + }, + safety, + )) } #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] @@ -1851,8 +1942,7 @@ pub(crate) mod parsing { let allow_safe = true; let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead, allow_safe) { let vis: Visibility = input.parse()?; - let sig = parse_signature(input, allow_safe)?; - let has_safe = sig.is_none(); + let sig: ForeignSignature = input.parse()?; let has_body = input.peek(token::Brace); let semi_token: Option = if has_body { let content; @@ -1863,13 +1953,13 @@ pub(crate) mod parsing { } else { Some(input.parse()?) }; - if has_safe || has_body { + if has_body { Ok(ForeignItem::Verbatim(verbatim::between(&begin, input))) } else { Ok(ForeignItem::Fn(ForeignItemFn { attrs: Vec::new(), vis, - sig: sig.unwrap(), + sig, semi_token: semi_token.unwrap(), })) } @@ -1943,7 +2033,7 @@ pub(crate) mod parsing { fn parse(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; - let sig: Signature = input.parse()?; + let sig: ForeignSignature = input.parse()?; let semi_token: Token![;] = input.parse()?; Ok(ForeignItemFn { attrs, @@ -2937,12 +3027,12 @@ mod printing { use crate::attr::FilterAttrs; use crate::data::Fields; use crate::item::{ - ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, ImplItemConst, - ImplItemFn, ImplItemMacro, ImplItemType, ItemConst, ItemEnum, ItemExternCrate, ItemFn, - ItemForeignMod, ItemImpl, ItemMacro, ItemMod, ItemStatic, ItemStruct, ItemTrait, - ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, Signature, StaticMutability, - TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, UseGlob, UseGroup, UseName, - UsePath, UseRename, Variadic, + ForeignFnSafety, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, + ForeignSignature, ImplItemConst, ImplItemFn, ImplItemMacro, ImplItemType, ItemConst, + ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, + ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, + Signature, StaticMutability, TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, + UseGlob, UseGroup, UseName, UsePath, UseRename, Variadic, }; use crate::mac::MacroDelimiter; use crate::path; @@ -3412,6 +3502,41 @@ mod printing { } } + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] + impl ToTokens for ForeignSignature { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.constness.to_tokens(tokens); + self.asyncness.to_tokens(tokens); + self.safety.to_tokens(tokens); + self.abi.to_tokens(tokens); + self.fn_token.to_tokens(tokens); + self.ident.to_tokens(tokens); + self.generics.to_tokens(tokens); + self.paren_token.surround(tokens, |tokens| { + self.inputs.to_tokens(tokens); + if let Some(variadic) = &self.variadic { + if !self.inputs.empty_or_trailing() { + ::default().to_tokens(tokens); + } + variadic.to_tokens(tokens); + } + }); + self.output.to_tokens(tokens); + self.generics.where_clause.to_tokens(tokens); + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] + impl ToTokens for ForeignFnSafety { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + ForeignFnSafety::Unsafe(unsafety) => unsafety.to_tokens(tokens), + ForeignFnSafety::Safe(safety) => safety.to_tokens(tokens), + ForeignFnSafety::None => {} + } + } + } + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for Signature { fn to_tokens(&self, tokens: &mut TokenStream) { diff --git a/src/lib.rs b/src/lib.rs index 28a6dd6d79..ecdff5b862 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -426,12 +426,13 @@ mod item; #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pub use crate::item::{ - FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, - ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, ImplItemType, ImplRestriction, Item, - ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, - ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, - Signature, StaticMutability, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, - TraitItemType, UseGlob, UseGroup, UseName, UsePath, UseRename, UseTree, Variadic, + FnArg, ForeignFnSafety, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, + ForeignItemType, ForeignSignature, ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, + ImplItemType, ImplRestriction, Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn, + ItemForeignMod, ItemImpl, ItemMacro, ItemMod, ItemStatic, ItemStruct, ItemTrait, + ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, Signature, StaticMutability, TraitItem, + TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, UseGlob, UseGroup, UseName, + UsePath, UseRename, UseTree, Variadic, }; mod lifetime; diff --git a/src/token.rs b/src/token.rs index 8d63a4111b..e11813c8fd 100644 --- a/src/token.rs +++ b/src/token.rs @@ -726,6 +726,7 @@ define_keywords! { "raw" pub struct Raw "ref" pub struct Ref "return" pub struct Return + "safe" pub struct Safe "Self" pub struct SelfType "self" pub struct SelfValue "static" pub struct Static @@ -905,6 +906,7 @@ macro_rules! Token { [raw] => { $crate::token::Raw }; [ref] => { $crate::token::Ref }; [return] => { $crate::token::Return }; + [safe] => { $crate::token::Safe }; [Self] => { $crate::token::SelfType }; [self] => { $crate::token::SelfValue }; [static] => { $crate::token::Static }; diff --git a/syn.json b/syn.json index 60f7ac42db..d8221f654a 100644 --- a/syn.json +++ b/syn.json @@ -2192,6 +2192,27 @@ ] } }, + { + "ident": "ForeignFnSafety", + "features": { + "any": [ + "full" + ] + }, + "variants": { + "Unsafe": [ + { + "token": "Unsafe" + } + ], + "Safe": [ + { + "token": "Safe" + } + ], + "None": [] + } + }, { "ident": "ForeignItem", "features": { @@ -2245,7 +2266,7 @@ "syn": "Visibility" }, "sig": { - "syn": "Signature" + "syn": "ForeignSignature" }, "semi_token": { "token": "Semi" @@ -2343,6 +2364,62 @@ } } }, + { + "ident": "ForeignSignature", + "features": { + "any": [ + "full" + ] + }, + "fields": { + "constness": { + "option": { + "token": "Const" + } + }, + "asyncness": { + "option": { + "token": "Async" + } + }, + "safety": { + "syn": "ForeignFnSafety" + }, + "abi": { + "option": { + "syn": "Abi" + } + }, + "fn_token": { + "token": "Fn" + }, + "ident": { + "proc_macro2": "Ident" + }, + "generics": { + "syn": "Generics" + }, + "paren_token": { + "group": "Paren" + }, + "inputs": { + "punctuated": { + "element": { + "syn": "FnArg" + }, + "punct": "Comma" + } + }, + "variadic": { + "option": { + "syn": "Variadic" + } + }, + "output": { + "syn": "ReturnType" + } + } + }, { "ident": "GenericArgument", "features": { @@ -5654,6 +5731,7 @@ "Raw": "raw", "Ref": "ref", "Return": "return", + "Safe": "safe", "SelfType": "Self", "SelfValue": "self", "Semi": ";", diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs index c0d37b791d..706052609d 100644 --- a/tests/debug/gen.rs +++ b/tests/debug/gen.rs @@ -1901,6 +1901,21 @@ impl Debug for Lite { } } } +impl Debug for Lite { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match &self.value { + syn::ForeignFnSafety::Unsafe(_val) => { + formatter.write_str("ForeignFnSafety::Unsafe")?; + Ok(()) + } + syn::ForeignFnSafety::Safe(_val) => { + formatter.write_str("ForeignFnSafety::Safe")?; + Ok(()) + } + syn::ForeignFnSafety::None => formatter.write_str("ForeignFnSafety::None"), + } + } +} impl Debug for Lite { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match &self.value { @@ -2015,6 +2030,58 @@ impl Debug for Lite { formatter.finish() } } +impl Debug for Lite { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = formatter.debug_struct("ForeignSignature"); + if self.value.constness.is_some() { + formatter.field("constness", &Present); + } + if self.value.asyncness.is_some() { + formatter.field("asyncness", &Present); + } + match self.value.safety { + syn::ForeignFnSafety::None => {} + _ => { + formatter.field("safety", Lite(&self.value.safety)); + } + } + if let Some(val) = &self.value.abi { + #[derive(RefCast)] + #[repr(transparent)] + struct Print(syn::Abi); + impl Debug for Print { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Some(")?; + Debug::fmt(Lite(&self.0), formatter)?; + formatter.write_str(")")?; + Ok(()) + } + } + formatter.field("abi", Print::ref_cast(val)); + } + formatter.field("ident", Lite(&self.value.ident)); + formatter.field("generics", Lite(&self.value.generics)); + if !self.value.inputs.is_empty() { + formatter.field("inputs", Lite(&self.value.inputs)); + } + if let Some(val) = &self.value.variadic { + #[derive(RefCast)] + #[repr(transparent)] + struct Print(syn::Variadic); + impl Debug for Print { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Some(")?; + Debug::fmt(Lite(&self.0), formatter)?; + formatter.write_str(")")?; + Ok(()) + } + } + formatter.field("variadic", Print::ref_cast(val)); + } + formatter.field("output", Lite(&self.value.output)); + formatter.finish() + } +} impl Debug for Lite { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match &self.value { @@ -5097,6 +5164,11 @@ impl Debug for Lite { formatter.write_str("Token![return]") } } +impl Debug for Lite { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Token![safe]") + } +} impl Debug for Lite { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("Token![Self]")