macro_attr_2018/lib.rs
1// Copyright (c) 2016 macro-attr contributors.
2// Copyright (c) 2020 Warlock <internalmike@gmail.com>.
3// Copyright (c) 2020 Clint Armstrong <clint@clintarmstrong.net>.
4//
5// Licensed under the MIT license (see LICENSE or <http://opensource.org
6// /licenses/MIT>) or the Apache License, Version 2.0 (see LICENSE of
7// <http://www.apache.org/licenses/LICENSE-2.0>), at your option. All
8// files in the project carrying such notice may not be copied, modified,
9// or distributed except according to those terms.
10
11#![deny(warnings)]
12#![doc(test(attr(deny(warnings))))]
13#![doc(test(attr(allow(dead_code))))]
14#![doc(test(attr(allow(unused_variables))))]
15#![doc(test(attr(allow(unused_macros))))]
16#![doc(test(attr(allow(unknown_lints, unused_macro_rules))))]
17
18//! This crate provides the `macro_attr!` macro that enables the use of custom,
19//! macro-based attributes and derivations.
20//!
21//! The `macro_attr!` macro should be used to wrap an entire *single* item
22//! (`enum`, `struct`, *etc.*) declaration, including its attributes (both `derive` and others).
23//! All attributes and derivations which whose names end with `!` will be assumed
24//! to be implemented by macros, and treated accordingly.
25//!
26//! ```rust
27//! use macro_attr_2018::macro_attr;
28//!
29//! // Define some traits to be derived.
30//!
31//! trait TypeName {
32//! fn type_name() -> &'static str;
33//! }
34//!
35//! trait ReprType {
36//! type Repr;
37//! }
38//!
39//! // Define macros which derive implementations of these macros.
40//!
41//! macro_rules! TypeName {
42//! // We can support any kind of item we want.
43//! (() $vis:vis enum $name:ident $($tail:tt)+) => { TypeName! { @impl $name } };
44//! (() $vis:vis struct $name:ident $($tail:tt)+) => { TypeName! { @impl $name } };
45//!
46//! // Inner rule to cut down on repetition.
47//! (@impl $name:ident) => {
48//! impl TypeName for $name {
49//! fn type_name() -> &'static str { stringify!($name) }
50//! }
51//! };
52//! }
53//!
54//! macro_rules! ReprType {
55//! // Note that we use a "derivation argument" here for the `$repr` type.
56//! (($repr:ty) $vis:vis enum $name:ident $($tail:tt)+) => {
57//! impl ReprType for $name {
58//! type Repr = $repr;
59//! }
60//! };
61//! }
62//!
63//! // Derive.
64//!
65//! macro_attr! {
66//! #[derive(TypeName!, ReprType!(u16))]
67//! #[repr(u16)]
68//! enum SomeEnum { A, B, C, D }
69//! }
70//!
71//! # fn main() {
72//! assert_eq!(SomeEnum::type_name(), "SomeEnum");
73//! assert_eq!(SomeEnum::A as <SomeEnum as ReprType>::Repr, 0u16);
74//! # }
75//! ```
76
77#![no_std]
78
79#[doc=include_str!("../README.md")]
80type _DocTestReadme = ();
81
82/// When given an item definition, including its attributes, this macro parses said attributes
83/// and dispatches any derivations suffixed with `!` to user-defined macros.
84///
85/// This allows multiple macros to process the same item.
86///
87/// Given the following input:
88///
89/// ```ignore
90/// #[derive(Copy, Name!(args...), Clone, Another!, Debug)]
91/// struct Foo;
92/// ```
93///
94/// `macro_attr!` will expand to the equivalent of:
95///
96/// ```ignore
97/// #[derive(Copy, Clone, Debug)]
98/// struct Foo;
99///
100/// Name!((args...) struct Foo;);
101/// Another!(() struct Foo;);
102/// ```
103///
104/// Note that macro derives may be mixed with regular derives,
105/// or put in their own `#[derive(...)]` attribute.
106/// Also note that macro derive invocations are *not* passed the other attributes on the item;
107/// input will consist of the arguments provided to the derivation (*i.e.* `(args...)`
108/// in this example), the item's visibility (if any), and the item definition itself.
109///
110/// A macro derivation invoked *without* arguments will be treated as though
111/// it was invoked with empty parentheses. *i.e.* `#[derive(Name!)]` is equivalent to `#[derive(Name!())]`.
112///
113/// A derivation macro may expand to any number of new items derived from the provided input.
114#[macro_export]
115macro_rules! macro_attr {
116 (
117 $(#[$($attrs:tt)+])*
118 $(pub $(($($vis:tt)+))?)? enum $($it:tt)+
119 ) => {
120 $crate::macro_attr_impl! {
121 @split_attrs [$(pub $(($($vis)+))?)? enum $($it)+]
122 [] []
123 [$([$($attrs)+])*]
124 }
125 };
126 (
127 $(#[$($attrs:tt)+])*
128 $(pub $(($($vis:tt)+))?)? struct $($it:tt)+
129 ) => {
130 $crate::macro_attr_impl! {
131 @split_attrs [$(pub $(($($vis)+))?)? struct $($it)+]
132 [] []
133 [$([$($attrs)+])*]
134 }
135 };
136 (
137 $(#[$($attrs:tt)+])*
138 $(pub $(($($vis:tt)+))?)? trait $($it:tt)+
139 ) => {
140 $crate::macro_attr_impl! {
141 @split_attrs [$(pub ($($vis)+))? trait $($it)+]
142 [] []
143 [$([$($attrs)+])*]
144 }
145 };
146 (
147 $(#[$($attrs:tt)+])*
148 $vis:vis $keyword:ident $($it:tt)+
149 ) => {
150 $crate::macro_attr_impl! {
151 @split_attrs [$vis $keyword $($it)+]
152 [] []
153 [$([$($attrs)+])*]
154 }
155 };
156}
157
158#[doc(hidden)]
159#[macro_export]
160macro_rules! macro_attr_impl {
161 (
162 @split_attrs [$($it:tt)+]
163 [$($derive_attrs:tt)*] [$([$other_attrs:meta])*]
164 [[derive($($derive_attr:tt)+)] $([$($attrs:tt)+])*]
165 ) => {
166 $crate::macro_attr_impl! {
167 @split_attrs [$($it)+]
168 [$($derive_attrs)* [$($derive_attr)+]]
169 [$([$other_attrs])*]
170 [$([$($attrs)+])*]
171 }
172 };
173 (
174 @split_attrs [$($it:tt)+]
175 [$($derive_attrs:tt)*] [$([$other_attrs:meta])*]
176 [[$attr:meta] $([$($attrs:tt)+])*]
177 ) => {
178 $crate::macro_attr_impl! {
179 @split_attrs [$($it)+]
180 [$($derive_attrs)*]
181 [$([$other_attrs])* [$attr]]
182 [$([$($attrs)+])*]
183 }
184 };
185 (
186 @split_attrs [$($it:tt)+]
187 [$($derive_attrs:tt)*] [$([$other_attrs:meta])*]
188 []
189 ) => {
190 $crate::macro_attr_impl! {
191 @split_derive_attrs [$($it)+] [$([$other_attrs])*]
192 [] []
193 [$($derive_attrs)*]
194 }
195 };
196 (
197 @split_derive_attrs [$($it:tt)+] [$([$other_attrs:meta])*]
198 [$($macro_derives:tt)*] [$($std_derives:tt)*]
199 [
200 [$macro_derive:ident ! $(($($macro_derive_args:tt)*))? $(, $($other_inner_derives:tt)*)?]
201 $([$($other_derives:tt)*])*
202 ]
203 ) => {
204 $crate::macro_attr_impl! {
205 @split_derive_attrs [$($it)+] [$([$other_attrs])*]
206 [
207 $($macro_derives)*
208 [$macro_derive ( $($($macro_derive_args)*)? )]
209 ]
210 [
211 $($std_derives)*
212 ]
213 [
214 $([$($other_inner_derives)*])?
215 $([$($other_derives)*])*
216 ]
217 }
218 };
219 (
220 @split_derive_attrs [$($it:tt)+] [$([$other_attrs:meta])*]
221 [$($macro_derives:tt)*] [$($std_derives:tt)*]
222 [
223 [$std_derive:ident $(($($std_derive_args:tt)*))? $(, $($other_inner_derives:tt)*)?]
224 $([$($other_derives:tt)*])*
225 ]
226 ) => {
227 $crate::macro_attr_impl! {
228 @split_derive_attrs [$($it)+] [$([$other_attrs])*]
229 [
230 $($macro_derives)*
231 ]
232 [
233 $($std_derives)*
234 #[derive($std_derive $(($($std_derive_args)*))?)]
235 ]
236 [
237 $([$($other_inner_derives)*])?
238 $([$($other_derives)*])*
239 ]
240 }
241 };
242 (
243 @split_derive_attrs [$($it:tt)+] [$([$other_attrs:meta])*]
244 [$($macro_derives:tt)*] [$($std_derives:tt)*]
245 [
246 []
247 $([$($other_derives:tt)*])*
248 ]
249 ) => {
250 $crate::macro_attr_impl! {
251 @split_derive_attrs [$($it)+] [$([$other_attrs])*]
252 [
253 $($macro_derives)*
254 ]
255 [
256 $($std_derives)*
257 ]
258 [
259 $([$($other_derives)*])*
260 ]
261 }
262 };
263 (
264 @split_derive_attrs [$($it:tt)+] [$([$other_attrs:meta])*]
265 [$([$macro_derive:ident ( $($macro_derive_args:tt)* )])*]
266 [$($std_derives:tt)*]
267 []
268 ) => {
269 $crate::macro_attr_impl! {
270 @as_item
271 $($std_derives)*
272 $(#[$other_attrs])*
273 $($it)+
274 }
275 $crate::macro_attr_impl! {
276 @expand [$($it)+]
277 [$([$macro_derive ( $($macro_derive_args)* )])*]
278 }
279 };
280 (
281 @expand [$($it:tt)+]
282 [
283 [$macro_derive:ident ( $($macro_derive_args:tt)* )]
284 $([$other_macro_derives:ident ( $($other_macro_derives_args:tt)* )])*
285 ]
286 ) => {
287 $macro_derive! {
288 ( $($macro_derive_args)* )
289 $($it)+
290 }
291 $crate::macro_attr_impl! {
292 @expand [$($it)+]
293 [$([$other_macro_derives ( $($other_macro_derives_args)* )])*]
294 }
295 };
296 (
297 @expand [$($it:tt)+]
298 []
299 ) => {
300 };
301 (@as_item $($i:item)*) => {$($i)*};
302}