diff --git a/book.toml b/book.toml index fe4b86f1f2..8e85b4a787 100644 --- a/book.toml +++ b/book.toml @@ -34,6 +34,7 @@ use-boolean-and = true "/items/traits.html#object-safety" = "traits.html#dyn-compatibility" "/lifetime-elision.html#static-lifetime-elision" = "lifetime-elision.html#const-and-static-elision" "/macros-by-example.html#path-based-scope" = "macros-by-example.html#the-macro_export-attribute" +"/procedural-macros.html#attribute-macros" = "procedural-macros.html#the-proc_macro_attribute-attribute" "/procedural-macros.html#derive-macros" = "procedural-macros.html#the-proc_macro_derive-attribute" "/procedural-macros.html#function-like-procedural-macros" = "procedural-macros.html#the-proc_macro-attribute" "/runtime.html#the-panic_handler-attribute" = "panic.html#the-panic_handler-attribute" diff --git a/src/attributes.md b/src/attributes.md index 6d9115b43b..26fa10cc42 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -372,7 +372,7 @@ The following is an index of all built-in attributes. [`non_exhaustive`]: attributes/type_system.md#the-non_exhaustive-attribute [`panic_handler`]: panic.md#the-panic_handler-attribute [`path`]: items/modules.md#the-path-attribute -[`proc_macro_attribute`]: procedural-macros.md#attribute-macros +[`proc_macro_attribute`]: procedural-macros.md#the-proc_macro_attribute-attribute [`proc_macro_derive`]: macro.proc.derive [`proc_macro`]: procedural-macros.md#the-proc_macro-attribute [`recursion_limit`]: attributes/limits.md#the-recursion_limit-attribute @@ -385,7 +385,7 @@ The following is an index of all built-in attributes. [`used`]: abi.md#the-used-attribute [`warn`]: attributes/diagnostics.md#lint-check-attributes [`windows_subsystem`]: runtime.md#the-windows_subsystem-attribute -[attribute macros]: procedural-macros.md#attribute-macros +[attribute macros]: procedural-macros.md#the-proc_macro_attribute-attribute [block expressions]: expressions/block-expr.md [built-in attributes]: #built-in-attributes-index [derive macro helper attributes]: procedural-macros.md#derive-macro-helper-attributes diff --git a/src/items/functions.md b/src/items/functions.md index 9819db5ced..468c50574f 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -472,7 +472,7 @@ fn foo_oof(#[some_inert_attribute] arg: u8) { [`cfg`]: ../conditional-compilation.md#the-cfg-attribute [`cfg_attr`]: ../conditional-compilation.md#the-cfg_attr-attribute [lint check attributes]: ../attributes/diagnostics.md#lint-check-attributes -[procedural macro attributes]: ../procedural-macros.md#attribute-macros +[procedural macro attributes]: macro.proc.attribute [testing attributes]: ../attributes/testing.md [`cold`]: ../attributes/codegen.md#the-cold-attribute [`inline`]: ../attributes/codegen.md#the-inline-attribute diff --git a/src/names/namespaces.md b/src/names/namespaces.md index 116c7a1fe6..072cb640d2 100644 --- a/src/names/namespaces.md +++ b/src/names/namespaces.md @@ -134,7 +134,7 @@ It is still an error for a [`use` import] to shadow another macro, regardless of [Associated const declarations]: ../items/associated-items.md#associated-constants [Associated function declarations]: ../items/associated-items.md#associated-functions-and-methods [Associated type declarations]: ../items/associated-items.md#associated-types -[Attribute macros]: ../procedural-macros.md#attribute-macros +[Attribute macros]: ../procedural-macros.md#the-proc_macro_attribute-attribute [attributes]: ../attributes.md [bang-style macros]: ../macros.md [Block labels]: ../expressions/loop-expr.md#labelled-block-expressions diff --git a/src/procedural-macros.md b/src/procedural-macros.md index be6ff36111..4395e3db95 100644 --- a/src/procedural-macros.md +++ b/src/procedural-macros.md @@ -240,91 +240,102 @@ A helper attribute for a derive macro is declared by adding its identifier to th > } > ``` + r[macro.proc.attribute] -## Attribute macros +## The `proc_macro_attribute` attribute r[macro.proc.attribute.intro] -*Attribute macros* define new [outer attributes][attributes] which can be -attached to [items], including items in [`extern` blocks], inherent and trait -[implementations], and [trait definitions]. - -r[macro.proc.attribute.def] -Attribute macros are defined by a [public] [function] with the -`proc_macro_attribute` [attribute] that has a signature of `(TokenStream, -TokenStream) -> TokenStream`. The first [`TokenStream`] is the delimited token -tree following the attribute's name, not including the outer delimiters. If -the attribute is written as a bare attribute name, the attribute -[`TokenStream`] is empty. The second [`TokenStream`] is the rest of the [item] -including other [attributes] on the [item]. The returned [`TokenStream`] -replaces the [item] with an arbitrary number of [items]. +The *`proc_macro_attribute` [attribute][attributes]* defines an *attribute macro* which can be used as an [outer attribute][attributes]. + +> [!EXAMPLE] +> This attribute macro takes the input stream and emits it as-is, effectively being a no-op attribute. +> +> +> ```rust,ignore +> # #![crate_type = "proc-macro"] +> # extern crate proc_macro; +> # use proc_macro::TokenStream; +> +> #[proc_macro_attribute] +> pub fn return_as_is(_attr: TokenStream, item: TokenStream) -> TokenStream { +> item +> } +> ``` + +> [!EXAMPLE] +> This shows, in the output of the compiler, the stringified [`TokenStream`s] that attribute macros see. +> +> +> ```rust,ignore +> // my-macro/src/lib.rs +> # extern crate proc_macro; +> # use proc_macro::TokenStream; +> #[proc_macro_attribute] +> pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream { +> println!("attr: \"{attr}\""); +> println!("item: \"{item}\""); +> item +> } +> ``` +> +> +> ```rust,ignore +> // src/lib.rs +> extern crate my_macro; +> +> use my_macro::show_streams; +> +> // Example: Basic function. +> #[show_streams] +> fn invoke1() {} +> // out: attr: "" +> // out: item: "fn invoke1() {}" +> +> // Example: Attribute with input. +> #[show_streams(bar)] +> fn invoke2() {} +> // out: attr: "bar" +> // out: item: "fn invoke2() {}" +> +> // Example: Multiple tokens in the input. +> #[show_streams(multiple => tokens)] +> fn invoke3() {} +> // out: attr: "multiple => tokens" +> // out: item: "fn invoke3() {}" +> +> // Example: Delimiters in the input. +> #[show_streams { delimiters }] +> fn invoke4() {} +> // out: attr: "delimiters" +> // out: item: "fn invoke4() {}" +> ``` + +r[macro.proc.attribute.syntax] +The `proc_macro_attribute` attribute uses the [MetaWord] syntax. + +r[macro.proc.attribute.allowed-positions] +The `proc_macro_attribute` attribute may only be applied to a `pub` function of type `fn(TokenStream, TokenStream) -> TokenStream` where [`TokenStream`] comes from the [`proc_macro` crate]. It must have the ["Rust" ABI][items.fn.extern]. No other function qualifiers are allowed. It must be located in the root of the crate. + +r[macro.proc.attribute.duplicates] +The `proc_macro_attribute` attribute may only be specified once on a function. r[macro.proc.attribute.namespace] -The `proc_macro_attribute` attribute defines the attribute in the [macro namespace] in the root of the crate. +The `proc_macro_attribute` attribute defines the attribute in the [macro namespace] in the root of the crate with the same name as the function. -For example, this attribute macro takes the input stream and returns it as is, -effectively being the no-op of attributes. +r[macro.proc.attribute.use-positions] +Attribute macros can only be used on: - -```rust,ignore -# #![crate_type = "proc-macro"] -# extern crate proc_macro; -# use proc_macro::TokenStream; +- [Items] +- Items in [`extern` blocks] +- Inherent and trait [implementations] +- [Trait definitions] -#[proc_macro_attribute] -pub fn return_as_is(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} -``` +r[macro.proc.attribute.behavior] +The first [`TokenStream`] parameter is the delimited token tree following the attribute's name but not including the outer delimiters. If the applied attribute contains only the attribute name or the attribute name followed by empty delimiters, the [`TokenStream`] is empty. -This following example shows the stringified [`TokenStream`s] that the attribute -macros see. The output will show in the output of the compiler. The output is -shown in the comments after the function prefixed with "out:". - - -```rust,ignore -// my-macro/src/lib.rs -# extern crate proc_macro; -# use proc_macro::TokenStream; - -#[proc_macro_attribute] -pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream { - println!("attr: \"{attr}\""); - println!("item: \"{item}\""); - item -} -``` +The second [`TokenStream`] is the rest of the [item], including other [attributes] on the [item]. - -```rust,ignore -// src/lib.rs -extern crate my_macro; - -use my_macro::show_streams; - -// Example: Basic function -#[show_streams] -fn invoke1() {} -// out: attr: "" -// out: item: "fn invoke1() {}" - -// Example: Attribute with input -#[show_streams(bar)] -fn invoke2() {} -// out: attr: "bar" -// out: item: "fn invoke2() {}" - -// Example: Multiple tokens in the input -#[show_streams(multiple => tokens)] -fn invoke3() {} -// out: attr: "multiple => tokens" -// out: item: "fn invoke3() {}" - -// Example: -#[show_streams { delimiters }] -fn invoke4() {} -// out: attr: "delimiters" -// out: item: "fn invoke4() {}" -``` +The item to which the attribute is applied is replaced by the zero or more items in the returned [`TokenStream`]. r[macro.proc.token] ## Declarative macro tokens and procedural macro tokens @@ -393,7 +404,7 @@ Note that neither declarative nor procedural macros support doc comment tokens (e.g. `/// Doc`), so they are always converted to token streams representing their equivalent `#[doc = r"str"]` attributes when passed to macros. -[Attribute macros]: #attribute-macros +[Attribute macros]: #the-proc_macro_attribute-attribute [Cargo's build scripts]: ../cargo/reference/build-scripts.html [Derive macros]: macro.proc.derive [Function-like macros]: #the-proc_macro-attribute