Skip to content

Commit a39632c

Browse files
authored
Rollup merge of rust-lang#39845 - JDemler:master, r=steveklabnik
Add Documentation for Custom Attributes and Error Reporting in Procedural Macros This fixes rust-lang#39821 . I'm not sure if the process of how to access custom attributes should be documented as well. But I feel, that this should rather be documented in `syn`
2 parents c341a65 + 198208b commit a39632c

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

src/doc/book/src/procedural-macros.md

+73
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,76 @@ Hello, World! My name is Waffles
211211
```
212212

213213
We've done it!
214+
215+
## Custom Attributes
216+
217+
In some cases it might make sense to allow users some kind of configuration.
218+
For example, the user might want to overwrite the name that is printed in the `hello_world()` method.
219+
220+
This can be achieved with custom attributes:
221+
222+
```rust,ignore
223+
#[derive(HelloWorld)]
224+
#[HelloWorldName = "the best Pancakes"]
225+
struct Pancakes;
226+
227+
fn main() {
228+
Pancakes::hello_world();
229+
}
230+
```
231+
232+
If we try to compile this though, the compiler will respond with an error:
233+
234+
```bash
235+
error: The attribute `HelloWorldName` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
236+
```
237+
238+
The compiler needs to know that we're handling this attribute and to not respond with an error.
239+
This is done in the `hello-world-derive` crate by adding `attributes` to the `proc_macro_derive` attribute:
240+
241+
```rust,ignore
242+
#[proc_macro_derive(HelloWorld, attributes(HelloWorldName))]
243+
pub fn hello_world(input: TokenStream) -> TokenStream
244+
```
245+
246+
Multiple attributes can be specified that way.
247+
248+
## Raising Errors
249+
250+
Let's assume that we do not want to accept enums as input to our custom derive method.
251+
252+
This condition can be easily checked with the help of `syn`.
253+
But how do we tell the user, that we do not accept enums?
254+
The idiomatic way to report errors in procedural macros is to panic:
255+
256+
```rust,ignore
257+
fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens {
258+
let name = &ast.ident;
259+
// Check if derive(HelloWorld) was specified for a struct
260+
if let syn::Body::Struct(_) = ast.body {
261+
// Yes, this is a struct
262+
quote! {
263+
impl HelloWorld for #name {
264+
fn hello_world() {
265+
println!("Hello, World! My name is {}", stringify!(#name));
266+
}
267+
}
268+
}
269+
} else {
270+
//Nope. This is an Enum. We cannot handle these!
271+
panic!("#[derive(HelloWorld)] is only defined for structs, not for enums!");
272+
}
273+
}
274+
```
275+
276+
If a user now tries to derive `HelloWorld` from an enum they will be greeted with following, hopefully helpful, error:
277+
278+
```bash
279+
error: custom derive attribute panicked
280+
--> src/main.rs
281+
|
282+
| #[derive(HelloWorld)]
283+
| ^^^^^^^^^^
284+
|
285+
= help: message: #[derive(HelloWorld)] is only defined for structs, not for enums!
286+
```

0 commit comments

Comments
 (0)