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

Remove need for use_attr and use_proc #5

Merged
merged 6 commits into from
Jun 6, 2023
Merged
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
246 changes: 109 additions & 137 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -33,6 +33,9 @@ mod keywords {
custom_keyword!(proc_macro_attribute);
custom_keyword!(proc_macro);
custom_keyword!(proc_macro_derive);

// WARNING: Must be kept same as in macro expansions
custom_keyword!(__private_macro_magic_tokens_forwarded);
}

/// Used to parse args that were passed to [`forward_tokens_internal`].
@@ -596,18 +599,25 @@ pub fn forward_tokens_inner_internal<T: Into<TokenStream2>>(tokens: T) -> Result
let parsed = parse2::<ForwardedTokens>(tokens.into())?;
let target_path = parsed.target_path;
let imported_tokens = parsed.item;
let combined_tokens = match parsed.extra {
Some(extra) => quote! {
#imported_tokens,
#extra
},
None => quote!(#imported_tokens),
};
Ok(quote! {
#target_path! {
#combined_tokens
}
})
let tokens_forwarded_keyword = keywords::__private_macro_magic_tokens_forwarded::default();
let pound = Punct::new('#', Spacing::Alone);
match parsed.extra {
// some extra, used by attr, so expand to attribute macro
Some(extra) => Ok(quote! {
#pound [#target_path(
#tokens_forwarded_keyword
#imported_tokens,
#extra
)] type __Discarded = ();
}),
// no extra, used by proc, import_tokens, etc, so expand to proc macro
None => Ok(quote! {
#target_path! {
#tokens_forwarded_keyword
#imported_tokens
}
}),
}
}

/// The internal implementation for the `#[with_custom_parsing(..)` attribute macro.
@@ -729,6 +739,7 @@ pub fn import_tokens_attr_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2
let orig_sig = proc_macro.proc_fn.sig;
let orig_stmts = proc_macro.proc_fn.block.stmts;
let orig_attrs = proc_macro.proc_fn.attrs;
let orig_sig_ident = &orig_sig.ident;

// inner macro
let inner_macro_ident = format_ident!("__import_tokens_attr_{}_inner", orig_sig.ident);
@@ -743,51 +754,65 @@ pub fn import_tokens_attr_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2
#(#orig_attrs)
*
pub #orig_sig {
pub #inner_sig {
let __combined_args = #mm_path::__private::syn::parse_macro_input!(#attr_ident as #mm_path::mm_core::AttrItemWithExtra);
let (#attr_ident, #tokens_ident) = (__combined_args.imported_item, __combined_args.extra);
let #attr_ident: proc_macro::TokenStream = #attr_ident.to_token_stream().into();
let (#tokens_ident, __source_path, __custom_tokens) = {
use #mm_path::mm_core::unescape_extra;
let extra = #tokens_ident.value();
let mut extra_split = extra.split("~~");
let (tokens_string, foreign_path_string, custom_parsed_string) = (
unescape_extra(extra_split.next().unwrap()),
unescape_extra(extra_split.next().unwrap()),
unescape_extra(extra_split.next().unwrap()),
);
let foreign_path: proc_macro::TokenStream = foreign_path_string.as_str().parse().unwrap();
let tokens: proc_macro::TokenStream = tokens_string.as_str().parse().unwrap();
let custom_parsed_tokens: proc_macro::TokenStream = custom_parsed_string.as_str().parse().unwrap();
(tokens, foreign_path, custom_parsed_tokens)
};
#(#orig_stmts)
*
}

use #mm_path::__private::*;
use #mm_path::__private::quote::ToTokens;
use #mm_path::mm_core::*;
let attached_item = syn::parse_macro_input!(#tokens_ident as syn::Item);
let attached_item_str = attached_item.to_token_stream().to_string();
#path_resolver
let extra = format!(
"{}~~{}~~{}",
escape_extra(attached_item_str),
escape_extra(path.to_token_stream().to_string().as_str()),
escape_extra(custom_parsed.to_token_stream().to_string().as_str())
);
quote::quote! {
#mm_override_path::forward_tokens! {
#pound path,
#inner_macro_ident,
#mm_override_path,
#pound extra
}
}.into()
}

