diff --git a/core/src/ast/attrs.rs b/core/src/ast/attrs.rs index fb4a0504c..2cc2c0d59 100644 --- a/core/src/ast/attrs.rs +++ b/core/src/ast/attrs.rs @@ -2,8 +2,11 @@ use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; +use std::borrow::Cow; +use std::convert::Infallible; +use std::str::FromStr; use syn::parse::{Error as ParseError, Parse, ParseStream}; -use syn::{Attribute, Ident, LitStr, Meta, Token}; +use syn::{Attribute, Expr, Ident, Lit, LitStr, Meta, Token}; /// The list of attributes on a type #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] @@ -16,6 +19,11 @@ pub struct Attrs { /// This isn't a regular attribute since AST backends do not handle regular attributes. Do not use /// in HIR backends, pub skip_if_unsupported: bool, + + /// Renames to apply to the underlying C function. Can be found on methods, impls, and bridge modules, and is inherited. + /// + /// Has no effect on types. + pub c_rename: RenameAttr, } impl Attrs { @@ -24,12 +32,14 @@ impl Attrs { Attr::Cfg(attr) => self.cfg.push(attr), Attr::DiplomatBackend(attr) => self.attrs.push(attr), Attr::SkipIfUnsupported => self.skip_if_unsupported = true, + Attr::CRename(rename) => self.c_rename.extend(&rename), } } /// Merge attributes that should be inherited from the parent pub(crate) fn merge_parent_attrs(&mut self, other: &Attrs) { - self.cfg.extend(other.cfg.iter().cloned()) + self.cfg.extend(other.cfg.iter().cloned()); + self.c_rename.extend(&other.c_rename); } pub(crate) fn add_attrs(&mut self, attrs: &[Attribute]) { for attr in syn_attr_to_ast_attr(attrs) { @@ -53,12 +63,14 @@ enum Attr { Cfg(Attribute), DiplomatBackend(DiplomatBackendAttr), SkipIfUnsupported, + CRename(RenameAttr), // More goes here } fn syn_attr_to_ast_attr(attrs: &[Attribute]) -> impl Iterator + '_ { let cfg_path: syn::Path = syn::parse_str("cfg").unwrap(); let dattr_path: syn::Path = syn::parse_str("diplomat::attr").unwrap(); + let crename_attr: syn::Path = syn::parse_str("diplomat::c_rename").unwrap(); let skipast: syn::Path = syn::parse_str("diplomat::skip_if_unsupported").unwrap(); attrs.iter().filter_map(move |a| { if a.path() == &cfg_path { @@ -68,6 +80,8 @@ fn syn_attr_to_ast_attr(attrs: &[Attribute]) -> impl Iterator + '_ a.parse_args() .expect("Failed to parse malformed diplomat::attr"), )) + } else if a.path() == &crename_attr { + Some(Attr::CRename(RenameAttr::from_syn(a).unwrap())) } else if a.path() == &skipast { Some(Attr::SkipIfUnsupported) } else { @@ -83,12 +97,23 @@ impl Serialize for Attrs { { // 1 is the number of fields in the struct. let mut state = serializer.serialize_struct("Attrs", 1)?; - let cfg: Vec<_> = self - .cfg - .iter() - .map(|a| quote::quote!(#a).to_string()) - .collect(); - state.serialize_field("cfg", &cfg)?; + if !self.cfg.is_empty() { + let cfg: Vec<_> = self + .cfg + .iter() + .map(|a| quote::quote!(#a).to_string()) + .collect(); + state.serialize_field("cfg", &cfg)?; + } + if !self.attrs.is_empty() { + state.serialize_field("attrs", &self.attrs)?; + } + if self.skip_if_unsupported { + state.serialize_field("skip_if_unsupported", &self.skip_if_unsupported)?; + } + if !self.c_rename.is_empty() { + state.serialize_field("c_rename", &self.c_rename)?; + } state.end() } } @@ -184,13 +209,121 @@ impl Parse for DiplomatBackendAttr { } } +/// A pattern for use in rename attributes, like `#[diplomat::c_rename]` +/// +/// This can be parsed from a string, typically something like `icu4x_{0}`. +/// It can have up to one {0} for replacement. +/// +/// In the future this may support transformations like to_camel_case, etc, +/// probably specified as a list like `#[diplomat::c_rename("foo{0}", to_camel_case)]` +#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)] +pub struct RenameAttr { + pattern: Option, +} + +impl RenameAttr { + /// Apply all renames to a given string + pub fn apply<'a>(&'a self, name: &'a str) -> Cow<'a, str> { + if let Some(ref pattern) = self.pattern { + let replacement = &pattern.replacement; + if let Some(index) = pattern.insertion_index { + format!("{}{name}{}", &replacement[..index], &replacement[index..]).into() + } else { + replacement.into() + } + } else { + name.into() + } + } + + /// Whether this rename is empty and will perform no changes + fn is_empty(&self) -> bool { + self.pattern.is_none() + } + + fn extend(&mut self, parent: &Self) { + // Patterns override each other on inheritance + if self.pattern.is_none() { + self.pattern = parent.pattern.clone(); + } + + // In the future if we support things like to_lower_case they may inherit separately + // from patterns. + } + + /// From a replacement pattern, like "icu4x_{0}". Can have up to one {0} in it for substitution. + fn from_pattern(s: &str) -> Self { + Self { + pattern: Some(s.parse().unwrap()), + } + } + + fn from_syn(a: &Attribute) -> Result> { + static C_RENAME_ERROR: &str = "#[diplomat::c_rename] must be given a string value"; + + match a.meta { + Meta::Path(..) => Err(C_RENAME_ERROR.into()), + Meta::NameValue(ref nv) => { + // Support a shortcut `c_rename = "..."` + let Expr::Lit(ref lit) = nv.value else { + return Err(C_RENAME_ERROR.into()); + }; + let Lit::Str(ref lit) = lit.lit else { + return Err(C_RENAME_ERROR.into()); + }; + Ok(RenameAttr::from_pattern(&lit.value())) + } + // The full syntax to which we'll add more things in the future, `c_rename("")` + Meta::List(..) => a.parse_args().map_err(|e| { + format!("Failed to parse malformed #[diplomat::c_rename(...)]: {e}").into() + }), + } + } +} + +impl FromStr for RenamePattern { + type Err = Infallible; + fn from_str(s: &str) -> Result { + if let Some(index) = s.find("{0}") { + let replacement = format!("{}{}", &s[..index], &s[index + 3..]); + Ok(Self { + replacement, + insertion_index: Some(index), + }) + } else { + Ok(Self { + replacement: s.into(), + insertion_index: None, + }) + } + } +} + +/// Meant to be used with Attribute::parse_args() +impl Parse for RenameAttr { + fn parse(input: ParseStream<'_>) -> syn::Result { + let value: LitStr = input.parse()?; + let attr = RenameAttr::from_pattern(&value.value()); + Ok(attr) + } +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)] +struct RenamePattern { + /// The string to replace with + replacement: String, + /// The index in `replacement` in which to insert the original string. If None, + /// this is a pure rename + insertion_index: Option, +} + #[cfg(test)] mod tests { use insta; use syn; - use super::{DiplomatBackendAttr, DiplomatBackendAttrCfg}; + use super::{DiplomatBackendAttr, DiplomatBackendAttrCfg, RenameAttr}; #[test] fn test_cfgs() { @@ -214,4 +347,14 @@ mod tests { let attr: DiplomatBackendAttr = attr.parse_args().unwrap(); insta::assert_yaml_snapshot!(attr); } + + #[test] + fn test_rename() { + let attr: syn::Attribute = syn::parse_quote!(#[diplomat::c_rename = "foobar_{0}"]); + let attr = RenameAttr::from_syn(&attr).unwrap(); + insta::assert_yaml_snapshot!(attr); + let attr: syn::Attribute = syn::parse_quote!(#[diplomat::c_rename("foobar_{0}")]); + let attr = RenameAttr::from_syn(&attr).unwrap(); + insta::assert_yaml_snapshot!(attr); + } } diff --git a/core/src/ast/enums.rs b/core/src/ast/enums.rs index 20f012fa4..29e727439 100644 --- a/core/src/ast/enums.rs +++ b/core/src/ast/enums.rs @@ -16,9 +16,9 @@ pub struct Enum { pub attrs: Attrs, } -impl From<&syn::ItemEnum> for Enum { +impl Enum { /// Extract an [`Enum`] metadata value from an AST node. - fn from(enm: &syn::ItemEnum) -> Enum { + pub fn new(enm: &syn::ItemEnum, parent_attrs: &Attrs) -> Enum { let mut last_discriminant = -1; if !enm.generics.params.is_empty() { // Generic types are not allowed. @@ -28,6 +28,9 @@ impl From<&syn::ItemEnum> for Enum { panic!("Enums cannot have generic parameters"); } + let mut attrs: Attrs = (&*enm.attrs).into(); + attrs.merge_parent_attrs(parent_attrs); + Enum { name: (&enm.ident).into(), docs: Docs::from_attrs(&enm.attrs), @@ -61,7 +64,7 @@ impl From<&syn::ItemEnum> for Enum { }) .collect(), methods: vec![], - attrs: (&*enm.attrs).into(), + attrs, } } } @@ -80,15 +83,18 @@ mod tests { settings.set_sort_maps(true); settings.bind(|| { - insta::assert_yaml_snapshot!(Enum::from(&syn::parse_quote! { - /// Some docs. - #[diplomat::rust_link(foo::Bar, Enum)] - enum MyLocalEnum { - Abc, - /// Some more docs. - Def - } - })); + insta::assert_yaml_snapshot!(Enum::new( + &syn::parse_quote! { + /// Some docs. + #[diplomat::rust_link(foo::Bar, Enum)] + enum MyLocalEnum { + Abc, + /// Some more docs. + Def + } + }, + &Default::default() + )); }); } @@ -98,16 +104,19 @@ mod tests { settings.set_sort_maps(true); settings.bind(|| { - insta::assert_yaml_snapshot!(Enum::from(&syn::parse_quote! { - /// Some docs. - #[diplomat::rust_link(foo::Bar, Enum)] - enum DiscriminantedEnum { - Abc = -1, - Def = 0, - Ghi = 1, - Jkl = 2, - } - })); + insta::assert_yaml_snapshot!(Enum::new( + &syn::parse_quote! { + /// Some docs. + #[diplomat::rust_link(foo::Bar, Enum)] + enum DiscriminantedEnum { + Abc = -1, + Def = 0, + Ghi = 1, + Jkl = 2, + } + }, + &Default::default() + )); }); } } diff --git a/core/src/ast/methods.rs b/core/src/ast/methods.rs index 1ae6a7740..7e8619c76 100644 --- a/core/src/ast/methods.rs +++ b/core/src/ast/methods.rs @@ -49,10 +49,14 @@ impl Method { impl_generics: Option<&syn::Generics>, impl_attrs: &Attrs, ) -> Method { + let mut attrs: Attrs = (&*m.attrs).into(); + attrs.merge_parent_attrs(impl_attrs); + let self_ident = self_path_type.path.elements.last().unwrap(); let method_ident = &m.sig.ident; + let concat_method_ident = format!("{self_ident}_{method_ident}"); let extern_ident = syn::Ident::new( - format!("{self_ident}_{method_ident}").as_str(), + &attrs.c_rename.apply(&concat_method_ident), m.sig.ident.span(), ); @@ -91,9 +95,6 @@ impl Method { return_ty.as_ref(), ); - let mut attrs: Attrs = (&*m.attrs).into(); - attrs.merge_parent_attrs(impl_attrs); - Method { name: Ident::from(method_ident), docs: Docs::from_attrs(&m.attrs), diff --git a/core/src/ast/modules.rs b/core/src/ast/modules.rs index db4ccf37b..d81db1b45 100644 --- a/core/src/ast/modules.rs +++ b/core/src/ast/modules.rs @@ -66,6 +66,7 @@ pub struct Module { pub imports: Vec<(Path, Ident)>, pub declared_types: BTreeMap, pub sub_modules: Vec, + pub attrs: Attrs, } impl Module { @@ -128,6 +129,8 @@ impl Module { .iter() .any(|a| a.path().to_token_stream().to_string() == "diplomat :: bridge"); + let mod_attrs: Attrs = (&*input.attrs).into(); + input .content .as_ref() @@ -143,15 +146,15 @@ impl Module { Item::Struct(strct) => { if analyze_types { let custom_type = match DiplomatStructAttribute::parse(&strct.attrs[..]) { - Ok(None) => CustomType::Struct(Struct::new(strct, false)), + Ok(None) => CustomType::Struct(Struct::new(strct, false, &mod_attrs)), Ok(Some(DiplomatStructAttribute::Out)) => { - CustomType::Struct(Struct::new(strct, true)) + CustomType::Struct(Struct::new(strct, true, &mod_attrs)) } Ok(Some(DiplomatStructAttribute::Opaque)) => { - CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Immutable)) + CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Immutable, &mod_attrs)) } Ok(Some(DiplomatStructAttribute::OpaqueMut)) => { - CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Mutable)) + CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Mutable, &mod_attrs)) } Err(errors) => { panic!("Multiple conflicting Diplomat struct attributes, there can be at most one: {errors:?}"); @@ -164,8 +167,11 @@ impl Module { Item::Enum(enm) => { if analyze_types { + let ident = (&enm.ident).into(); + let mut enm = Enum::new(enm, &mod_attrs); + enm.attrs.merge_parent_attrs(&mod_attrs); custom_types_by_name - .insert((&enm.ident).into(), CustomType::Enum(Enum::from(enm))); + .insert(ident, CustomType::Enum(enm)); } } @@ -177,8 +183,8 @@ impl Module { syn::Type::Path(s) => PathType::from(s), _ => panic!("Self type not found"), }; - let attrs = Attrs::from(&*imp.attrs); - + let mut impl_attrs = Attrs::from(&*imp.attrs); + impl_attrs.merge_parent_attrs(&mod_attrs); let mut new_methods = imp .items .iter() @@ -187,7 +193,7 @@ impl Module { _ => None, }) .filter(|m| matches!(m.vis, Visibility::Public(_))) - .map(|m| Method::from_syn(m, self_path.clone(), Some(&imp.generics), &attrs)) + .map(|m| Method::from_syn(m, self_path.clone(), Some(&imp.generics), &impl_attrs)) .collect(); let self_ident = self_path.path.elements.last().unwrap(); @@ -216,6 +222,7 @@ impl Module { imports, declared_types: custom_types_by_name, sub_modules, + attrs: mod_attrs, } } } diff --git a/core/src/ast/snapshots/diplomat_core__ast__attrs__tests__rename-2.snap b/core/src/ast/snapshots/diplomat_core__ast__attrs__tests__rename-2.snap new file mode 100644 index 000000000..2bb669327 --- /dev/null +++ b/core/src/ast/snapshots/diplomat_core__ast__attrs__tests__rename-2.snap @@ -0,0 +1,8 @@ +--- +source: core/src/ast/attrs.rs +expression: attr +--- +pattern: + replacement: foobar_ + insertion_index: 7 + diff --git a/core/src/ast/snapshots/diplomat_core__ast__attrs__tests__rename.snap b/core/src/ast/snapshots/diplomat_core__ast__attrs__tests__rename.snap new file mode 100644 index 000000000..2bb669327 --- /dev/null +++ b/core/src/ast/snapshots/diplomat_core__ast__attrs__tests__rename.snap @@ -0,0 +1,8 @@ +--- +source: core/src/ast/attrs.rs +expression: attr +--- +pattern: + replacement: foobar_ + insertion_index: 7 + diff --git a/core/src/ast/snapshots/diplomat_core__ast__enums__tests__enum_with_discr.snap b/core/src/ast/snapshots/diplomat_core__ast__enums__tests__enum_with_discr.snap index 18b94c4fb..56d6eafaf 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__enums__tests__enum_with_discr.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__enums__tests__enum_with_discr.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/enums.rs -expression: "Enum::from(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Enum)] enum\n DiscriminantedEnum { Abc = - 1, Def = 0, Ghi = 1, Jkl = 2, }\n })" +expression: "Enum::new(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Enum)] enum\n DiscriminantedEnum { Abc = - 1, Def = 0, Ghi = 1, Jkl = 2, }\n }, &Default::default())" --- name: DiscriminantedEnum docs: @@ -16,23 +16,22 @@ variants: - -1 - - "" - [] - - cfg: [] + - {} - - Def - 0 - - "" - [] - - cfg: [] + - {} - - Ghi - 1 - - "" - [] - - cfg: [] + - {} - - Jkl - 2 - - "" - [] - - cfg: [] + - {} methods: [] -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap b/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap index 4a551dc56..3c509d534 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/enums.rs -expression: "Enum::from(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Enum)] enum MyLocalEnum\n {\n Abc, /// Some more docs.\n Def\n }\n })" +expression: "Enum::new(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Enum)] enum MyLocalEnum\n {\n Abc, /// Some more docs.\n Def\n }\n }, &Default::default())" --- name: MyLocalEnum docs: @@ -16,13 +16,12 @@ variants: - 0 - - "" - [] - - cfg: [] + - {} - - Def - 1 - - Some more docs. - [] - - cfg: [] + - {} methods: [] -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap index 0f39f8d84..3070658f7 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap @@ -36,6 +36,5 @@ params: return_type: Primitive: u64 lifetime_env: {} -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap index 7c2f40581..29371208c 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap @@ -29,6 +29,5 @@ params: lifetimes: [] return_type: ~ lifetime_env: {} -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap index c20bd6a0d..c024cb0e0 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap @@ -28,6 +28,5 @@ params: return_type: Primitive: u64 lifetime_env: {} -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap index 389135057..61f428d0a 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap @@ -27,6 +27,5 @@ params: lifetimes: [] return_type: ~ lifetime_env: {} -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__import_in_non_diplomat_not_analyzed.snap b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__import_in_non_diplomat_not_analyzed.snap index 3e38f88a8..35ee9b08a 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__import_in_non_diplomat_not_analyzed.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__import_in_non_diplomat_not_analyzed.snap @@ -17,12 +17,13 @@ modules: fields: [] methods: [] output_only: false - attrs: - cfg: [] + attrs: {} sub_modules: [] + attrs: {} other: name: other imports: [] declared_types: {} sub_modules: [] + attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__method_visibility.snap b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__method_visibility.snap index 179444b35..951f7b21c 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__method_visibility.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__method_visibility.snap @@ -23,10 +23,9 @@ declared_types: params: [] return_type: ~ lifetime_env: {} - attrs: - cfg: [] + attrs: {} output_only: false - attrs: - cfg: [] + attrs: {} sub_modules: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap index 9e853a747..71d09869b 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap @@ -44,8 +44,7 @@ declared_types: - NonOpaqueStruct lifetimes: [] lifetime_env: {} - attrs: - cfg: [] + attrs: {} - name: set_a docs: - "" @@ -66,11 +65,9 @@ declared_types: Primitive: i32 return_type: ~ lifetime_env: {} - attrs: - cfg: [] + attrs: {} output_only: false - attrs: - cfg: [] + attrs: {} OpaqueStruct: Opaque: name: OpaqueStruct @@ -94,8 +91,7 @@ declared_types: - OpaqueStruct lifetimes: [] lifetime_env: {} - attrs: - cfg: [] + attrs: {} - name: get_string docs: - "" @@ -118,10 +114,9 @@ declared_types: - String lifetimes: [] lifetime_env: {} - attrs: - cfg: [] + attrs: {} mutability: Immutable - attrs: - cfg: [] + attrs: {} sub_modules: [] +attrs: {} diff --git a/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap b/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap index e99b309c1..e01f8c6b1 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/structs.rs -expression: "Struct::new(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Struct)] struct\n MyLocalStruct { a : i32, b : Box < MyLocalStruct > }\n }, true)" +expression: "Struct::new(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Struct)] struct\n MyLocalStruct { a : i32, b : Box < MyLocalStruct > }\n }, true, &Default::default())" --- name: MyLocalStruct docs: @@ -28,6 +28,5 @@ fields: - [] methods: [] output_only: true -attrs: - cfg: [] +attrs: {} diff --git a/core/src/ast/structs.rs b/core/src/ast/structs.rs index 2fb1db483..b6e3a7173 100644 --- a/core/src/ast/structs.rs +++ b/core/src/ast/structs.rs @@ -18,7 +18,7 @@ pub struct Struct { impl Struct { /// Extract a [`Struct`] metadata value from an AST node. - pub fn new(strct: &syn::ItemStruct, output_only: bool) -> Self { + pub fn new(strct: &syn::ItemStruct, output_only: bool, parent_attrs: &Attrs) -> Self { let self_path_type = PathType::extract_self_type(strct); let fields: Vec<_> = strct .fields @@ -38,7 +38,8 @@ impl Struct { .collect(); let lifetimes = LifetimeEnv::from_struct_item(strct, &fields[..]); - + let mut attrs: Attrs = (&*strct.attrs).into(); + attrs.merge_parent_attrs(parent_attrs); Struct { name: (&strct.ident).into(), docs: Docs::from_attrs(&strct.attrs), @@ -46,7 +47,7 @@ impl Struct { fields, methods: vec![], output_only, - attrs: (&*strct.attrs).into(), + attrs, } } } @@ -67,14 +68,16 @@ pub struct OpaqueStruct { impl OpaqueStruct { /// Extract a [`OpaqueStruct`] metadata value from an AST node. - pub fn new(strct: &syn::ItemStruct, mutability: Mutability) -> Self { + pub fn new(strct: &syn::ItemStruct, mutability: Mutability, parent_attrs: &Attrs) -> Self { + let mut attrs: Attrs = (&*strct.attrs).into(); + attrs.merge_parent_attrs(parent_attrs); OpaqueStruct { name: Ident::from(&strct.ident), docs: Docs::from_attrs(&strct.attrs), lifetimes: LifetimeEnv::from_struct_item(strct, &[]), methods: vec![], mutability, - attrs: (&*strct.attrs).into(), + attrs, } } } @@ -102,7 +105,8 @@ mod tests { b: Box } }, - true + true, + &Default::default() )); }); } diff --git a/core/src/hir/attrs.rs b/core/src/hir/attrs.rs index 6a55f52e2..2758d0a14 100644 --- a/core/src/hir/attrs.rs +++ b/core/src/hir/attrs.rs @@ -7,11 +7,14 @@ use crate::hir::LoweringError; use quote::ToTokens; use syn::{LitStr, Meta}; +pub use crate::ast::attrs::RenameAttr; + #[non_exhaustive] #[derive(Clone, Default, Debug)] pub struct Attrs { pub disable: bool, pub rename: Option, + pub c_rename: RenameAttr, // more to be added: rename, namespace, etc } @@ -34,7 +37,12 @@ impl Attrs { context: AttributeContext, errors: &mut Vec, ) -> Self { - let mut this = Attrs::default(); + let mut this = Attrs { + // Backends must support this since it applies to the macro/C code. + c_rename: ast.c_rename.clone(), + ..Default::default() + }; + let support = validator.attrs_supported(); for attr in &ast.attrs { if validator.satisfies_cfg(&attr.cfg) { @@ -110,7 +118,7 @@ impl Attrs { pub struct BackendAttrSupport { pub disabling: bool, pub renaming: bool, - // more to be added: rename, namespace, etc + // more to be added: namespace, etc } /// Defined by backends when validating attributes diff --git a/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap b/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap index 9a1272ce0..c3ee88d34 100644 --- a/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap +++ b/core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap @@ -93,12 +93,18 @@ TypeContext { attrs: Attrs { disable: false, rename: None, + c_rename: RenameAttr { + pattern: None, + }, }, }, ], attrs: Attrs { disable: false, rename: None, + c_rename: RenameAttr { + pattern: None, + }, }, lifetimes: LifetimeEnv { nodes: [ @@ -201,12 +207,18 @@ TypeContext { attrs: Attrs { disable: false, rename: None, + c_rename: RenameAttr { + pattern: None, + }, }, }, ], attrs: Attrs { disable: false, rename: None, + c_rename: RenameAttr { + pattern: None, + }, }, lifetimes: LifetimeEnv { nodes: [ @@ -231,6 +243,9 @@ TypeContext { attrs: Attrs { disable: false, rename: None, + c_rename: RenameAttr { + pattern: None, + }, }, lifetimes: LifetimeEnv { nodes: [ diff --git a/example/cpp/Makefile b/example/cpp/Makefile index 2e6b9746d..be6221984 100644 --- a/example/cpp/Makefile +++ b/example/cpp/Makefile @@ -4,7 +4,7 @@ ALL_HEADERS := $(wildcard *.h) $(wildcard *.hpp) $(wildcard tests/*.hpp) ALL_RUST := $(wildcard ../src/*.rs) -CXX=g++-10 +CXX=g++-13 FORCE: diff --git a/feature_tests/c/include/AttrOpaque1.h b/feature_tests/c/include/AttrOpaque1.h index ab5a85309..fc8e1d82f 100644 --- a/feature_tests/c/include/AttrOpaque1.h +++ b/feature_tests/c/include/AttrOpaque1.h @@ -19,9 +19,13 @@ namespace capi { extern "C" { #endif -void AttrOpaque1_method(const AttrOpaque1* self); +AttrOpaque1* namespace_AttrOpaque1_new(); -void AttrOpaque1_method_disabledcpp(const AttrOpaque1* self); +uint8_t namespace_AttrOpaque1_method(const AttrOpaque1* self); + +uint8_t renamed_in_c_only(const AttrOpaque1* self); + +void namespace_AttrOpaque1_method_disabledcpp(const AttrOpaque1* self); void AttrOpaque1_destroy(AttrOpaque1* self); #ifdef __cplusplus diff --git a/feature_tests/c2/include/AttrOpaque1.h b/feature_tests/c2/include/AttrOpaque1.h index 3108cc911..65a533ed2 100644 --- a/feature_tests/c2/include/AttrOpaque1.h +++ b/feature_tests/c2/include/AttrOpaque1.h @@ -15,9 +15,13 @@ extern "C" { #endif // __cplusplus -void AttrOpaque1_method(const AttrOpaque1* self); +AttrOpaque1* namespace_AttrOpaque1_new(); -void AttrOpaque1_method_disabledcpp(const AttrOpaque1* self); +uint8_t namespace_AttrOpaque1_method(const AttrOpaque1* self); + +uint8_t renamed_in_c_only(const AttrOpaque1* self); + +void namespace_AttrOpaque1_method_disabledcpp(const AttrOpaque1* self); void AttrOpaque1_destroy(AttrOpaque1* self); diff --git a/feature_tests/cpp/Makefile b/feature_tests/cpp/Makefile index 958164800..f0baa9905 100644 --- a/feature_tests/cpp/Makefile +++ b/feature_tests/cpp/Makefile @@ -4,7 +4,7 @@ ALL_HEADERS := $(wildcard *.h) $(wildcard *.hpp) $(wildcard tests/*.hpp) ALL_RUST := $(wildcard ../src/*.rs) -CXX=g++-10 +CXX=g++-13 FORCE: @@ -22,7 +22,12 @@ $(ALL_HEADERS): ./tests/option.out: ../../target/debug/libdiplomat_feature_tests.a $(ALL_HEADERS) ./tests/option.cpp $(CXX) -std=c++17 ./tests/option.cpp ../../target/debug/libdiplomat_feature_tests.a -ldl -lpthread -lm -g -o ./tests/option.out -test: ./tests/structs.out ./tests/result.out ./tests/option.out +./tests/attrs.out: ../../target/debug/libdiplomat_feature_tests.a $(ALL_HEADERS) ./tests/attrs.cpp + $(CXX) -std=c++17 ./tests/attrs.cpp ../../target/debug/libdiplomat_feature_tests.a -ldl -lpthread -lm -g -o ./tests/attrs.out + + +test: ./tests/structs.out ./tests/result.out ./tests/option.out ./tests/attrs.out ./tests/structs.out ./tests/result.out ./tests/option.out + ./tests/attrs.out diff --git a/feature_tests/cpp/docs/source/attrs_ffi.rst b/feature_tests/cpp/docs/source/attrs_ffi.rst index d2ff116ac..4c6194a39 100644 --- a/feature_tests/cpp/docs/source/attrs_ffi.rst +++ b/feature_tests/cpp/docs/source/attrs_ffi.rst @@ -11,7 +11,13 @@ .. cpp:class:: AttrOpaque1 - .. cpp:function:: void method() const + .. cpp:function:: static AttrOpaque1 new_() + + + .. cpp:function:: uint8_t method() const + + + .. cpp:function:: uint8_t crenamed() const .. cpp:function:: void method_disabledcpp() const diff --git a/feature_tests/cpp/include/AttrOpaque1.h b/feature_tests/cpp/include/AttrOpaque1.h index ab5a85309..fc8e1d82f 100644 --- a/feature_tests/cpp/include/AttrOpaque1.h +++ b/feature_tests/cpp/include/AttrOpaque1.h @@ -19,9 +19,13 @@ namespace capi { extern "C" { #endif -void AttrOpaque1_method(const AttrOpaque1* self); +AttrOpaque1* namespace_AttrOpaque1_new(); -void AttrOpaque1_method_disabledcpp(const AttrOpaque1* self); +uint8_t namespace_AttrOpaque1_method(const AttrOpaque1* self); + +uint8_t renamed_in_c_only(const AttrOpaque1* self); + +void namespace_AttrOpaque1_method_disabledcpp(const AttrOpaque1* self); void AttrOpaque1_destroy(AttrOpaque1* self); #ifdef __cplusplus diff --git a/feature_tests/cpp/include/AttrOpaque1.hpp b/feature_tests/cpp/include/AttrOpaque1.hpp index 4140aea2a..df355af9c 100644 --- a/feature_tests/cpp/include/AttrOpaque1.hpp +++ b/feature_tests/cpp/include/AttrOpaque1.hpp @@ -11,6 +11,7 @@ #include "AttrOpaque1.h" +class AttrOpaque1; /** * A destruction policy for using AttrOpaque1 with std::unique_ptr. @@ -22,7 +23,9 @@ struct AttrOpaque1Deleter { }; class AttrOpaque1 { public: - void method() const; + static AttrOpaque1 new_(); + uint8_t method() const; + uint8_t crenamed() const; void method_disabledcpp() const; inline const capi::AttrOpaque1* AsFFI() const { return this->inner.get(); } inline capi::AttrOpaque1* AsFFIMut() { return this->inner.get(); } @@ -35,10 +38,16 @@ class AttrOpaque1 { }; -inline void AttrOpaque1::method() const { - capi::AttrOpaque1_method(this->inner.get()); +inline AttrOpaque1 AttrOpaque1::new_() { + return AttrOpaque1(capi::namespace_AttrOpaque1_new()); +} +inline uint8_t AttrOpaque1::method() const { + return capi::namespace_AttrOpaque1_method(this->inner.get()); +} +inline uint8_t AttrOpaque1::crenamed() const { + return capi::renamed_in_c_only(this->inner.get()); } inline void AttrOpaque1::method_disabledcpp() const { - capi::AttrOpaque1_method_disabledcpp(this->inner.get()); + capi::namespace_AttrOpaque1_method_disabledcpp(this->inner.get()); } #endif diff --git a/feature_tests/cpp/tests/attrs.cpp b/feature_tests/cpp/tests/attrs.cpp new file mode 100644 index 000000000..024b9e983 --- /dev/null +++ b/feature_tests/cpp/tests/attrs.cpp @@ -0,0 +1,16 @@ +#include +#include "../include/AttrOpaque1.hpp" +#include "assert.hpp" + +int main(int argc, char *argv[]) { + AttrOpaque1 o = AttrOpaque1::new_(); + // the cpp2 renames don't apply. However, these must link correctly!! + simple_assert_eq("method should call", o.method(), 77); + simple_assert_eq("method should call", o.crenamed(), 123); + + // These C names should also resolve + void* renamed = (void*)capi::renamed_in_c_only; + std::cout<<"Renamed function at "< +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif // __cplusplus + + +typedef struct AttrOpaque1 AttrOpaque1; + + +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif // __cplusplus + +#endif // AttrOpaque1_D_H diff --git a/feature_tests/cpp2/include/AttrOpaque1.h b/feature_tests/cpp2/include/AttrOpaque1.h new file mode 100644 index 000000000..2a4a72d50 --- /dev/null +++ b/feature_tests/cpp2/include/AttrOpaque1.h @@ -0,0 +1,32 @@ +#ifndef AttrOpaque1_H +#define AttrOpaque1_H + +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#include "AttrOpaque1.d.h" + +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif // __cplusplus + + +AttrOpaque1* namespace_AttrOpaque1_new(); + +uint8_t namespace_AttrOpaque1_method(const AttrOpaque1* self); + +uint8_t renamed_in_c_only(const AttrOpaque1* self); + +void AttrOpaque1_destroy(AttrOpaque1* self); + + +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif // __cplusplus + +#endif // AttrOpaque1_H diff --git a/feature_tests/cpp2/include/AttrOpaque1Renamed.d.hpp b/feature_tests/cpp2/include/AttrOpaque1Renamed.d.hpp index 1eac0c4a0..2ddfd0006 100644 --- a/feature_tests/cpp2/include/AttrOpaque1Renamed.d.hpp +++ b/feature_tests/cpp2/include/AttrOpaque1Renamed.d.hpp @@ -14,12 +14,16 @@ class AttrOpaque1Renamed { public: - inline void method_renamed() const; + inline static std::unique_ptr new_(); - inline const capi::AttrOpaque1Renamed* AsFFI() const; - inline capi::AttrOpaque1Renamed* AsFFI(); - inline static const AttrOpaque1Renamed* FromFFI(const capi::AttrOpaque1Renamed* ptr); - inline static AttrOpaque1Renamed* FromFFI(capi::AttrOpaque1Renamed* ptr); + inline uint8_t method_renamed() const; + + inline uint8_t crenamed() const; + + inline const capi::AttrOpaque1* AsFFI() const; + inline capi::AttrOpaque1* AsFFI(); + inline static const AttrOpaque1Renamed* FromFFI(const capi::AttrOpaque1* ptr); + inline static AttrOpaque1Renamed* FromFFI(capi::AttrOpaque1* ptr); inline static void operator delete(void* ptr); private: AttrOpaque1Renamed() = delete; diff --git a/feature_tests/cpp2/include/AttrOpaque1Renamed.hpp b/feature_tests/cpp2/include/AttrOpaque1Renamed.hpp index 931c9c47d..e3d5f15b6 100644 --- a/feature_tests/cpp2/include/AttrOpaque1Renamed.hpp +++ b/feature_tests/cpp2/include/AttrOpaque1Renamed.hpp @@ -13,28 +13,39 @@ #include "AttrOpaque1.h" -inline void AttrOpaque1Renamed::method_renamed() const { - capi::AttrOpaque1_method(this->AsFFI()); +inline std::unique_ptr AttrOpaque1Renamed::new_() { + auto result = capi::namespace_AttrOpaque1_new(); + return std::unique_ptr(AttrOpaque1Renamed::FromFFI(result)); } -inline const capi::AttrOpaque1Renamed* AttrOpaque1Renamed::AsFFI() const { - return reinterpret_cast(this); +inline uint8_t AttrOpaque1Renamed::method_renamed() const { + auto result = capi::namespace_AttrOpaque1_method(this->AsFFI()); + return result; } -inline capi::AttrOpaque1Renamed* AttrOpaque1Renamed::AsFFI() { - return reinterpret_cast(this); +inline uint8_t AttrOpaque1Renamed::crenamed() const { + auto result = capi::renamed_in_c_only(this->AsFFI()); + return result; } -inline const AttrOpaque1Renamed* AttrOpaque1Renamed::FromFFI(const capi::AttrOpaque1Renamed* ptr) { +inline const capi::AttrOpaque1* AttrOpaque1Renamed::AsFFI() const { + return reinterpret_cast(this); +} + +inline capi::AttrOpaque1* AttrOpaque1Renamed::AsFFI() { + return reinterpret_cast(this); +} + +inline const AttrOpaque1Renamed* AttrOpaque1Renamed::FromFFI(const capi::AttrOpaque1* ptr) { return reinterpret_cast(ptr); } -inline AttrOpaque1Renamed* AttrOpaque1Renamed::FromFFI(capi::AttrOpaque1Renamed* ptr) { +inline AttrOpaque1Renamed* AttrOpaque1Renamed::FromFFI(capi::AttrOpaque1* ptr) { return reinterpret_cast(ptr); } inline void AttrOpaque1Renamed::operator delete(void* ptr) { - capi::AttrOpaque1Renamed_destroy(reinterpret_cast(ptr)); + capi::AttrOpaque1_destroy(reinterpret_cast(ptr)); } diff --git a/feature_tests/cpp2/tests/attrs.cpp b/feature_tests/cpp2/tests/attrs.cpp new file mode 100644 index 000000000..37de0809a --- /dev/null +++ b/feature_tests/cpp2/tests/attrs.cpp @@ -0,0 +1,15 @@ +#include +#include "../include/AttrOpaque1Renamed.hpp" +#include "assert.hpp" + +int main(int argc, char *argv[]) { + std::unique_ptr r = AttrOpaque1Renamed::new_(); + simple_assert_eq("method should call", r->method_renamed(), 77); + simple_assert_eq("method should call", r->crenamed(), 123); + + // These C names should also resolve + void* renamed = (void*)capi::renamed_in_c_only; + std::cout<<"Renamed function at "< self); -@ffi.Native)>(isLeaf: true, symbol: 'AttrOpaque1_method') +@ffi.Native Function()>(isLeaf: true, symbol: 'namespace_AttrOpaque1_new') +// ignore: non_constant_identifier_names +external ffi.Pointer _namespace_AttrOpaque1_new(); + +@ffi.Native)>(isLeaf: true, symbol: 'namespace_AttrOpaque1_method') +// ignore: non_constant_identifier_names +external int _namespace_AttrOpaque1_method(ffi.Pointer self); + +@ffi.Native)>(isLeaf: true, symbol: 'renamed_in_c_only') // ignore: non_constant_identifier_names -external void _AttrOpaque1_method(ffi.Pointer self); +external int _renamed_in_c_only(ffi.Pointer self); -@ffi.Native)>(isLeaf: true, symbol: 'AttrOpaque1_method_disabledcpp') +@ffi.Native)>(isLeaf: true, symbol: 'namespace_AttrOpaque1_method_disabledcpp') // ignore: non_constant_identifier_names -external void _AttrOpaque1_method_disabledcpp(ffi.Pointer self); +external void _namespace_AttrOpaque1_method_disabledcpp(ffi.Pointer self); diff --git a/feature_tests/dotnet/Lib/Generated/AttrOpaque1.cs b/feature_tests/dotnet/Lib/Generated/AttrOpaque1.cs index 4f672d05e..cd6c56c48 100644 --- a/feature_tests/dotnet/Lib/Generated/AttrOpaque1.cs +++ b/feature_tests/dotnet/Lib/Generated/AttrOpaque1.cs @@ -29,7 +29,32 @@ public unsafe AttrOpaque1(Raw.AttrOpaque1* handle) _inner = handle; } - public void Method() + /// + /// A AttrOpaque1 allocated on Rust side. + /// + public static AttrOpaque1 New() + { + unsafe + { + Raw.AttrOpaque1* retVal = Raw.AttrOpaque1.New(); + return new AttrOpaque1(retVal); + } + } + + public byte Method() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("AttrOpaque1"); + } + byte retVal = Raw.AttrOpaque1.Method(_inner); + return retVal; + } + } + + public byte Crenamed() { unsafe { @@ -37,7 +62,8 @@ public void Method() { throw new ObjectDisposedException("AttrOpaque1"); } - Raw.AttrOpaque1.Method(_inner); + byte retVal = Raw.AttrOpaque1.Crenamed(_inner); + return retVal; } } diff --git a/feature_tests/dotnet/Lib/Generated/RawAttrOpaque1.cs b/feature_tests/dotnet/Lib/Generated/RawAttrOpaque1.cs index 99e414f3b..fa47cb219 100644 --- a/feature_tests/dotnet/Lib/Generated/RawAttrOpaque1.cs +++ b/feature_tests/dotnet/Lib/Generated/RawAttrOpaque1.cs @@ -16,11 +16,17 @@ public partial struct AttrOpaque1 { private const string NativeLib = "diplomat_feature_tests"; - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "AttrOpaque1_method", ExactSpelling = true)] - public static unsafe extern void Method(AttrOpaque1* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "namespace_AttrOpaque1_new", ExactSpelling = true)] + public static unsafe extern AttrOpaque1* NamespaceNew(); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "AttrOpaque1_method_disabledcpp", ExactSpelling = true)] - public static unsafe extern void MethodDisabledcpp(AttrOpaque1* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "namespace_AttrOpaque1_method", ExactSpelling = true)] + public static unsafe extern byte NamespaceMethod(AttrOpaque1* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "renamed_in_c_only", ExactSpelling = true)] + public static unsafe extern byte RenamedInCOnly(AttrOpaque1* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "namespace_AttrOpaque1_method_disabledcpp", ExactSpelling = true)] + public static unsafe extern void NamespaceMethodDisabledcpp(AttrOpaque1* self); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "AttrOpaque1_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(AttrOpaque1* self); diff --git a/feature_tests/js/api/AttrOpaque1.d.ts b/feature_tests/js/api/AttrOpaque1.d.ts index cbe37a77c..caeff8f8b 100644 --- a/feature_tests/js/api/AttrOpaque1.d.ts +++ b/feature_tests/js/api/AttrOpaque1.d.ts @@ -1,3 +1,4 @@ +import { u8 } from "./diplomat-runtime" /** */ @@ -5,7 +6,15 @@ export class AttrOpaque1 { /** */ - method(): void; + static new(): AttrOpaque1; + + /** + */ + method(): u8; + + /** + */ + crenamed(): u8; /** */ diff --git a/feature_tests/js/api/AttrOpaque1.mjs b/feature_tests/js/api/AttrOpaque1.mjs index 5fa7b0402..8bfeb2324 100644 --- a/feature_tests/js/api/AttrOpaque1.mjs +++ b/feature_tests/js/api/AttrOpaque1.mjs @@ -15,11 +15,19 @@ export class AttrOpaque1 { } } + static new() { + return new AttrOpaque1(wasm.namespace_AttrOpaque1_new(), true, []); + } + method() { - wasm.AttrOpaque1_method(this.underlying); + return wasm.namespace_AttrOpaque1_method(this.underlying); + } + + crenamed() { + return wasm.renamed_in_c_only(this.underlying); } method_disabledcpp() { - wasm.AttrOpaque1_method_disabledcpp(this.underlying); + wasm.namespace_AttrOpaque1_method_disabledcpp(this.underlying); } } diff --git a/feature_tests/js/docs/source/attrs_ffi.rst b/feature_tests/js/docs/source/attrs_ffi.rst index e8c16cdf5..1d2066e61 100644 --- a/feature_tests/js/docs/source/attrs_ffi.rst +++ b/feature_tests/js/docs/source/attrs_ffi.rst @@ -5,8 +5,12 @@ .. js:class:: AttrOpaque1 + .. js:function:: new() + .. js:method:: method() + .. js:method:: crenamed() + .. js:method:: method_disabledcpp() .. js:class:: AttrOpaque2 diff --git a/feature_tests/src/attrs.rs b/feature_tests/src/attrs.rs index bd1d6da29..8feab53ad 100644 --- a/feature_tests/src/attrs.rs +++ b/feature_tests/src/attrs.rs @@ -1,13 +1,23 @@ #[diplomat::bridge] +#[diplomat::c_rename = "namespace_{0}"] pub mod ffi { #[diplomat::opaque] #[diplomat::attr(cpp2, rename = "AttrOpaque1Renamed")] pub struct AttrOpaque1; impl AttrOpaque1 { + pub fn new() -> Box { + Box::new(AttrOpaque1) + } + #[diplomat::attr(cpp2, rename = "method_renamed")] - pub fn method(&self) { - println!("method"); + pub fn method(&self) -> u8 { + 77 + } + + #[diplomat::c_rename("renamed_in_c_only")] + pub fn crenamed(&self) -> u8 { + 123 } #[diplomat::attr(cpp2, disable)] diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 1507c71fa..34c51646e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -277,6 +277,7 @@ impl AttributeInfo { || seg == "out" || seg == "attr" || seg == "skip_if_unsupported" + || seg == "c_rename" { // diplomat-tool reads these, not diplomat::bridge. // throw them away so rustc doesn't complain about unknown attributes @@ -303,8 +304,10 @@ impl AttributeInfo { } } -fn gen_bridge(input: ItemMod) -> ItemMod { +fn gen_bridge(mut input: ItemMod) -> ItemMod { let module = ast::Module::from_syn(&input, true); + // Clean out any diplomat attributes so Rust doesn't get mad + let _attrs = AttributeInfo::extract(&mut input.attrs); let (brace, mut new_contents) = input.content.unwrap(); new_contents.push(parse2(quote! { use diplomat_runtime::*; }).unwrap()); diff --git a/tool/src/c2/formatter.rs b/tool/src/c2/formatter.rs index 74c6a3e6a..fd530d4db 100644 --- a/tool/src/c2/formatter.rs +++ b/tool/src/c2/formatter.rs @@ -78,7 +78,8 @@ impl<'tcx> CFormatter<'tcx> { pub fn fmt_method_name(&self, ty: TypeId, method: &hir::Method) -> String { let ty_name = self.fmt_type_name(ty); let method_name = method.name.as_str(); - format!("{ty_name}_{method_name}") + let put_together = format!("{ty_name}_{method_name}"); + method.attrs.c_rename.apply(&put_together).into() } pub fn fmt_ptr<'a>(&self, ident: &'a str, mutability: hir::Mutability) -> Cow<'a, str> { diff --git a/tool/src/c2/ty.rs b/tool/src/c2/ty.rs index 66b4ac8ec..3e84d338c 100644 --- a/tool/src/c2/ty.rs +++ b/tool/src/c2/ty.rs @@ -31,8 +31,8 @@ impl<'tcx> super::CContext<'tcx> { } for method in ty.methods() { if method.attrs.disable { - // Skip type if disabled - return; + // Skip method if disabled + continue; } let _guard = self.errors.set_context_method( self.formatter.fmt_type_name_diagnostics(id), diff --git a/tool/src/common/mod.rs b/tool/src/common/mod.rs index 3bc5ff3b5..999e31978 100644 --- a/tool/src/common/mod.rs +++ b/tool/src/common/mod.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::fmt; /// This type abstracts over files being written to. -#[derive(Default)] +#[derive(Default, Debug)] pub struct FileMap { // The context types exist as a way to avoid passing around a billion different // parameters. However, passing them around as &mut self restricts the amount of diff --git a/tool/src/cpp2/formatter.rs b/tool/src/cpp2/formatter.rs index 792273ca8..fd92cae1a 100644 --- a/tool/src/cpp2/formatter.rs +++ b/tool/src/cpp2/formatter.rs @@ -80,8 +80,8 @@ impl<'tcx> Cpp2Formatter<'tcx> { ident.into() } - pub fn fmt_c_name<'a>(&self, ident: &'a str) -> Cow<'a, str> { - format!("capi::{ident}").into() + pub fn fmt_c_type_name<'a>(&self, id: TypeId) -> Cow<'a, str> { + format!("capi::{}", self.c.fmt_type_name(id)).into() } pub fn fmt_c_ptr<'a>(&self, ident: &'a str, mutability: hir::Mutability) -> Cow<'a, str> { diff --git a/tool/src/cpp2/ty.rs b/tool/src/cpp2/ty.rs index 3880f0020..2ec6ede4e 100644 --- a/tool/src/cpp2/ty.rs +++ b/tool/src/cpp2/ty.rs @@ -118,7 +118,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { /// cannot be added to it. pub fn gen_enum_def(&mut self, ty: &'tcx hir::EnumDef, id: TypeId) { let type_name = self.cx.formatter.fmt_type_name(id); - let ctype = self.cx.formatter.fmt_c_name(&type_name); + let ctype = self.cx.formatter.fmt_c_type_name(id); let methods = ty .methods @@ -173,7 +173,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { pub fn gen_opaque_def(&mut self, ty: &'tcx hir::OpaqueDef, id: TypeId) { let type_name = self.cx.formatter.fmt_type_name(id); - let ctype = self.cx.formatter.fmt_c_name(&type_name); + let ctype = self.cx.formatter.fmt_c_type_name(id); let methods = ty .methods @@ -228,7 +228,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { pub fn gen_struct_def(&mut self, def: &'tcx hir::StructDef

, id: TypeId) { let type_name = self.cx.formatter.fmt_type_name(id); - let ctype = self.cx.formatter.fmt_c_name(&type_name); + let ctype = self.cx.formatter.fmt_c_type_name(id); let field_decls = def .fields