nop_macros/lib.rs
1//! Procedural macros that do nothing, allowing attributes to be used as metadata.
2//!
3//! Any code marked with the [`#[nop_macros::nop]`](macro@crate::nop) attribute
4//! is passed through without modification.
5//! Similarly, using [`#[derive(nop_macros::NopDerive)]`](macro@crate::NopDerive)
6//! on a type does not generate any code or implement any traits.
7//!
8//! Useful for source-code only metadata,
9//! readable by tools but ignored at runtime.
10//! I use this for configuring build-time source generation (see ["Use Cases"](#use-cases) below).
11//!
12//! All macros can be used multiple times and renamed with `pub use`.
13//!
14//! # Example
15//! ```
16//! pub use nop_macros::nop as example1;
17//! pub use nop_macros::nop_noargs as example2;
18//! pub use nop_macros::nop as example3;
19//! pub use nop_macros::NopDerive as DeriveExample1;
20//! pub use nop_macros::NopDerive as DeriveExample2;
21//!
22//! #[example1(ignored)]
23//! #[example2]
24//! pub fn foo() -> i32 {
25//! 7
26//! }
27//!
28//! #[example2]
29//! const BAR: u32 = 42;
30//!
31//! #[example3(781)]
32//! pub fn baz() -> i32 {
33//! 18
34//! }
35//!
36//! assert_eq!(foo(), 7);
37//! assert_eq!(BAR, 42);
38//! assert_eq!(baz(), 18);
39//!
40//! #[derive(Debug, DeriveExample1, DeriveExample2)]
41//! struct Foo {
42//! bar: String
43//! }
44//! ```
45//!
46//! # Use Cases
47//! I use this for generating source-code at build-time,
48//! configured by attributes on rust code.
49//!
50//! An example of a build-time source generator that parses rust code is [cbindgen](https://github.com/mozilla/cbindgen).
51//! However, that project uses comments for configruation, which I want to avoid.
52//!
53//! Build-time source generation is significantly more powerful and flexible than procedural macros.
54//! Maktlad has a [blog post](https://matklad.github.io/2021/02/14/for-the-love-of-macros.html) on this subject.
55//!
56//! I recomend [genco](https://docs.rs/genco) instead of [quote](https://docs.rs/quote) for build-time quasiquoting.
57//! It preserves whitespace and supports languages besides rust.
58//!
59//! I still use [syn](https://docs.rs/syn) for parsing rust code.
60extern crate proc_macro;
61
62use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
63
64/// A derive macro that does nothing,
65/// and performs no validation.
66///
67/// Does not accept any attributes by default,
68/// but those can be added using [`#[nop_macros::nop]`](macro@crate::nop).
69///
70/// ## Example
71/// ```
72/// #[derive(nop_macros::NopDerive)]
73/// struct Foo {
74/// foo: u32
75/// }
76///
77/// // can be done twice!
78/// #[derive(nop_macros::NopDerive, nop_macros::NopDerive)]
79/// enum Bar {
80/// Baz(String),
81/// Nop,
82/// }
83/// ```
84#[proc_macro_derive(NopDerive)]
85pub fn nop_derive(_input: TokenStream) -> TokenStream {
86 TokenStream::default()
87}
88
89/// A procedural macro that does nothing to the annotated code,
90/// and ignores any arguments.
91///
92/// Any annotated code is passed through unchanged.
93///
94/// # Example
95/// ```
96/// #[nop_macros::nop(this_is_ignored, 7)]
97/// pub fn foo() -> i32 {
98/// 7
99/// }
100/// assert_eq!(foo(), 7);
101/// ```
102#[proc_macro_attribute]
103pub fn nop(_attr: TokenStream, item: TokenStream) -> TokenStream {
104 item
105}
106
107/// A procedural macro that does nothing to the annotated code,
108/// and accepts no arguments.
109///
110/// Gives an error if arguments are passed.
111///
112/// Aside from requiring no arguments,
113/// this behaves identically to the `#[nop_macros::nop]` attribute.
114///
115/// # Example
116/// ```
117/// #[nop_macros::nop_noargs]
118/// fn foo() -> i32 {
119/// 7
120/// }
121/// assert_eq!(foo(), 7);
122/// ```
123///
124/// ### Error if args passed
125/// ```compile_fail
126/// #[nop_macros::nop_noargs(foo)]
127/// fn foo() -> i32 {
128/// 7
129/// }
130/// assert_eq!(foo(), 7);
131/// ```
132#[proc_macro_attribute]
133pub fn nop_noargs(attr: TokenStream, item: TokenStream) -> TokenStream {
134 if let Some(first) = attr.into_iter().next() {
135 compile_error("Cannot give arguments to #[nop_noargs]", first.span())
136 .into_iter()
137 .chain(item)
138 .collect()
139 } else {
140 item
141 }
142}
143
144fn compile_error(msg: &str, span: Span) -> TokenStream {
145 let set_span = |mut tree: TokenTree| {
146 tree.set_span(span);
147 tree
148 };
149 [
150 TokenTree::Ident(Ident::new("compile_error", span)),
151 set_span(TokenTree::Punct(Punct::new('!', Spacing::Alone))),
152 set_span(TokenTree::Group(Group::new(
153 Delimiter::Parenthesis,
154 std::iter::once(set_span(TokenTree::Literal(Literal::string(msg)))).collect(),
155 ))),
156 set_span(TokenTree::Punct(Punct::new(';', Spacing::Alone))),
157 ]
158 .into_iter()
159 .collect()
160}