diff --git a/core/src/lib.rs b/core/src/lib.rs index 4c2f01e..23dad78 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,6 +1,9 @@ //! This crate contains most of the internal implementation of the macros in the //! `macro_magic_macros` crate. For the most part, the proc macros in `macro_magic_macros` just //! call their respective `_internal` variants in this crate. + +use std::sync::atomic::{AtomicUsize, Ordering}; + use derive_syn_parse::Parse; use macro_magic_core_macros::*; use proc_macro2::{Delimiter, Group, Punct, Spacing, Span, TokenStream as TokenStream2}; @@ -15,6 +18,9 @@ use syn::{ pub const MACRO_MAGIC_ROOT: &'static str = get_macro_magic_root!(); +/// A global counter, can be used to generate a relatively unique identifier. +static COUNTER: AtomicUsize = AtomicUsize::new(0); + /// Private module containing custom keywords used for parsing in this crate mod keywords { use syn::custom_keyword; @@ -390,11 +396,36 @@ pub fn flatten_ident(ident: &Ident) -> Ident { /// /// Used by [`export_tokens_internal`] and several other functions. pub fn export_tokens_macro_ident(ident: &Ident) -> Ident { - let ident = flatten_ident(&ident); + let ident = flatten_ident(ident); let ident_string = format!("__export_tokens_tt_{}", ident.to_token_stream().to_string()); Ident::new(ident_string.as_str(), Span::call_site()) } +pub fn export_tokens_macro_path(item_path: &Path) -> Path { + let Some(last_seg) = item_path.segments.last() else { unreachable!("must have at least one segment") }; + let mut leading_segs = item_path + .segments + .iter() + .cloned() + .map(|seg| seg.ident) + .collect::>()[0..item_path.segments.len() - 1] + .to_vec(); + let last_seg = export_tokens_macro_ident(&last_seg.ident); + leading_segs.push(last_seg); + parse_quote!(#(#leading_segs)::*) +} + +fn new_unique_export_tokens_ident(ident: &Ident) -> Ident { + let unique_id = COUNTER.fetch_add(1, Ordering::SeqCst); + let ident = flatten_ident(ident); + let ident_string = format!( + "__export_tokens_tt_{}_{}", + ident.to_token_stream().to_string(), + unique_id + ); + Ident::new(ident_string.as_str(), Span::call_site()) +} + /// The internal code behind the `#[export_tokens]` attribute macro. /// /// The `attr` variable contains the tokens for the optional naming [`struct@Ident`] (necessary @@ -441,6 +472,7 @@ pub fn export_tokens_internal, E: Into>( } None => parse2::(attr)?, }; + let macro_ident = new_unique_export_tokens_ident(&ident); let ident = export_tokens_macro_ident(&ident); let item_emit = match emit { true => quote! { @@ -452,7 +484,7 @@ pub fn export_tokens_internal, E: Into>( let output = quote! { #[doc(hidden)] #[macro_export] - macro_rules! #ident { + macro_rules! #macro_ident { // arm with extra support (used by attr) ( $(::)?$($tokens_var:ident)::*, @@ -473,6 +505,7 @@ pub fn export_tokens_internal, E: Into>( } }; } + pub use #macro_ident as #ident; #item_emit }; Ok(output) @@ -526,16 +559,7 @@ pub fn export_tokens_alias_internal>( /// where `my_tokens` contains the tokens of `ExportedItem`. pub fn import_tokens_internal>(tokens: T) -> Result { let args = parse2::(tokens.into())?; - let Some(source_ident_seg) = args.source_path.segments.last() else { unreachable!("must have at least one segment") }; - let source_ident_seg = export_tokens_macro_ident(&source_ident_seg.ident); - let source_path = if args.source_path.segments.len() > 1 { - let Some(crate_seg) = args.source_path.segments.first() else { - unreachable!("path has at least two segments, so there is a first segment"); - }; - quote!(#crate_seg::#source_ident_seg) - } else { - quote!(#source_ident_seg) - }; + let source_path = export_tokens_macro_path(&args.source_path); let inner_macro_path = private_path("e!(import_tokens_inner)); let tokens_var_ident = args.tokens_var_ident; Ok(quote! { @@ -565,16 +589,7 @@ pub fn forward_tokens_internal>(tokens: T) -> Result path, None => macro_magic_root(), }; - let Some(source_ident_seg) = args.source.segments.last() else { unreachable!("must have at least one segment") }; - let source_ident_seg = export_tokens_macro_ident(&source_ident_seg.ident); - let source_path = if args.source.segments.len() > 1 { - let Some(crate_seg) = args.source.segments.first() else { - unreachable!("path has at least two segments, so there is a first segment"); - }; - quote!(#crate_seg::#source_ident_seg) - } else { - quote!(#source_ident_seg) - }; + let source_path = export_tokens_macro_path(&args.source); let target_path = args.target; if let Some(extra) = args.extra { Ok(quote! { diff --git a/tests/external_crate/src/lib.rs b/tests/external_crate/src/lib.rs index 941c1f1..793a6bd 100644 --- a/tests/external_crate/src/lib.rs +++ b/tests/external_crate/src/lib.rs @@ -2,7 +2,7 @@ use macro_magic::*; -mod some_submodule { +pub mod some_submodule { use macro_magic::*; struct FooBarStruct {} @@ -41,13 +41,6 @@ mod an_external_module { } } -fn _some_function() { - #[export_tokens] - fn some_sub_function() -> u32 { - 33 - } -} - macro_rules! another_macro { () => { let a = 2; diff --git a/tests/middle_crate/src/lib.rs b/tests/middle_crate/src/lib.rs index 51ddf6c..dc620ce 100644 --- a/tests/middle_crate/src/lib.rs +++ b/tests/middle_crate/src/lib.rs @@ -8,7 +8,4 @@ pub mod export_mod { struct ForeignItem {} pub use test_macros::distant_re_export_attr; - pub use test_macros::distant_re_export_proc; - -pub use macro_magic::{use_attr, use_proc}; diff --git a/tests/tests.rs b/tests/tests.rs index 99464f7..2079810 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -86,7 +86,7 @@ pub mod hunter { } } -#[test_tokens_attr2(external_crate::AnExternalTraitImpl)] +#[test_tokens_attr2(external_crate::some_submodule::AnExternalTraitImpl)] struct LocalItemStruct {} #[test_tokens_attr_direct_import(external_crate::an_external_function)] @@ -101,7 +101,7 @@ struct LionStruct {} struct TigerStruct {} // test proc item position -item_level_proc!(external_crate::AnExternalTraitImpl); +item_level_proc!(external_crate::some_submodule::AnExternalTraitImpl); #[test] fn test_import_tokens_proc_item_position() { @@ -111,23 +111,14 @@ fn test_import_tokens_proc_item_position() { #[test] fn test_import_tokens_proc_statement_position() { example_tokens_proc!(LionStruct); - example_tokens_proc!(external_crate::AnExternalTraitImpl); + example_tokens_proc!(external_crate::some_submodule::AnExternalTraitImpl); } #[test] fn test_import_tokens_proc_expr_position() { let something = example_tokens_proc!(TigerStruct); assert_eq!(something.to_string(), "struct TigerStruct {}"); - let _something_else = example_tokens_proc!(external_crate::AnExternalTraitImpl); -} - -#[test] -fn test_export_tokens_inside_function() { - let something = example_tokens_proc!(external_crate::some_sub_function); - assert_eq!( - something.to_string(), - "fn some_sub_function() -> u32 { 33 }" - ); + let _something_else = example_tokens_proc!(external_crate::some_submodule::AnExternalTraitImpl); } #[test] @@ -166,7 +157,7 @@ fn import_tokens_same_mod_ident() { #[cfg(feature = "proc_support")] #[test] fn import_tokens_different_mod_no_ident() { - import_tokens!(let tokens = PlusPlus); + import_tokens!(let tokens = some_module::PlusPlus); assert_eq!( tokens.to_string(), "fn plus_plus < T : Into < i64 > > (n : T) -> i64 { n . into () + 1 }" @@ -176,7 +167,7 @@ fn import_tokens_different_mod_no_ident() { #[cfg(feature = "proc_support")] #[test] fn import_tokens_different_mod_ident() { - import_tokens!(let tokens = MinusMinus); + import_tokens!(let tokens = some_module::MinusMinus); assert_eq!( tokens.to_string(), "fn minus_minus < T : Into < i32 > > (n : T) -> i32 { n . into () - 1 }" @@ -199,7 +190,7 @@ fn println_inside_fn_current_file() { #[test] fn println_inside_fn_external_file() { - let tokens = example_tokens_proc!(external_fn_with_println); + let tokens = example_tokens_proc!(external_file::external_fn_with_println); assert_eq!( tokens.to_string(), "fn external_fn_with_println() { println! (\"testing\") ; }"