#[doc(hidden)]
#[proc_macro]
pub #inner_sig {
let __combined_args = #mm_path::__private::syn::parse_macro_input!(#attr_ident as #mm_path::mm_core::AttrItemWithExtra);
let (#attr_ident, #tokens_ident) = (__combined_args.imported_item, __combined_args.extra);
let #attr_ident: proc_macro::TokenStream = #attr_ident.to_token_stream().into();
let (#tokens_ident, __source_path, __custom_tokens) = {
use #mm_path::mm_core::unescape_extra;
let extra = #tokens_ident.value();
let mut extra_split = extra.split("~~");
let (tokens_string, foreign_path_string, custom_parsed_string) = (
unescape_extra(extra_split.next().unwrap()),
unescape_extra(extra_split.next().unwrap()),
unescape_extra(extra_split.next().unwrap()),
syn::custom_keyword!(__private_macro_magic_tokens_forwarded);

let mut cloned_attr = #attr_ident.clone().into_iter();
let first_attr_token = cloned_attr.next();
let attr_minus_first_token = proc_macro::TokenStream::from_iter(cloned_attr);

let forwarded = first_attr_token.map_or(false, |token| {
syn::parse::<__private_macro_magic_tokens_forwarded>(token.into()).is_ok()
});

if forwarded {
#inner_macro_ident(attr_minus_first_token)
} else {
let attached_item = syn::parse_macro_input!(#tokens_ident as syn::Item);
let attached_item_str = attached_item.to_token_stream().to_string();
#path_resolver
let extra = format!(
"{}~~{}~~{}",
escape_extra(attached_item_str),
escape_extra(path.to_token_stream().to_string().as_str()),
escape_extra(custom_parsed.to_token_stream().to_string().as_str())
);
let foreign_path: proc_macro::TokenStream = foreign_path_string.as_str().parse().unwrap();
let tokens: proc_macro::TokenStream = tokens_string.as_str().parse().unwrap();
let custom_parsed_tokens: proc_macro::TokenStream = custom_parsed_string.as_str().parse().unwrap();
(tokens, foreign_path, custom_parsed_tokens)
};
#(#orig_stmts)
*
quote::quote! {
#mm_override_path::forward_tokens! {
#pound path,
#orig_sig_ident,
#mm_override_path,
#pound extra
}
}.into()
}
}

})
}

@@ -812,6 +837,7 @@ pub fn import_tokens_proc_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2
let orig_sig = proc_macro.proc_fn.sig;
let orig_stmts = proc_macro.proc_fn.block.stmts;
let orig_attrs = proc_macro.proc_fn.attrs;
let orig_sig_ident = &orig_sig.ident;

// inner macro
let inner_macro_ident = format_ident!("__import_tokens_proc_{}_inner", orig_sig.ident);
@@ -830,60 +856,42 @@ pub fn import_tokens_proc_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2
#(#orig_attrs)
*
pub #orig_sig {
#inner_sig {
#(#orig_stmts)
*
}

use #mm_path::__private::*;
use #mm_path::__private::quote::ToTokens;
let source_path = match syn::parse::<syn::Path>(#tokens_ident) {
Ok(path) => path,
Err(e) => return e.to_compile_error().into(),
};
quote::quote! {
#mm_override_path::forward_tokens! {
#pound source_path,
#inner_macro_ident,
#mm_override_path
}
}.into()
}

#[doc(hidden)]
#[proc_macro]
pub #inner_sig {
#(#orig_stmts)
*
}
})
}
syn::custom_keyword!(__private_macro_magic_tokens_forwarded);

