1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! `#[magetypes]` — generate per-tier function variants via text substitution.
use proc_macro::TokenStream;
use quote::{ToTokens, quote};
use crate::common::*;
use crate::tiers::*;
pub(crate) fn magetypes_impl(mut input_fn: LightFn, tiers: &[ResolvedTier]) -> TokenStream {
// Strip user-provided #[arcane] / #[rite] to prevent double-wrapping
// (magetypes auto-adds #[arcane] on non-scalar variants)
input_fn
.attrs
.retain(|attr| !attr.path().is_ident("arcane") && !attr.path().is_ident("rite"));
let fn_name = &input_fn.sig.ident;
let fn_attrs = &input_fn.attrs;
let mut variants = Vec::new();
for tier in tiers {
// Clone and rename at the AST level (no string surgery)
let mut variant_fn = input_fn.clone();
variant_fn.sig.ident = quote::format_ident!("{}_{}", fn_name, tier.suffix);
// Strip attrs — they go on the outer wrapper, not each variant
variant_fn.attrs = Vec::new();
// Replace `Token` ident with the concrete token path at the token level.
// This is safe: each identifier is a discrete token tree, so `ScalarToken`,
// `IntoConcreteToken`, etc. are single Ident nodes that do NOT match "Token".
let variant_tokens = if tier.token_path.is_empty() {
// `default` tier has no token type — just emit the fn without replacement
variant_fn.to_token_stream()
} else {
let concrete_tokens: proc_macro2::TokenStream = tier
.token_path
.parse()
.expect("tier token_path must be valid tokens");
replace_ident_in_tokens(variant_fn.to_token_stream(), "Token", &concrete_tokens)
};
// Add cfg guard: arch + optional feature gate
let allow_attr = if tier.allow_unexpected_cfg {
quote! { #[allow(unexpected_cfgs)] }
} else {
quote! {}
};
let cfg_guard = match (tier.target_arch, &tier.feature_gate) {
(Some(arch), Some(feat)) => quote! {
#[cfg(target_arch = #arch)]
#allow_attr
#[cfg(feature = #feat)]
},
(Some(arch), None) => quote! { #[cfg(target_arch = #arch)] },
(None, Some(feat)) => quote! {
#allow_attr
#[cfg(feature = #feat)]
},
(None, None) => quote! {},
};
variants.push(if tier.name != "scalar" && tier.name != "default" {
// Non-fallback variants get #[arcane] so target_feature is applied
quote! {
#cfg_guard
#[archmage::arcane]
#variant_tokens
}
} else {
quote! {
#cfg_guard
#variant_tokens
}
});
}
// Remove attributes from the list that should not be duplicated
let filtered_attrs: Vec<_> = fn_attrs
.iter()
.filter(|a| !a.path().is_ident("magetypes"))
.collect();
let output = quote! {
#(#filtered_attrs)*
#(#variants)*
};
output.into()
}