@@ -6,5 +6,236 @@ The tracking issue for this feature is: [#38356]
6
6
7
7
------------------------
8
8
9
+ This feature flag guards the new procedural macro features as laid out by [ RFC 1566] , which alongside the now-stable
10
+ [ custom derives] , provide stabilizable alternatives to the compiler plugin API (which requires the use of
11
+ perma-unstable internal APIs) for programmatically modifying Rust code at compile-time.
9
12
13
+ The two new procedural macro kinds are:
14
+
15
+ * Function-like procedural macros which are invoked like regular declarative macros, and:
10
16
17
+ * Attribute-like procedural macros which can be applied to any item which built-in attributes can
18
+ be applied to, and which can take arguments in their invocation as well.
19
+
20
+ Additionally, this feature flag implicitly enables the [ ` use_extern_macros ` ] ( language-features/use-extern-macros.html ) feature,
21
+ which allows macros to be imported like any other item with ` use ` statements, as compared to
22
+ applying ` #[macro_use] ` to an ` extern crate ` declaration. It is important to note that procedural macros may
23
+ ** only** be imported in this manner, and will throw an error otherwise.
24
+
25
+ You ** must** declare the ` proc_macro ` feature in both the crate declaring these new procedural macro kinds as well as
26
+ in any crates that use them.
27
+
28
+ ### Common Concepts
29
+
30
+ As with custom derives, procedural macros may only be declared in crates of the ` proc-macro ` type, and must be public
31
+ functions. No other public items may be declared in ` proc-macro ` crates, but private items are fine.
32
+
33
+ To declare your crate as a ` proc-macro ` crate, simply add:
34
+
35
+ ``` toml
36
+ [lib ]
37
+ proc-macro = true
38
+ ```
39
+
40
+ to your ` Cargo.toml ` .
41
+
42
+ Unlike custom derives, however, the name of the function implementing the procedural macro is used directly as the
43
+ procedural macro's name, so choose carefully.
44
+
45
+ Additionally, both new kinds of procedural macros return a ` TokenStream ` which * wholly* replaces the original
46
+ invocation and its input.
47
+
48
+ #### Importing
49
+
50
+ As referenced above, the new procedural macros are not meant to be imported via ` #[macro_use] ` and will throw an
51
+ error if they are. Instead, they are meant to be imported like any other item in Rust, with ` use ` statements:
52
+
53
+ ``` rust,ignore
54
+ #![feature(proc_macro)]
55
+
56
+ // Where `my_proc_macros` is some crate of type `proc_macro`
57
+ extern crate my_proc_macros;
58
+
59
+ // And declares a `#[proc_macro] pub fn my_bang_macro()` at its root.
60
+ use my_proc_macros::my_bang_macro;
61
+
62
+ fn main() {
63
+ println!("{}", my_bang_macro!());
64
+ }
65
+ ```
66
+
67
+ #### Error Reporting
68
+
69
+ Any panics in a procedural macro implementation will be caught by the compiler and turned into an error message pointing
70
+ to the problematic invocation. Thus, it is important to make your panic messages as informative as possible: use
71
+ ` Option::expect ` instead of ` Option::unwrap ` and ` Result::expect ` instead of ` Result::unwrap ` , and inform the user of
72
+ the error condition as unambiguously as you can.
73
+
74
+ #### ` TokenStream `
75
+
76
+ The ` proc_macro::TokenStream ` type is hardcoded into the signatures of procedural macro functions for both input and
77
+ output. It is a wrapper around the compiler's internal representation for a given chunk of Rust code.
78
+
79
+ ### Function-like Procedural Macros
80
+
81
+ These are procedural macros that are invoked like regular declarative macros. They are declared as public functions in
82
+ crates of the ` proc_macro ` type and using the ` #[proc_macro] ` attribute. The name of the declared function becomes the
83
+ name of the macro as it is to be imported and used. The function must be of the kind ` fn(TokenStream) -> TokenStream `
84
+ where the sole argument is the input to the macro and the return type is the macro's output.
85
+
86
+ This kind of macro can expand to anything that is valid for the context it is invoked in, including expressions and
87
+ statements, as well as items.
88
+
89
+ ** Note** : invocations of this kind of macro require a wrapping ` [] ` , ` {} ` or ` () ` like regular macros, but these do not
90
+ appear in the input, only the tokens between them. The tokens between the braces do not need to be valid Rust syntax.
91
+
92
+ <span class =" filename " >my_macro_crate/src/lib.rs</span >
93
+
94
+ ``` rust,ignore
95
+ #![feature(proc_macro)]
96
+
97
+ // This is always necessary to get the `TokenStream` typedef.
98
+ extern crate proc_macro;
99
+
100
+ use proc_macro::TokenStream;
101
+
102
+ #[proc_macro]
103
+ pub fn say_hello(_input: TokenStream) -> TokenStream {
104
+ // This macro will accept any input because it ignores it.
105
+ // To enforce correctness in macros which don't take input,
106
+ // you may want to add `assert!(_input.to_string().is_empty());`.
107
+ "println!(\"Hello, world!\")".parse().unwrap()
108
+ }
109
+ ```
110
+
111
+ <span class =" filename " >my_macro_user/Cargo.toml</span >
112
+
113
+ ``` toml
114
+ [dependencies ]
115
+ my_macro_crate = { path = " <relative path to my_macro_crate>" }
116
+ ```
117
+
118
+ <span class =" filename " >my_macro_user/src/lib.rs</span >
119
+
120
+ ``` rust,ignore
121
+ #![feature(proc_macro)]
122
+
123
+ extern crate my_macro_crate;
124
+
125
+ use my_macro_crate::say_hello;
126
+
127
+ fn main() {
128
+ say_hello!();
129
+ }
130
+ ```
131
+
132
+ As expected, this prints ` Hello, world! ` .
133
+
134
+ ### Attribute-like Procedural Macros
135
+
136
+ These are arguably the most powerful flavor of procedural macro as they can be applied anywhere attributes are allowed.
137
+
138
+ They are declared as public functions in crates of the ` proc-macro ` type, using the ` #[proc_macro_attribute] ` attribute.
139
+ The name of the function becomes the name of the attribute as it is to be imported and used. The function must be of the
140
+ kind ` fn(TokenStream, TokenStream) -> TokenStream ` where:
141
+
142
+ The first argument represents any metadata for the attribute (see [ the reference chapter on attributes] [ refr-attr ] ).
143
+ Only the metadata itself will appear in this argument, for example:
144
+
145
+ * ` #[my_macro] ` will get an empty string.
146
+ * ` #[my_macro = "string"] ` will get ` = "string" ` .
147
+ * ` #[my_macro(ident)] ` will get ` (ident) ` .
148
+ * etc.
149
+
150
+ The second argument is the item that the attribute is applied to. It can be a function, a type definition,
151
+ an impl block, an ` extern ` block, or a module—attribute invocations can take the inner form (` #![my_attr] ` )
152
+ or outer form (` #[my_attr] ` ).
153
+
154
+ The return type is the output of the macro which * wholly* replaces the item it was applied to. Thus, if your intention
155
+ is to merely modify an item, it * must* be copied to the output. The output must be an item; expressions, statements
156
+ and bare blocks are not allowed.
157
+
158
+ There is no restriction on how many items an attribute-like procedural macro can emit as long as they are valid in
159
+ the given context.
160
+
161
+ <span class =" filename " >my_macro_crate/src/lib.rs</span >
162
+
163
+ ``` rust,ignore
164
+ #![feature(proc_macro)]
165
+
166
+ extern crate proc_macro;
167
+
168
+ use proc_macro::TokenStream;
169
+
170
+ /// Adds a `/// ### Panics` docstring to the end of the input's documentation
171
+ ///
172
+ /// Does not assert that its receiver is a function or method.
173
+ #[proc_macro_attribute]
174
+ pub fn panics_note(args: TokenStream, input: TokenStream) -> TokenStream {
175
+ let args = args.to_string();
176
+ let mut input = input.to_string();
177
+
178
+ assert!(args.starts_with("= \""), "`#[panics_note]` requires an argument of the form \
179
+ `#[panics_note = \"panic note here\"]`");
180
+
181
+ // Get just the bare note string
182
+ let panics_note = args.trim_matches(&['=', ' ', '"'][..]);
183
+
184
+ // The input will include all docstrings regardless of where the attribute is placed,
185
+ // so we need to find the last index before the start of the item
186
+ let insert_idx = idx_after_last_docstring(&input);
187
+
188
+ // And insert our `### Panics` note there so it always appears at the end of an item's docs
189
+ input.insert_str(insert_idx, &format!("/// # Panics \n/// {}\n", panics_note));
190
+
191
+ input.parse().unwrap()
192
+ }
193
+
194
+ // `proc-macro` crates can contain any kind of private item still
195
+ fn idx_after_last_docstring(input: &str) -> usize {
196
+ // Skip docstring lines to find the start of the item proper
197
+ input.lines().skip_while(|line| line.trim_left().starts_with("///")).next()
198
+ // Find the index of the first non-docstring line in the input
199
+ // Note: assumes this exact line is unique in the input
200
+ .and_then(|line_after| input.find(line_after))
201
+ // No docstrings in the input
202
+ .unwrap_or(0)
203
+ }
204
+ ```
205
+
206
+ <span class =" filename " >my_macro_user/Cargo.toml</span >
207
+
208
+ ``` toml
209
+ [dependencies ]
210
+ my_macro_crate = { path = " <relative path to my_macro_crate>" }
211
+ ```
212
+
213
+ <span class =" filename " >my_macro_user/src/lib.rs</span >
214
+
215
+ ``` rust,ignore
216
+ #![feature(proc_macro)]
217
+
218
+ extern crate my_macro_crate;
219
+
220
+ use my_macro_crate::panics_note;
221
+
222
+ /// Do the `foo` thing.
223
+ #[panics_note = "Always."]
224
+ pub fn foo() {
225
+ panic!()
226
+ }
227
+ ```
228
+
229
+ Then the rendered documentation for ` pub fn foo ` will look like this:
230
+
231
+ > ` pub fn foo() `
232
+ >
233
+ > ----
234
+ > Do the ` foo ` thing.
235
+ > # Panics
236
+ > Always.
237
+
238
+ [ RFC 1566 ] : https://github.com/rust-lang/rfcs/blob/master/text/1566-proc-macros.md
239
+ [ custom derives ] : https://doc.rust-lang.org/book/procedural-macros.html
240
+ [ rust-lang/rust#41430 ] : https://github.com/rust-lang/rust/issues/41430
241
+ [ refr-attr ] : https://doc.rust-lang.org/reference/attributes.html
0 commit comments