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

Adds ability to use provided ident verbatim for export #13

Merged
merged 5 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
95 changes: 76 additions & 19 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,9 @@ impl ProcMacro {
/// Constructs a [`ProcMacro`] from anything compatible with [`TokenStream2`].
pub fn from<T: Into<TokenStream2>>(tokens: T) -> Result<Self> {
let proc_fn = parse2::<ItemFn>(tokens.into())?;
let Visibility::Public(_) = proc_fn.vis else { return Err(Error::new(proc_fn.vis.span(), "Visibility must be public")) };
let Visibility::Public(_) = proc_fn.vis else {
return Err(Error::new(proc_fn.vis.span(), "Visibility must be public"));
};
let mut macro_type: Option<ProcMacroType> = None;
if proc_fn
.attrs
Expand Down Expand Up @@ -431,8 +433,9 @@ pub fn export_tokens_macro_ident(ident: &Ident) -> Ident {
/// on the item at that path, the returned macro path will be invalid.
pub fn export_tokens_macro_path(item_path: &Path) -> Path {
let mut macro_path = item_path.clone();
let Some(last_seg) = macro_path.segments.pop()
else { unreachable!("must have at least one segment") };
let Some(last_seg) = macro_path.segments.pop() else {
unreachable!("must have at least one segment")
};
let last_seg = export_tokens_macro_ident(&last_seg.into_value().ident);
macro_path.segments.push(last_seg.into());
macro_path
Expand All @@ -456,10 +459,14 @@ fn new_unique_export_tokens_ident(ident: &Ident) -> Ident {
/// all require `attr` to be specified.
///
/// An empty [`TokenStream2`] is sufficient for opting out of using `attr`
///
/// The `hide_exported_ident` variable specifies whether the macro uses an auto-generated name
/// via [`export_tokens_macro_ident`] or the name of the item itself.
pub fn export_tokens_internal<T: Into<TokenStream2>, E: Into<TokenStream2>>(
attr: T,
tokens: E,
emit: bool,
hide_exported_ident: bool,
) -> Result<TokenStream2> {
let attr = attr.into();
let item: Item = parse2(tokens.into())?;
Expand Down Expand Up @@ -493,7 +500,11 @@ pub fn export_tokens_internal<T: Into<TokenStream2>, E: Into<TokenStream2>>(
None => parse2::<Ident>(attr)?,
};
let macro_ident = new_unique_export_tokens_ident(&ident);
let ident = export_tokens_macro_ident(&ident);
let ident = if hide_exported_ident {
export_tokens_macro_ident(&ident)
} else {
ident
};
let item_emit = match emit {
true => quote! {
#[allow(unused)]
Expand Down Expand Up @@ -536,13 +547,14 @@ pub fn export_tokens_internal<T: Into<TokenStream2>, E: Into<TokenStream2>>(
pub fn export_tokens_alias_internal<T: Into<TokenStream2>>(
tokens: T,
emit: bool,
hide_exported_ident: bool,
) -> Result<TokenStream2> {
let alias = parse2::<Ident>(tokens.into())?;
let export_tokens_internal_path = macro_magic_path(&quote!(mm_core::export_tokens_internal));
Ok(quote! {
#[proc_macro_attribute]
pub fn #alias(attr: proc_macro::TokenStream, tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
match #export_tokens_internal_path(attr, tokens, #emit) {
match #export_tokens_internal_path(attr, tokens, #emit, #hide_exported_ident) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand Down Expand Up @@ -603,13 +615,20 @@ pub fn import_tokens_inner_internal<T: Into<TokenStream2>>(tokens: T) -> Result<
/// The internal implementation for the `forward_tokens` macro.
///
/// You shouldn't need to call this in any circumstances but it is provided just in case.
pub fn forward_tokens_internal<T: Into<TokenStream2>>(tokens: T) -> Result<TokenStream2> {
pub fn forward_tokens_internal<T: Into<TokenStream2>>(
tokens: T,
hidden_source_path: bool,
) -> Result<TokenStream2> {
let args = parse2::<ForwardTokensArgs>(tokens.into())?;
let mm_path = match args.mm_path {
Some(path) => path,
None => macro_magic_root(),
};
let source_path = export_tokens_macro_path(&args.source);
let source_path = if hidden_source_path {
export_tokens_macro_path(&args.source)
} else {
args.source
};
let target_path = args.target;
if let Some(extra) = args.extra {
Ok(quote! {
Expand Down Expand Up @@ -775,6 +794,7 @@ impl ToTokens for OverridePath {
pub fn import_tokens_attr_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2>>(
attr: T1,
tokens: T2,
hidden_source_path: bool,
) -> Result<TokenStream2> {
let attr = attr.into();
let mm_override_path = parse2::<OverridePath>(attr)?;
Expand Down Expand Up @@ -871,18 +891,33 @@ pub fn import_tokens_attr_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2
Ok(res) => res,
Err(err) => return err.to_compile_error().into()
};
quote::quote! {
#pound resolved_mm_override_path::forward_tokens! {
#pound path,
#orig_sig_ident,
#pound resolved_mm_override_path,
{
{ #pound attached_item },
{ #pound path },
{ #pound custom_parsed }
if #hidden_source_path {
quote::quote! {
#pound resolved_mm_override_path::forward_tokens! {
#pound path,
#orig_sig_ident,
#pound resolved_mm_override_path,
{
{ #pound attached_item },
{ #pound path },
{ #pound custom_parsed }
}
}
}
}.into()
}.into()
} else {
quote::quote! {
#pound resolved_mm_override_path::forward_tokens_verbatim! {
#pound path,
#orig_sig_ident,
#pound resolved_mm_override_path,
{
{ #pound attached_item },
{ #pound path },
{ #pound custom_parsed }
}
}
}.into()
}
}
}
};
Expand Down Expand Up @@ -981,7 +1016,8 @@ mod tests {
#[test]
fn export_tokens_internal_missing_ident() {
assert!(
export_tokens_internal(quote!(), quote!(impl MyTrait for Something), true).is_err()
export_tokens_internal(quote!(), quote!(impl MyTrait for Something), true, true)
.is_err()
);
}

Expand All @@ -992,6 +1028,7 @@ mod tests {
quote!(
struct MyStruct {}
),
true,
true
)
.unwrap()
Expand All @@ -1007,6 +1044,7 @@ mod tests {
struct Something {}
),
true,
true
)
.unwrap()
.to_string()
Expand All @@ -1021,6 +1059,7 @@ mod tests {
struct MyStruct<T> {}
),
true,
true
)
.unwrap()
.to_string()
Expand All @@ -1035,6 +1074,7 @@ mod tests {
struct MyStruct {}
),
true,
true
)
.is_err());
assert!(export_tokens_internal(
Expand All @@ -1043,6 +1083,7 @@ mod tests {
struct MyStruct {}
),
true,
true
)
.is_err());
}
Expand All @@ -1055,12 +1096,28 @@ mod tests {
struct Something {}
),
false,
true
)
.unwrap()
.to_string()
.contains("some_name"));
}

#[test]
fn export_tokens_internal_verbatim_ident() {
assert!(export_tokens_internal(
quote!(),
quote!(
struct MyStruct<T> {}
),
true,
false
)
.unwrap()
.to_string()
.contains("MyStruct"));
}

#[test]
fn import_tokens_internal_simple_path() {
assert!(
Expand Down
28 changes: 22 additions & 6 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ use proc_macro::TokenStream;
/// private/inaccessible contexts, however this was removed in 0.4.x.
#[proc_macro_attribute]
pub fn export_tokens(attr: TokenStream, tokens: TokenStream) -> TokenStream {
match export_tokens_internal(attr, tokens, true) {
match export_tokens_internal(attr, tokens, true, true) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand All @@ -78,7 +78,7 @@ pub fn export_tokens(attr: TokenStream, tokens: TokenStream) -> TokenStream {
/// and/or do not need to be used locally.
#[proc_macro_attribute]
pub fn export_tokens_no_emit(attr: TokenStream, tokens: TokenStream) -> TokenStream {
match export_tokens_internal(attr, tokens, false) {
match export_tokens_internal(attr, tokens, false, true) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand All @@ -93,7 +93,7 @@ pub fn export_tokens_no_emit(attr: TokenStream, tokens: TokenStream) -> TokenStr
/// Can only be used within a proc macro crate.
#[proc_macro]
pub fn export_tokens_alias(tokens: TokenStream) -> TokenStream {
match export_tokens_alias_internal(tokens, true) {
match export_tokens_alias_internal(tokens, true, true) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand All @@ -105,7 +105,7 @@ pub fn export_tokens_alias(tokens: TokenStream) -> TokenStream {
/// Can only be used within a proc macro crate.
#[proc_macro]
pub fn export_tokens_alias_no_emit(tokens: TokenStream) -> TokenStream {
match export_tokens_alias_internal(tokens, false) {
match export_tokens_alias_internal(tokens, false, true) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand Down Expand Up @@ -140,7 +140,15 @@ pub fn export_tokens_alias_no_emit(tokens: TokenStream) -> TokenStream {
/// ```
#[proc_macro]
pub fn forward_tokens(tokens: TokenStream) -> TokenStream {
match forward_tokens_internal(tokens) {
match forward_tokens_internal(tokens, true) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}

#[proc_macro]
pub fn forward_tokens_verbatim(tokens: TokenStream) -> TokenStream {
match forward_tokens_internal(tokens, false) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand Down Expand Up @@ -387,7 +395,15 @@ pub fn import_tokens_proc(attr: TokenStream, tokens: TokenStream) -> TokenStream
/// For more information and an example see [`macro@with_custom_parsing`].
#[proc_macro_attribute]
pub fn import_tokens_attr(attr: TokenStream, tokens: TokenStream) -> TokenStream {
match import_tokens_attr_internal(attr, tokens) {
match import_tokens_attr_internal(attr, tokens, true) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}

#[proc_macro_attribute]
pub fn import_tokens_attr_verbatim(attr: TokenStream, tokens: TokenStream) -> TokenStream {
match import_tokens_attr_internal(attr, tokens, false) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ pub mod mm_core {
}

pub use macro_magic_macros::{
export_tokens, export_tokens_alias, export_tokens_no_emit, forward_tokens, use_attr, use_proc,
export_tokens, export_tokens_alias, export_tokens_no_emit, forward_tokens,
forward_tokens_verbatim, use_attr, use_proc,
};

#[cfg(feature = "proc_support")]
pub use macro_magic_macros::{
import_tokens, import_tokens_attr, import_tokens_proc, with_custom_parsing,
import_tokens, import_tokens_attr, import_tokens_attr_verbatim, import_tokens_proc,
with_custom_parsing,
};

/// Contains re-exports required at compile-time by the macro_magic macros and support
Expand Down
18 changes: 12 additions & 6 deletions tests/test_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,18 @@ pub fn combine_structs(attr: TokenStream, tokens: TokenStream) -> TokenStream {
let Fields::Named(local_fields) = local_struct.fields else {
return Error::new(
local_struct.fields.span(),
"unnamed fields are not supported"
).to_compile_error().into()
"unnamed fields are not supported",
)
.to_compile_error()
.into();
};
let Fields::Named(foreign_fields) = foreign_struct.fields else {
return Error::new(
foreign_struct.fields.span(),
"unnamed fields are not supported"
).to_compile_error().into()
"unnamed fields are not supported",
)
.to_compile_error()
.into();
};
let local_fields = local_fields.named.iter();
let foreign_fields = foreign_fields.named.iter();
Expand Down Expand Up @@ -303,8 +307,10 @@ pub fn require(tokens: TokenStream) -> TokenStream {
return Error::new(
external_mod.span(),
"cannot import tokens from a file-based module since custom file-level \
attributes are not yet supported by Rust"
).to_compile_error().into()
attributes are not yet supported by Rust",
)
.to_compile_error()
.into();
};
quote! {
#(#stmts)
Expand Down