macro_magic_macros/lib.rs
1#![no_std]
2
3use macro_magic_core::*;
4use proc_macro::TokenStream;
5
6/// Can be applied to any item. Doing so will make the tokens for this item available for
7/// import by the other macros in this crate.
8///
9/// An optional argument can be provided specifying an override export name to use instead of
10/// the regular name of the item, such as `#[export_tokens(MyCoolName)]` or
11/// `#[export_tokens(some_name)]`. Syntactically this name is parsed as a `syn::Ident` and is
12/// then normalized by converting to snake_case. Note that because of this, `MyCoolName` would
13/// collide with `my_cool_name`, resulting in a compiler error if these items are being
14/// exported from the same module.
15///
16/// Note that some types of items, namely `syn::ItemForeignMod`, `syn::ItemUse`,
17/// `syn::ItemImpl`, and `syn::Item::Verbatim`, do not have an inherent concept of a naming
18/// ident, and so for these items specifying an override name is required or you will get a
19/// compiler error. This also applies to `macro_rules!` definitions that do not specify a name.
20///
21/// Note also that while you can presently _attach_ `#[export_tokens]` to anything attributes
22/// can be attached to, some of these items do not exist at the module path level, and
23/// therefore cannot be accessed. You should only attach `#[export_tokens]` to items that are
24/// accessible by path from the location where you wish to use their tokens.
25///
26/// ## Examples
27///
28/// Applied to a regular function definition:
29/// ```ignore
30/// #[export_tokens]
31/// fn my_function() {
32/// println!("hey");
33/// }
34/// ```
35///
36/// Applied to a module:
37/// ```ignore
38/// #[export_tokens]
39/// mod my_module() {
40/// fn some_fn() {
41/// stuff();
42/// }
43/// }
44/// ```
45///
46/// Applied to an `impl` requiring an override name:
47/// ```ignore
48/// #[export_tokens(impl_my_trait_for_my_item)]
49/// impl MyTrait for MyItem {
50/// fn something() {
51/// do_stuff();
52/// }
53/// }
54/// ```
55///
56/// Applied to a struct, but specifying an override name:
57/// ```ignore
58/// #[export_tokens(SomeOtherName)]
59/// struct MyStruct {
60/// field: u32,
61/// }
62/// ```
63///
64/// Previously it was possible to access `#[export_tokens]` items defined in
65/// private/inaccessible contexts, however this was removed in 0.4.x.
66#[proc_macro_attribute]
67pub fn export_tokens(attr: TokenStream, tokens: TokenStream) -> TokenStream {
68 match export_tokens_internal(attr, tokens, true, true) {
69 Ok(tokens) => tokens.into(),
70 Err(err) => err.to_compile_error().into(),
71 }
72}
73
74/// Like [`#[export_tokens]`](`macro@export_tokens`) but does not emit the tokens of the
75/// attached item locally.
76///
77/// This is useful for scenarios where the local tokens would not compile anyway locally,
78/// and/or do not need to be used locally.
79#[proc_macro_attribute]
80pub fn export_tokens_no_emit(attr: TokenStream, tokens: TokenStream) -> TokenStream {
81 match export_tokens_internal(attr, tokens, false, true) {
82 Ok(tokens) => tokens.into(),
83 Err(err) => err.to_compile_error().into(),
84 }
85}
86
87/// Creates an attribute proc macro that is an alias for
88/// [`#[export_tokens]`](`macro@export_tokens`).
89///
90/// Simply pass an ident to this proc macro, and an alias for
91/// [`#[export_tokens]`](`macro@export_tokens`) will be created with the specified name.
92///
93/// Can only be used within a proc macro crate.
94#[proc_macro]
95pub fn export_tokens_alias(tokens: TokenStream) -> TokenStream {
96 match export_tokens_alias_internal(tokens, true, true) {
97 Ok(tokens) => tokens.into(),
98 Err(err) => err.to_compile_error().into(),
99 }
100}
101
102/// Like [`#[export_tokens]`](`macro@export_tokens`) but intead creates an alias for
103/// [`#[export_tokens_no_emit]`](`macro@export_tokens_no_emit`)
104///
105/// Can only be used within a proc macro crate.
106#[proc_macro]
107pub fn export_tokens_alias_no_emit(tokens: TokenStream) -> TokenStream {
108 match export_tokens_alias_internal(tokens, false, true) {
109 Ok(tokens) => tokens.into(),
110 Err(err) => err.to_compile_error().into(),
111 }
112}
113
114/// "Forwards" the tokens of the specified exported item (specified by path as the first arg)
115/// to the specified proc or `macro_rules!` macro (specified by path as the second arg).
116///
117/// This is used internally as the basis for many of the other macros in this crate, but can
118/// also be useful in its own right in certain situations.
119///
120/// Note that the referenced item _must_ have the [`#[export_tokens]`][`macro@export_tokens`]
121/// attribute attached to it, or this will not work.
122///
123/// There is also an optional third argument called "extra" which allows you to forward
124/// arbitrary data to the target macro. This is used by
125/// [`#[import_tokens_attr]`](`macro@import_tokens_proc`) to pass the tokens for the attached
126/// item in addition to the tokens for the external item.
127///
128/// ## Example
129///
130/// ```ignore
131/// #[macro_export]
132/// macro_rules! receiver {
133/// ($tokens:item) => {
134/// stringify!($tokens)
135/// };
136/// }
137///
138/// let result = forward_tokens!(LionStruct, receiver);
139/// assert_eq!(result, "struct LionStruct {}");
140/// ```
141#[proc_macro]
142pub fn forward_tokens(tokens: TokenStream) -> TokenStream {
143 match forward_tokens_internal(tokens, true) {
144 Ok(tokens) => tokens.into(),
145 Err(err) => err.to_compile_error().into(),
146 }
147}
148
149#[proc_macro]
150pub fn forward_tokens_verbatim(tokens: TokenStream) -> TokenStream {
151 match forward_tokens_internal(tokens, false) {
152 Ok(tokens) => tokens.into(),
153 Err(err) => err.to_compile_error().into(),
154 }
155}
156
157/// Allows you to import the tokens of an external item marked with
158/// [`#[export_tokens]`][`macro@export_tokens`] whose path is already known at compile-time
159/// without having to do any additional parsing.
160///
161/// If the path of the item is defined by the downstream programmer and is not "hard-coded",
162/// then you should instead use [`#[import_tokens_attr]`](`macro@import_tokens_attr`) /
163/// [`#[import_tokens_proc]`](`macro@import_tokens_proc`).
164///
165/// The macro lets you define as its argument a let variable declaration that will expand to
166/// that variable being set to the tokens of the specified external item at compile-time.
167///
168/// For example:
169///
170/// ```ignore
171/// import_tokens!(let tokens = external_crate::SomeItem);
172/// ```
173///
174/// will expand such that a `tokens` variable will be created containing the tokens for the
175/// `SomeItem` item that exists in an external crate. For this to work,
176/// `external_crate::SomeItem` must be the path of an item that has
177/// [`#[export_tokens]`][`macro@export_tokens`] attached to it. The imported tokens wil be of
178/// type `TokenStream2`.
179///
180/// Unfortunately this macro isn't very useful, because it is quite rare that you already know
181/// the path of the item you want to import _inside_ your proc macro. Note that having the
182/// _tokens_ for the path you want isn't the same as having those tokens already expanded in
183/// the current context.
184///
185/// That said, this can be quite useful for scenarios where for whatever reason you have an
186/// item with a set-in-stone path whose tokens you need to access at compile time.
187#[proc_macro]
188pub fn import_tokens(tokens: TokenStream) -> TokenStream {
189 match import_tokens_internal(tokens) {
190 Ok(tokens) => tokens.into(),
191 Err(err) => err.to_compile_error().into(),
192 }
193}
194
195/// An attribute macro that can be attached to a proc macro function definition that will cause
196/// it to receive the tokens of the external item referred to by its argument as input to your
197/// proc macro.
198///
199/// For example:
200///
201/// ```ignore
202/// #[import_tokens_proc]
203/// #[proc_macro]
204/// pub fn my_macro(tokens: TokenStream) -> TokenStream {
205/// // `tokens` will contain the tokens of
206/// let item = parse_macro_input!(tokens as Item);
207/// // you can now do stuff with `item`
208/// // ...
209/// }
210/// ```
211///
212/// Which you could use like this:
213///
214/// ```ignore
215/// my_macro!(some_crate::some_item);
216/// ```
217///
218/// In this case the `tokens` variable will contain the tokens for the `some_crate::some_item`
219/// item, as long as it has been marked with [`#[export_tokens]`][`macro@export_tokens`].
220///
221/// Note that this attribute can only be used within a proc macro crate.
222///
223/// ## Overriding [`MACRO_MAGIC_ROOT`]:
224///
225/// You can also provide a module path as an optional argument to this attribute macro and that
226/// path will be used as the override for [`MACRO_MAGIC_ROOT`] within the context of code
227/// generated by this attribute. Instead of a `Path`, you are also free to provide any `Expr`
228/// that evaluates to something compatible with [`Into<String>`] so you can dynamically
229/// generate this path based on `format!` and other string manipulation machinery, if
230/// necessary.
231///
232/// Here is an example of providing a `Path` as the override for [`MACRO_MAGIC_ROOT`]:
233///
234/// ```ignore
235/// #[import_tokens_proc(my_crate::__private::macro_magic)]
236/// pub fn my_macro(tokens: TokenStream) -> TokenStream {
237/// // ..
238/// }
239/// ```
240///
241/// and here is an example of providing an [`Into<String>`]-compatible `Expr` as the override
242/// for [`MACRO_MAGIC_ROOT`]:
243///
244/// ```ignore
245/// #[import_tokens_proc(format!("{}::__private::macro_magic", generate_crate_access_2018("my_crate")))]
246/// pub fn my_macro(tokens: TokenStream) -> TokenStream {
247/// // ..
248/// }
249/// ```
250#[proc_macro_attribute]
251pub fn import_tokens_proc(attr: TokenStream, tokens: TokenStream) -> TokenStream {
252 match import_tokens_proc_internal(attr, tokens) {
253 Ok(tokens) => tokens.into(),
254 Err(err) => err.to_compile_error().into(),
255 }
256}
257
258/// Can be attached to an attribute proc macro function, causing it to receive the tokens for
259/// the external item referred to by the path provided as the `attr` / first argument to the
260/// attribute macro.
261///
262/// The item whose path is provided as the `attr` / first argument _must_ have the
263/// [`#[export_tokens]`][`macro@export_tokens`] attribute attached to it, or this will not
264/// work.
265///
266/// For example:
267///
268/// ```ignore
269/// #[import_tokens_attr]
270/// #[proc_macro_attribute]
271/// pub fn my_attribute(attr: TokenStream, tokens: TokenStream) -> TokenStream {
272/// let external_item = parse_macro_input!(attr as Item);
273/// let attached_item = parse_macro_input!(tokens as Item);
274/// // ...
275/// }
276/// ```
277///
278/// Which could then be used like:
279///
280/// ```ignore
281/// #[my_attribute(path::to::AnItem)]
282/// mod my_mod {
283/// // ...
284/// }
285/// ```
286///
287/// This would result in the `external_item` variable having the parsed tokens of the external
288/// `path::to::AnItem` item, and the `attached_item` variable having the parsed tokens of the
289/// item the attribute is attached to (`my_mod`) as usual.
290///
291/// This allows for the creation of extremely powerful attribute macros that take in an export
292/// tokens path as their `attr` and internally receive the tokens for that external item. For
293/// example you could write an attribute macro that combines two modules or two structs
294/// together, among many other things. Custom parsing, covered below, makes these capabilities
295/// even more powerful.
296///
297/// ## Overriding [`MACRO_MAGIC_ROOT`]
298///
299/// You can also provide a module path as an optional argument to this attribute macro and that
300/// path will be used as the override for [`MACRO_MAGIC_ROOT`] within the context of code
301/// generated by this attribute. Instead of a `Path`, you are also free to provide any `Expr`
302/// that evaluates to something compatible with [`Into<String>`] so you can dynamically
303/// generate this path based on `format!` and other string manipulation machinery, if
304/// necessary.
305///
306/// Here is an example of providing a `Path` as the override for [`MACRO_MAGIC_ROOT`]:
307///
308/// ```ignore
309/// #[import_tokens_attr(my_crate::__private::macro_magic)]
310/// pub fn my_macro(attr: TokenStream, tokens: TokenStream) -> TokenStream {
311/// // ..
312/// }
313/// ```
314///
315/// and here is an example of providing an [`Into<String>`]-compatible `Expr` as the override
316/// for [`MACRO_MAGIC_ROOT`]:
317///
318/// ```ignore
319/// #[import_tokens_proc(format!("{}::__private::macro_magic", generate_crate_access_2018("my_crate")))]
320/// pub fn my_macro(attr: TokenStream, tokens: TokenStream) -> TokenStream {
321/// // ..
322/// }
323/// ```
324///
325///
326/// ## Optional Feature: `#[with_custom_parsing(..)]`
327///
328/// By default, [`#[import_tokens_attr]`](`macro@import_tokens_attr`)-based attribute macros
329/// expect the foreign item path to be passed directly as the only argument to the resulting
330/// macro. Sometimes, however, it is desirable to support multiple arguments, or otherwise
331/// implement some kind of custom parsing that determines how the foreign path is obtained. You
332/// can do this by attaching the optional attribute
333/// [`#[with_custom_parsing(..)]`](`macro@with_custom_parsing`) to the same proc macro
334/// attribute definition that you attached `#[import_tokens_attr]` to.
335///
336/// This optional attribute takes one argument, which should be the path to a struct that
337/// implements `syn::parse::Parse`, `quote::ToTokens`, and [`ForeignPath`]. To access the
338/// tokens for your custom parsed input, you can use the magic variable `__custom_tokens:
339/// TokenStream` anywhere in your attribute proc macro.
340///
341/// Here is a full example:
342///
343/// ```ignore
344/// #[derive(Parse)]
345/// struct MyCustomParsing {
346/// foreign_path: syn::Path,
347/// _comma: syn::token::Comma,
348/// custom_path: syn::Path,
349/// }
350///
351/// impl ToTokens for MyCustomParsing {
352/// fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
353/// tokens.extend(self.foreign_path.to_token_stream());
354/// tokens.extend(self._comma.to_token_stream());
355/// tokens.extend(self.custom_path.to_token_stream());
356/// }
357/// }
358///
359/// impl ForeignPath for MyCustomParsing {
360/// fn foreign_path(&self) -> &syn::Path {
361/// &self.foreign_path
362/// }
363/// }
364/// #[import_tokens_attr]
365/// #[with_custom_parsing(MyCustomParsing)]
366/// #[proc_macro_attribute]
367/// pub fn my_attribute(attr: TokenStream, tokens: TokenStream) -> TokenStream {
368/// let external_item = parse_macro_input!(attr as Item);
369/// let attached_item = parse_macro_input!(tokens as Item);
370/// let custom_parsed_item = parse_macro_input!(__custom_tokens as MyCustomParsing);
371/// // ...
372/// }
373/// ```
374///
375/// Usage would look like:
376/// ```ignore
377/// #[my_attribute(foreign::path, some_other::path)]
378/// struct SomeItem {}
379/// ```
380///
381/// This is just an example, you could implement the parsing any way you want, maybe even using
382/// something that isn't initially a `syn::Path` but is transformed into one. The possibilities
383/// are endless.
384///
385/// ## Notes
386///
387/// * See `tests.rs` for more examples.
388/// * Can only be used within a proc macro crate.
389/// * A handy `__source_path: TokenStream` variable is also injected into your proc macro
390/// function definition which provides access to the original `syn::Path` that was provided
391/// as the path for the foreign item before its tokens were imported. You can access this
392/// directly simply by referring to `__source_path`. This should parse to a `syn::Path`.
393/// * When using the custom parsing feature, you can also access the original tokens for the
394/// input attribute within your proc macro body using the magic variable `__custom_tokens`.
395/// For more information and an example see [`macro@with_custom_parsing`].
396#[proc_macro_attribute]
397pub fn import_tokens_attr(attr: TokenStream, tokens: TokenStream) -> TokenStream {
398 match import_tokens_attr_internal(attr, tokens, true) {
399 Ok(tokens) => tokens.into(),
400 Err(err) => err.to_compile_error().into(),
401 }
402}
403
404#[proc_macro_attribute]
405pub fn import_tokens_attr_verbatim(attr: TokenStream, tokens: TokenStream) -> TokenStream {
406 match import_tokens_attr_internal(attr, tokens, false) {
407 Ok(tokens) => tokens.into(),
408 Err(err) => err.to_compile_error().into(),
409 }
410}
411
412/// To be used in tandem with [`#[import_tokens_attr]`](`macro@import_tokens_attr`)
413///
414/// Example:
415/// ```ignore
416/// #[import_tokens_attr]
417/// #[with_custom_parsing(MyCustomParsing)]
418/// #[proc_macro_attribute]
419/// pub fn my_attribute(attr: TokenStream, tokens: TokenStream) -> TokenStream {
420/// let external_item = parse_macro_input!(attr as Item);
421/// let attached_item = parse_macro_input!(tokens as Item);
422/// let custom_parsed_item = parse_macro_input!(__custom_tokens as MyCustomParsing);
423/// // ...
424/// }
425/// ```
426#[proc_macro_attribute]
427pub fn with_custom_parsing(attr: TokenStream, tokens: TokenStream) -> TokenStream {
428 match with_custom_parsing_internal(attr, tokens, "import_tokens_attr") {
429 Ok(tokens) => tokens.into(),
430 Err(err) => err.to_compile_error().into(),
431 }
432}
433
434/// Deprecated: No-op
435#[deprecated(
436 note = "`use_attr` is no longer needed for importing or re-exporting, implementation is no-op, it can be removed safely"
437)]
438#[proc_macro_attribute]
439pub fn use_attr(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
440 tokens
441}
442
443/// Deprecated: No-op
444#[deprecated(
445 note = "`use_proc` is no longer needed for importing or re-exporting, implementation is no-op, it can be removed safely"
446)]
447#[proc_macro_attribute]
448pub fn use_proc(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
449 tokens
450}
451
452/// A helper macro used by [`macro@import_tokens`]. Hidden from docs.
453#[doc(hidden)]
454#[proc_macro]
455pub fn import_tokens_inner(tokens: TokenStream) -> TokenStream {
456 match import_tokens_inner_internal(tokens) {
457 Ok(tokens) => tokens.into(),
458 Err(err) => err.to_compile_error().into(),
459 }
460}
461
462/// A helper macro used by [`macro@forward_tokens`]. Hidden from docs.
463#[doc(hidden)]
464#[proc_macro]
465pub fn forward_tokens_inner(tokens: TokenStream) -> TokenStream {
466 match forward_tokens_inner_internal(tokens) {
467 Ok(tokens) => tokens.into(),
468 Err(err) => err.to_compile_error().into(),
469 }
470}