Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cfg_attr-like attributes #2113

Merged
merged 5 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions fixtures/proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ publish = false
name = "uniffi_proc_macro"
crate-type = ["lib", "cdylib"]

[features]
default = ["myfeature"]
myfeature = []

[dependencies]
# Add the "scaffolding-ffi-buffer-fns" feature to make sure things can build correctly
uniffi = { workspace = true, features = ["scaffolding-ffi-buffer-fns"] }
Expand Down
4 changes: 2 additions & 2 deletions fixtures/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ impl TraitWithForeign for RustTraitImpl {
#[derive(uniffi::Object)]
pub struct Object;

#[uniffi::export]
#[cfg_attr(feature = "myfeature", uniffi::export)]
impl Object {
#[uniffi::constructor]
#[cfg_attr(feature = "myfeature", uniffi::constructor)]
fn new() -> Arc<Self> {
Arc::new(Self)
}
Expand Down
115 changes: 72 additions & 43 deletions uniffi_macros/src/export/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use quote::ToTokens;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
Attribute, Ident, LitStr, Meta, PathArguments, PathSegment, Token,
punctuated::Punctuated,
Attribute, Ident, LitStr, Meta, Path, PathArguments, PathSegment, Token,
};
use uniffi_meta::UniffiTraitDiscriminants;

Expand Down Expand Up @@ -249,57 +250,85 @@ impl ExportedImplFnAttributes {
pub fn new(attrs: &[Attribute]) -> syn::Result<Self> {
let mut this = Self::default();
for attr in attrs {
let segs = &attr.path().segments;

let fst = segs
.first()
.expect("attributes have at least one path segment");
if fst.ident != "uniffi" {
continue;
}
ensure_no_path_args(fst)?;

let args = match &attr.meta {
Meta::List(_) => attr.parse_args::<ExportFnArgs>()?,
_ => Default::default(),
};
this.args = args;

if segs.len() != 2 {
return Err(syn::Error::new_spanned(
segs,
"unsupported uniffi attribute",
));
}
let snd = &segs[1];
ensure_no_path_args(snd)?;

match snd.ident.to_string().as_str() {
"constructor" => {
if this.constructor {
return Err(syn::Error::new_spanned(
attr,
"duplicate constructor attribute",
));
let path = attr.path();

if is_uniffi_path(path) {
this.process_path(path, attr, &attr.meta)?;
} else if matches!(attr.meta, Meta::List(_)) {
saks marked this conversation as resolved.
Show resolved Hide resolved
if let Ok(nested) =
attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
{
for meta in &nested {
if let Meta::Path(path) = meta {
this.process_path(path, attr, meta)?
}
}
this.constructor = true;
};
}
}

Ok(this)
}

fn process_path(&mut self, path: &Path, attr: &Attribute, meta: &Meta) -> syn::Result<()> {
let segs = &path.segments;

let fst = segs
.first()
.expect("attributes have at least one path segment");

if fst.ident != "uniffi" {
return Ok(());
}
ensure_no_path_args(fst)?;

let args = match meta {
Meta::List(_) => attr.parse_args::<ExportFnArgs>()?,
_ => Default::default(),
};
self.args = args;

if segs.len() != 2 {
return Err(syn::Error::new_spanned(
segs,
"unsupported uniffi attribute",
));
}
let snd = &segs[1];
ensure_no_path_args(snd)?;

match snd.ident.to_string().as_str() {
"constructor" => {
if self.constructor {
return Err(syn::Error::new_spanned(
attr,
"duplicate constructor attribute",
));
}
"method" => {
if this.constructor {
return Err(syn::Error::new_spanned(
attr,
"confused constructor/method attributes",
));
}
self.constructor = true;
}
"method" => {
if self.constructor {
return Err(syn::Error::new_spanned(
attr,
"confused constructor/method attributes",
));
}
_ => return Err(syn::Error::new_spanned(snd, "unknown uniffi attribute")),
}
_ => return Err(syn::Error::new_spanned(snd, "unknown uniffi attribute")),
}

Ok(this)
Ok(())
}
}

fn is_uniffi_path(path: &Path) -> bool {
path.segments
.first()
.map(|segment| segment.ident == "uniffi")
.unwrap_or(false)
}

fn ensure_no_path_args(seg: &PathSegment) -> syn::Result<()> {
if matches!(seg.arguments, PathArguments::None) {
Ok(())
Expand Down