/// Internal implementation for the `#[use_proc]` and `#[use_attr]` attribute macros
pub fn use_internal<T1: Into<TokenStream2>, T2: Into<TokenStream2>>(
attr: T1,
tokens: T2,
mode: ProcMacroType,
) -> Result<TokenStream2> {
parse2::<Nothing>(attr.into())?;
let orig_stmt = parse2::<BasicUseStmt>(tokens.into())?;
let orig_path = orig_stmt.path.clone();
let orig_attrs = orig_stmt.attrs;
let vis = orig_stmt.vis;
let ident = &orig_stmt
.path
.segments
.last()
.expect("path must have at least one segment")
.ident;
let hidden_ident = match mode {
ProcMacroType::Normal => format_ident!("__import_tokens_proc_{}_inner", ident),
ProcMacroType::Attribute => format_ident!("__import_tokens_attr_{}_inner", ident),
ProcMacroType::Derive => unimplemented!(),
};
let mut hidden_path: Path = orig_stmt.path.clone();
hidden_path.segments.last_mut().unwrap().ident = hidden_ident;
Ok(quote! {
#(#orig_attrs)
*
#vis use #orig_path;
#[doc(hidden)]
#vis use #hidden_path;
let mut cloned_tokens = #tokens_ident.clone().into_iter();
let first_token = cloned_tokens.next();
let tokens_minus_first = proc_macro::TokenStream::from_iter(cloned_tokens);

let forwarded = first_token.map_or(false, |token| {
syn::parse::<__private_macro_magic_tokens_forwarded>(token.into()).is_ok()
});

if forwarded {
#inner_macro_ident(tokens_minus_first)
} else {
use #mm_path::__private::*;
use #mm_path::__private::quote::ToTokens;
let source_path = match syn::parse::<syn::Path>(#tokens_ident) {
Ok(path) => path,
Err(e) => return e.to_compile_error().into(),
};
quote::quote! {
#mm_override_path::forward_tokens! {
#pound source_path,
#orig_sig_ident,
#mm_override_path
}
}.into()
}
}
})
}

@@ -1058,42 +1066,6 @@ mod tests {
.is_err());
}

#[test]
fn test_parse_use_stmt() {
assert!(use_internal(
quote!(),
quote!(
use some::path;
),
ProcMacroType::Attribute,
)
.is_ok());
assert!(use_internal(
quote!(),
quote!(
use some::path
),
ProcMacroType::Normal,
)
.is_err());
assert!(use_internal(
quote!(),
quote!(
use some::
),
ProcMacroType::Attribute,
)
.is_err());
assert!(use_internal(
quote!(),
quote!(
pub use some::long::path;
),
ProcMacroType::Attribute,
)
.is_ok());
}

#[test]
fn test_snake_case() {
assert_eq!(to_snake_case("ThisIsATriumph"), "this_is_a_triumph");
68 changes: 12 additions & 56 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -377,66 +377,22 @@ pub fn with_custom_parsing(attr: TokenStream, tokens: TokenStream) -> TokenStrea
}
}

