From 5189c4f8c10d0b8cae1fb66e5b4e0717af30d60d Mon Sep 17 00:00:00 2001 From: Jakob Demler Date: Wed, 15 Feb 2017 12:59:01 +0100 Subject: [PATCH 1/4] custom attributes and error reporting docs for procedural macros --- src/doc/book/src/procedural-macros.md | 70 ++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/doc/book/src/procedural-macros.md b/src/doc/book/src/procedural-macros.md index d286c3b7bdc63..ef8b638dc1c60 100644 --- a/src/doc/book/src/procedural-macros.md +++ b/src/doc/book/src/procedural-macros.md @@ -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. +For our example the user might want to overwrite the name that is printed in the `hello_world()` method. -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: + +``` +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. +This is done in the `hello-world-derive`-crate by adding `attributes` to the `proc_macro_derive` attribute: + +```rust,ignore +#[proc_macro_derive(HelloWorld, attributes(HelloWorldName))] +pub fn hello_world(input: TokenStream) -> TokenStream +``` + +Multiple attributes can be specified that way. + + +## Raising Errors +Let's assume that we do not want to accept `Enums` as input to our custom derive method. + +This condition can be easily checked with the help of `syn`. +But how to we tell the user, that we do not accept `Enums`. +The idiomatic was to report errors in procedural macros is to panic: + +```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: + +``` +error: custom derive attribute panicked + --> src/main.rs + | + | #[derive(HelloWorld)] + | ^^^^^^^^^^ + | + = help: message: #[derive(HelloWorld)] is only defined for structs, not for enums! +``` From 9a39994ee3a6f825318af9d6ceb23ee3187c7888 Mon Sep 17 00:00:00 2001 From: Jakob Demler Date: Wed, 15 Feb 2017 15:31:52 +0100 Subject: [PATCH 2/4] add bash to error-messages --- src/doc/book/src/procedural-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/book/src/procedural-macros.md b/src/doc/book/src/procedural-macros.md index ef8b638dc1c60..e9777f9992f74 100644 --- a/src/doc/book/src/procedural-macros.md +++ b/src/doc/book/src/procedural-macros.md @@ -226,7 +226,7 @@ fn main() { 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) ``` @@ -270,7 +270,7 @@ fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { 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 | From 97451996e69a0bb8d98cfe77bd904d8033419ab9 Mon Sep 17 00:00:00 2001 From: Jakob Demler Date: Wed, 15 Feb 2017 21:21:31 +0100 Subject: [PATCH 3/4] fixed whitespace issues --- src/doc/book/src/procedural-macros.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/book/src/procedural-macros.md b/src/doc/book/src/procedural-macros.md index e9777f9992f74..468a8f904a45a 100644 --- a/src/doc/book/src/procedural-macros.md +++ b/src/doc/book/src/procedural-macros.md @@ -209,7 +209,9 @@ 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. For our example the user might want to overwrite the name that is printed in the `hello_world()` method. @@ -240,8 +242,8 @@ pub fn hello_world(input: TokenStream) -> TokenStream Multiple attributes can be specified that way. - ## Raising Errors + Let's assume that we do not want to accept `Enums` as input to our custom derive method. This condition can be easily checked with the help of `syn`. From 198208be0e818d99e42281568a2eec305175c6c9 Mon Sep 17 00:00:00 2001 From: Jakob Demler Date: Sun, 19 Feb 2017 18:15:44 +0100 Subject: [PATCH 4/4] Fixed some small issues --- src/doc/book/src/procedural-macros.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/doc/book/src/procedural-macros.md b/src/doc/book/src/procedural-macros.md index 468a8f904a45a..e22ef85546bdb 100644 --- a/src/doc/book/src/procedural-macros.md +++ b/src/doc/book/src/procedural-macros.md @@ -210,12 +210,15 @@ 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. -For our example the user might want to overwrite the name that is printed in the `hello_world()` method. +For example, the user might want to overwrite the name that is printed in the `hello_world()` method. This can be achieved with custom attributes: + ```rust,ignore #[derive(HelloWorld)] #[HelloWorldName = "the best Pancakes"] @@ -232,8 +235,8 @@ If we try to compile this though, the compiler will respond with an error: 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. -This is done in the `hello-world-derive`-crate by adding `attributes` to the `proc_macro_derive` attribute: +The compiler needs to know that we're handling this attribute and to not respond with an error. +This is done in the `hello-world-derive` crate by adding `attributes` to the `proc_macro_derive` attribute: ```rust,ignore #[proc_macro_derive(HelloWorld, attributes(HelloWorldName))] @@ -244,11 +247,11 @@ Multiple attributes can be specified that way. ## Raising Errors -Let's assume that we do not want to accept `Enums` as input to our custom derive method. +Let's assume that we do not want to accept enums as input to our custom derive method. This condition can be easily checked with the help of `syn`. -But how to we tell the user, that we do not accept `Enums`. -The idiomatic was to report errors in procedural macros is to panic: +But how do we tell the user, that we do not accept enums? +The idiomatic way to report errors in procedural macros is to panic: ```rust,ignore fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { @@ -257,14 +260,14 @@ fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { if let syn::Body::Struct(_) = ast.body { // Yes, this is a struct quote! { - impl HelloWorld for #name { + 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! + //Nope. This is an Enum. We cannot handle these! panic!("#[derive(HelloWorld)] is only defined for structs, not for enums!"); } }