Skip to content

Commit

Permalink
Merge pull request #13 from gupnik/verbatim_export
Browse files Browse the repository at this point in the history
Adds ability to use provided ident verbatim for export
  • Loading branch information
sam0x17 committed Oct 10, 2023
2 parents eea86e6 + e23ca50 commit cdc40f3
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 33 deletions.
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

0 comments on commit cdc40f3

Please sign in to comment.