-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Documentation for Custom Attributes and Error Reporting in Procedural Macros #39845
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -210,4 +210,74 @@ Hello, World! My name is FrenchToast | |
Hello, World! My name is Waffles | ||
``` | ||
|
||
We've done it! | ||
## Custom Attributes | ||
|
||
In some cases it might make sense to allow users some kind of configuration. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you put a newline above this line, please? |
||
For our example the user might want to overwrite the name that is printed in the `hello_world()` method. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
This can be achieved with custom attributes: | ||
```rust,ignore | ||
#[derive(HelloWorld)] | ||
#[HelloWorldName = "the best Pancakes"] | ||
struct Pancakes; | ||
|
||
fn main() { | ||
Pancakes::hello_world(); | ||
} | ||
``` | ||
|
||
If we try to compile this though, the compiler will respond with an error: | ||
|
||
```bash | ||
error: The attribute `HelloWorldName` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) | ||
``` | ||
|
||
The compiler needs to know that we handle this attribute and to not respond with an error. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
This is done in the `hello-world-derive`-crate by adding `attributes` to the `proc_macro_derive` attribute: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The hyphen before |
||
|
||
```rust,ignore | ||
#[proc_macro_derive(HelloWorld, attributes(HelloWorldName))] | ||
pub fn hello_world(input: TokenStream) -> TokenStream | ||
``` | ||
|
||
Multiple attributes can be specified that way. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you delete one of these newlines? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean the newline before the heading or after the code block? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there should only be one newline, like this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (that is, before the heading) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the |
||
## Raising Errors | ||
|
||
Let's assume that we do not want to accept `Enums` as input to our custom derive method. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a newline above this line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of |
||
|
||
This condition can be easily checked with the help of `syn`. | ||
But how to we tell the user, that we do not accept `Enums`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
The idiomatic was to report errors in procedural macros is to panic: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
```rust,ignore | ||
fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { | ||
let name = &ast.ident; | ||
// Check if derive(HelloWorld) was specified for a struct | ||
if let syn::Body::Struct(_) = ast.body { | ||
// Yes, this is a struct | ||
quote! { | ||
impl HelloWorld for #name { | ||
fn hello_world() { | ||
println!("Hello, World! My name is {}", stringify!(#name)); | ||
} | ||
} | ||
} | ||
} else { | ||
//Nope. This is an Enum. We cannot handle these! | ||
panic!("#[derive(HelloWorld)] is only defined for structs, not for enums!"); | ||
} | ||
} | ||
``` | ||
|
||
If a user now tries to derive `HelloWorld` from an enum they will be greeted with following, hopefully helpful, error: | ||
|
||
```bash | ||
error: custom derive attribute panicked | ||
--> src/main.rs | ||
| | ||
| #[derive(HelloWorld)] | ||
| ^^^^^^^^^^ | ||
| | ||
= help: message: #[derive(HelloWorld)] is only defined for structs, not for enums! | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing this for the next section seems to make the previous section truncated, IMO removal is not needed.