/// Can be used to properly import and re-export attribute macros that were created using
/// [`macro@import_tokens_attr`].
///
/// You should use this if you ever need to import or re-export macros created using this
/// facility since it will ensure the hidden helper macros are imported and/or re-exported as
/// well.
///
/// This attribute only supports simple, non-tree-based use statements consisting of `use [vis]
/// [path];` and will fail if you attempt to provide a more complex use statement.
///
/// ## Examples
///
/// A simple import:
/// ```ignore
/// #[use_attr]
/// use my_crate::some_attribute;
/// ```
///
/// A re-export:
/// ```ignore
/// #[use_attr]
/// pub use my_crate::some_other_attribute;
/// ```
/// Deprecated: No-op
#[deprecated(
note = "`use_attr` is no longer needed for importing or re-exporting, implementation is no-op, it can be removed safely"
)]
#[proc_macro_attribute]
pub fn use_attr(attr: TokenStream, tokens: TokenStream) -> TokenStream {
match use_internal(attr, tokens, ProcMacroType::Attribute) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
pub fn use_attr(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
tokens
}

/// Can be used to properly import and re-export proc macros that were created using
/// [`macro@import_tokens_proc`].
///
/// You should use this if you ever need to import or re-export macros created using this
/// facility since it will ensure the hidden helper macros are imported and/or re-exported as
/// well.
///
/// This attribute only supports simple, non-tree-based use statements consisting of `use [vis]
/// [path];` and will fail if you attempt to provide a more complex use statement.
///
/// ## Examples
///
/// A simple import:
/// ```ignore
/// #[use_proc]
/// use my_crate::some_proc_macro;
/// ```
///
/// A re-export:
/// ```ignore
/// #[use_proc]
/// pub use my_crate::some_other_proc_macro;
/// ```
/// Deprecated: No-op
#[deprecated(
note = "`use_proc` is no longer needed for importing or re-exporting, implementation is no-op, it can be removed safely"
)]
#[proc_macro_attribute]
pub fn use_proc(attr: TokenStream, tokens: TokenStream) -> TokenStream {
match use_internal(attr, tokens, ProcMacroType::Normal) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
pub fn use_proc(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
tokens
}

/// A helper macro used by [`macro@import_tokens`]. Hidden from docs.
2 changes: 0 additions & 2 deletions tests/isolated_crate/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#![cfg(test)]

#[middle_crate::use_attr]
use middle_crate::distant_re_export_attr;

#[middle_crate::use_proc]
use middle_crate::distant_re_export_proc;

#[distant_re_export_attr(middle_crate::ForeignItem)]
2 changes: 0 additions & 2 deletions tests/middle_crate/src/lib.rs
Original file line number Diff line number Diff line change
@@ -7,10 +7,8 @@ pub mod export_mod {
#[macro_magic::export_tokens]
struct ForeignItem {}

#[macro_magic::use_attr]
pub use test_macros::distant_re_export_attr;

#[macro_magic::use_proc]
pub use test_macros::distant_re_export_proc;

pub use macro_magic::{use_attr, use_proc};
20 changes: 16 additions & 4 deletions tests/test_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,9 @@ use derive_syn_parse::Parse;
use macro_magic::{mm_core::ForeignPath, *};
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, spanned::Spanned, Error, Fields, Item, ItemMod, ItemStruct, Path};
use syn::{
parse_macro_input, spanned::Spanned, Error, Fields, Ident, Item, ItemMod, ItemStruct, Path,
};

/// An example proc macro built on top of `import_tokens_internal`.
///
@@ -43,14 +45,24 @@ pub fn some_macro(tokens: TokenStream) -> TokenStream {
.into()
}

#[derive(Parse)]
struct SomeOtherMacroArgs {
forwarded_ident: Ident,
item: Item,
}

#[proc_macro]
pub fn some_other_macro(tokens: TokenStream) -> TokenStream {
// println!("tokens: {}", tokens.to_string());
let item = parse_macro_input!(tokens as Item);
let args = parse_macro_input!(tokens as SomeOtherMacroArgs);
assert_eq!(
args.forwarded_ident.to_token_stream().to_string(),
"__private_macro_magic_tokens_forwarded",
);
assert_eq!(
item.to_token_stream().to_string(),
args.item.to_token_stream().to_string(),
"struct SomeStruct { field1 : u32, field2 : bool, }"
);
let item = args.item;
quote! {
#[allow(unused)]
#item
12 changes: 1 addition & 11 deletions tests/tests.rs
Original file line number Diff line number Diff line change
@@ -6,27 +6,17 @@ use test_macros::{custom_export_tokens, include_impl, include_impl_inner};
#[cfg(feature = "proc_support")]
use test_macros::some_macro;

#[use_attr]
use test_macros::combine_structs;
#[use_attr]
use test_macros::emit_foreign_path;
#[use_proc]
use test_macros::example_tokens_proc;
#[use_attr]
use test_macros::import_tokens_attr_with_custom_parsing_a;
#[use_attr]
use test_macros::import_tokens_attr_with_custom_parsing_b;
#[use_proc]
use test_macros::item_level_proc;
#[use_proc]
use test_macros::require;
#[use_attr]
use test_macros::test_tokens_attr1;
#[use_attr]
use test_macros::test_tokens_attr2;

/// Some doc comment
#[use_attr]
pub use test_macros::test_tokens_attr_direct_import;

#[export_tokens]
@@ -146,7 +136,7 @@ fn attr_direct_import() {
fn test_forward_tokens() {
#[macro_export]
macro_rules! receiver {
($tokens:item) => {
(__private_macro_magic_tokens_forwarded $tokens:item) => {
stringify!($tokens)
};
}