Skip to content
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

Merged
merged 4 commits into from
Feb 25, 2017
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion src/doc/book/src/procedural-macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,73 @@ Ok so now, let's compile `hello-world`. Executing `cargo run` now yields:
Hello, World! My name is FrenchToast
Hello, World! My name is Waffles
```
## Custom Attributes
In some cases it might make sense to allow users some kind of configuration.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you put a newline above this line, please?

Copy link
Contributor

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.

For our example the user might want to overwrite the name that is printed in the `hello_world()` method.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, ... is enough for introducing the example.


We've done it!
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we handle -> we're handling

This is done in the `hello-world-derive`-crate by adding `attributes` to the `proc_macro_derive` attribute:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hyphen before crate should be replaced by a space.


```rust,ignore
#[proc_macro_derive(HelloWorld, attributes(HelloWorldName))]
pub fn hello_world(input: TokenStream) -> TokenStream
```

Multiple attributes can be specified that way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you delete one of these newlines?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should only be one newline, like this:

Multiple attributes can be specified that way.

## Raising Errors

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(that is, before the heading)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the fixed whitespace issues-commit fixed this issue, correct?


## Raising Errors
Let's assume that we do not want to accept `Enums` as input to our custom derive method.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a newline above this line?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of Enums is over-emphasis, IMO just enums or enumerations without the inline code block is enough.


This condition can be easily checked with the help of `syn`.
But how to we tell the user, that we do not accept `Enums`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • how to -> how do
  • trailing . -> ?

The idiomatic was to report errors in procedural macros is to panic:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was -> way


```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!
```