From 661912e99829ad66bbe2b13e6a4864c7a4982419 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 20 Sep 2024 18:10:12 -0700 Subject: [PATCH 01/32] Declarative `macro_rules!` attribute macros --- text/0000-declarative-attribute-macros.md | 119 ++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 text/0000-declarative-attribute-macros.md diff --git a/text/0000-declarative-attribute-macros.md b/text/0000-declarative-attribute-macros.md new file mode 100644 index 00000000000..6231e396862 --- /dev/null +++ b/text/0000-declarative-attribute-macros.md @@ -0,0 +1,119 @@ +- Feature Name: `declarative_attribute_macros` +- Start Date: 2024-09-20 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Support defining `macro_rules!` macros that work as attribute macros. + +# Motivation +[motivation]: #motivation + +Many crates provide attribute macros. Today, this requires defining proc +macros, in a separate crate, typically with several additional dependencies +adding substantial compilation time, and typically guarded by a feature that +users need to remember to enable. + +However, many common cases of attribute macros don't require any more power +than an ordinary `macro_rules!` macro. Supporting these common cases would +allow many crates to avoid defining proc macros, reduce dependencies and +compilation time, and provide these macros unconditionally without requiring a +the user to enable a feature. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +When defining a `macro_rules!` macro, you can prefix some of the macro's rules +with `attribute(...) =>` to allow using the macro as an attribute. The +arguments to the attribute, if any, are parsed by the *MacroMatcher* in the +first set of parentheses; the second *MacroMatcher* parses the entire construct +the attribute was applied to. The resulting macro will work anywhere an +attribute currently works. + +```rust +macro_rules! main { + attribute() => ($func:item) => { make_async_main!($func) }; + attribute(threads = $threads:literal) => ($func:item) => { make_async_main!($threads, $func) }; +} + +#[main] +async fn main() { ... } + +#[main(threads = 42)] +async fn main() { ... } +``` + +Attribute macros defined using `macro_rules!` follow the same scoping rules as +any other macro, and may be invoked by any path that resolves to them. + +An attribute macro must not require itself for resolution, either directly or +indirectly (e.g. applied to a containing module or item). + +Note that a single macro can have both attribute and non-attribute rules. +Attribute invocations can only match the attribute rules, and non-attribute +invocations can only match the non-attribute rules. + +For simplicity, an attribute macro may not recursively invoke its attribute +rules; to recurse, invoke a non-attribute rule or another macro. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The grammar for macros is extended as follows: + +> _MacroRule_ :\ +>    ( `attribute` _MacroMatcher_ `=>` )? _MacroMatcher_ `=>` _MacroTranscriber_ + +The first _MacroMatcher_ matches the attribute's arguments, which will be an +empty token tree if not present. The second _MacroMatcher_ matches the entire +construct the attribute was applied to, receiving precisely what a +proc-macro-based attribute would in the same place. + +This grammar addition is backwards compatible: previously, a _MacroRule_ could +only start with `(`, `[`, or `{`, so the parser can easily distinguish the +identifier `attribute`. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Adding this feature will allow many crates in the ecosystem to drop their proc +macro crates and corresponding dependencies, and decrease their build times. + +Crates could instead define `macro_rules!` macros and encourage users to invoke +them using existing syntax like `macroname! { ... }`. This would provide the +same functionality, but would not support the same syntax people are accustomed +to, and could not maintain semver compatibility with an existing +proc-macro-based attribute. + +We could require the `!` in attribute macros (`#[myattr!]` or similar). +However, proc-macro-based attribute macros do not require this, and this would +not allow declarative attribute macros to be fully compatible with +proc-macro-based attribute macros. + +Many macros will want to parse their arguments and separately parse the +construct they're applied to, rather than a combinatorial explosion of both. +This problem is not unique to attribute macros. In both cases, the standard +solution is to parse one while carrying along the other. + +We could use `attr` rather than `attribute`. Rust usually avoids abbreviating +except for the most common constructs; however, this can occur repeatedly in +multiple rules, so it may make sense to abbreviate it. + +# Prior art +[prior-art]: #prior-art + +We have had proc-macro-based attribute macros for a long time, and the +ecosystem makes extensive use of them. + +The [`macro_rules_attribute`](https://crates.io/crates/macro_rules_attribute) +crate defines proc macros that allow invoking declarative macros as attributes, +demonstrating a demand for this. This feature would allow defining such +attributes without requiring proc macros at all, and would support the same +invocation syntax as a proc macro. + +# Future possibilities +[future-possibilities]: #future-possibilities + +We should provide a way to define `derive` macros declaratively, as well. From 6c9944ba0073fd514716e5fec76ef36d6fb0c7a4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 20 Sep 2024 18:17:53 -0700 Subject: [PATCH 02/32] RFC 3697 --- ...attribute-macros.md => 3697-declarative-attribute-macros.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename text/{0000-declarative-attribute-macros.md => 3697-declarative-attribute-macros.md} (98%) diff --git a/text/0000-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md similarity index 98% rename from text/0000-declarative-attribute-macros.md rename to text/3697-declarative-attribute-macros.md index 6231e396862..e611c623013 100644 --- a/text/0000-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -1,6 +1,6 @@ - Feature Name: `declarative_attribute_macros` - Start Date: 2024-09-20 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#3697](https://github.com/rust-lang/rfcs/pull/3697) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary From 6fd82e895f56936dbff1f2d572e7a931e7147bbe Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 20 Sep 2024 18:37:50 -0700 Subject: [PATCH 03/32] Attribute macros are active --- text/3697-declarative-attribute-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index e611c623013..a6f91231d42 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -75,6 +75,10 @@ This grammar addition is backwards compatible: previously, a _MacroRule_ could only start with `(`, `[`, or `{`, so the parser can easily distinguish the identifier `attribute`. +Attribute macros declared using `macro_rules!` are +[active](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes), +just like those declared using proc macros. + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 2b4989b47f00cce2e04ba8f4bea30749a056effb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 20 Sep 2024 18:47:47 -0700 Subject: [PATCH 04/32] Fix typo --- text/3697-declarative-attribute-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index a6f91231d42..3ff26da7c3b 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -19,7 +19,7 @@ users need to remember to enable. However, many common cases of attribute macros don't require any more power than an ordinary `macro_rules!` macro. Supporting these common cases would allow many crates to avoid defining proc macros, reduce dependencies and -compilation time, and provide these macros unconditionally without requiring a +compilation time, and provide these macros unconditionally without requiring the user to enable a feature. # Guide-level explanation From 258608e5e504b5a8d39b1eaea4cbf4682207a436 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 21 Sep 2024 14:22:52 -0700 Subject: [PATCH 05/32] Remove the extra `=>` --- text/3697-declarative-attribute-macros.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 3ff26da7c3b..3de8c349167 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -26,7 +26,7 @@ the user to enable a feature. [guide-level-explanation]: #guide-level-explanation When defining a `macro_rules!` macro, you can prefix some of the macro's rules -with `attribute(...) =>` to allow using the macro as an attribute. The +with `attribute(...)` to allow using the macro as an attribute. The arguments to the attribute, if any, are parsed by the *MacroMatcher* in the first set of parentheses; the second *MacroMatcher* parses the entire construct the attribute was applied to. The resulting macro will work anywhere an @@ -34,8 +34,8 @@ attribute currently works. ```rust macro_rules! main { - attribute() => ($func:item) => { make_async_main!($func) }; - attribute(threads = $threads:literal) => ($func:item) => { make_async_main!($threads, $func) }; + attribute() ($func:item) => { make_async_main!($func) }; + attribute(threads = $threads:literal) ($func:item) => { make_async_main!($threads, $func) }; } #[main] @@ -64,7 +64,7 @@ rules; to recurse, invoke a non-attribute rule or another macro. The grammar for macros is extended as follows: > _MacroRule_ :\ ->    ( `attribute` _MacroMatcher_ `=>` )? _MacroMatcher_ `=>` _MacroTranscriber_ +>    ( `attribute` _MacroMatcher_ )? _MacroMatcher_ `=>` _MacroTranscriber_ The first _MacroMatcher_ matches the attribute's arguments, which will be an empty token tree if not present. The second _MacroMatcher_ matches the entire @@ -101,6 +101,9 @@ construct they're applied to, rather than a combinatorial explosion of both. This problem is not unique to attribute macros. In both cases, the standard solution is to parse one while carrying along the other. +We could include another `=>` or other syntax between the first and second +macro matchers. + We could use `attr` rather than `attribute`. Rust usually avoids abbreviating except for the most common constructs; however, this can occur repeatedly in multiple rules, so it may make sense to abbreviate it. From fa7820faa3381661c5399496cf032b98d660b6c0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 22 Sep 2024 00:11:37 -0700 Subject: [PATCH 06/32] Abbreviate to `attr`, with `cfg_attr` as precedent --- text/3697-declarative-attribute-macros.md | 27 ++++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 3de8c349167..60c2038a2ba 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -26,7 +26,7 @@ the user to enable a feature. [guide-level-explanation]: #guide-level-explanation When defining a `macro_rules!` macro, you can prefix some of the macro's rules -with `attribute(...)` to allow using the macro as an attribute. The +with `attr(...)` to allow using the macro as an attribute. The arguments to the attribute, if any, are parsed by the *MacroMatcher* in the first set of parentheses; the second *MacroMatcher* parses the entire construct the attribute was applied to. The resulting macro will work anywhere an @@ -34,8 +34,8 @@ attribute currently works. ```rust macro_rules! main { - attribute() ($func:item) => { make_async_main!($func) }; - attribute(threads = $threads:literal) ($func:item) => { make_async_main!($threads, $func) }; + attr() ($func:item) => { make_async_main!($func) }; + attr(threads = $threads:literal) ($func:item) => { make_async_main!($threads, $func) }; } #[main] @@ -51,12 +51,12 @@ any other macro, and may be invoked by any path that resolves to them. An attribute macro must not require itself for resolution, either directly or indirectly (e.g. applied to a containing module or item). -Note that a single macro can have both attribute and non-attribute rules. -Attribute invocations can only match the attribute rules, and non-attribute -invocations can only match the non-attribute rules. +Note that a single macro can have both `attr` and non-`attr` rules. Attribute +invocations can only match the `attr` rules, and non-attribute invocations can +only match the non-`attr` rules. -For simplicity, an attribute macro may not recursively invoke its attribute -rules; to recurse, invoke a non-attribute rule or another macro. +For simplicity, an attribute macro may not recursively invoke its `attr` rules; +to recurse, invoke a non-`attr` rule or another macro. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -64,7 +64,7 @@ rules; to recurse, invoke a non-attribute rule or another macro. The grammar for macros is extended as follows: > _MacroRule_ :\ ->    ( `attribute` _MacroMatcher_ )? _MacroMatcher_ `=>` _MacroTranscriber_ +>    ( `attr` _MacroMatcher_ )? _MacroMatcher_ `=>` _MacroTranscriber_ The first _MacroMatcher_ matches the attribute's arguments, which will be an empty token tree if not present. The second _MacroMatcher_ matches the entire @@ -73,7 +73,7 @@ proc-macro-based attribute would in the same place. This grammar addition is backwards compatible: previously, a _MacroRule_ could only start with `(`, `[`, or `{`, so the parser can easily distinguish the -identifier `attribute`. +identifier `attr`. Attribute macros declared using `macro_rules!` are [active](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes), @@ -104,9 +104,10 @@ solution is to parse one while carrying along the other. We could include another `=>` or other syntax between the first and second macro matchers. -We could use `attr` rather than `attribute`. Rust usually avoids abbreviating -except for the most common constructs; however, this can occur repeatedly in -multiple rules, so it may make sense to abbreviate it. +We could use `attribute` rather than `attr`. Rust usually avoids abbreviating +except for the most common constructs; however, `cfg_attr` provides precedent +for this abbreviation, and `attr` appears repeatedly in multiple rules which +motivates abbreviating it. # Prior art [prior-art]: #prior-art From 0e4e46c7385f09394a814e65d2b60a1ef0a2b0b9 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 24 Sep 2024 09:42:54 -0700 Subject: [PATCH 07/32] Mention `$crate` --- text/3697-declarative-attribute-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 60c2038a2ba..976b25eb901 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -85,6 +85,10 @@ just like those declared using proc macros. Adding this feature will allow many crates in the ecosystem to drop their proc macro crates and corresponding dependencies, and decrease their build times. +This will also give attribute macros access to the `$crate` mechanism to refer +to the defining crate, which is simpler than mechanisms currently used in proc +macros to achieve the same goal. + Crates could instead define `macro_rules!` macros and encourage users to invoke them using existing syntax like `macroname! { ... }`. This would provide the same functionality, but would not support the same syntax people are accustomed From 0fea4754489c2210ee528b5ed13b4b36bf386427 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 24 Sep 2024 09:43:10 -0700 Subject: [PATCH 08/32] Caching --- text/3697-declarative-attribute-macros.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 976b25eb901..6436e9ab6d9 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -89,6 +89,9 @@ This will also give attribute macros access to the `$crate` mechanism to refer to the defining crate, which is simpler than mechanisms currently used in proc macros to achieve the same goal. +Macros defined this way can more easily support caching, as they cannot depend +on arbitrary unspecified inputs. + Crates could instead define `macro_rules!` macros and encourage users to invoke them using existing syntax like `macroname! { ... }`. This would provide the same functionality, but would not support the same syntax people are accustomed From b11f474586b7bb066e773739442d7a57fa339cae Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 25 Sep 2024 12:09:02 -0700 Subject: [PATCH 09/32] Add an alternative way of marking and handling attr/non-attr macros --- text/3697-declarative-attribute-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 6436e9ab6d9..693decce9a5 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -108,6 +108,10 @@ construct they're applied to, rather than a combinatorial explosion of both. This problem is not unique to attribute macros. In both cases, the standard solution is to parse one while carrying along the other. +Instead of or in addition to marking the individual rules, we could mark the +whole macro with `#[attribute_macro]` or similar, and allow having an attribute +macro and a non-attribute macro with the same name. + We could include another `=>` or other syntax between the first and second macro matchers. From d13e2baca32e84d14644db2054f83d95705201d8 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 25 Sep 2024 12:33:21 -0700 Subject: [PATCH 10/32] Clarify justification for having a single macro support attr and non-attr --- text/3697-declarative-attribute-macros.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 693decce9a5..ba184c5627c 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -108,6 +108,11 @@ construct they're applied to, rather than a combinatorial explosion of both. This problem is not unique to attribute macros. In both cases, the standard solution is to parse one while carrying along the other. +We could leave out support for writing a function-like macro and an attribute +macro with the same name. However, this would prevent crates from preserving +backwards compatibility when adding attribute support to an existing +function-like macro. + Instead of or in addition to marking the individual rules, we could mark the whole macro with `#[attribute_macro]` or similar, and allow having an attribute macro and a non-attribute macro with the same name. From 0900f9f733599f34d455450b96c45c3508d1b519 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 25 Sep 2024 12:34:38 -0700 Subject: [PATCH 11/32] Add notes about backwards compatibility of adding `attr` rules --- text/3697-declarative-attribute-macros.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index ba184c5627c..8c66cc5b6a1 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -53,7 +53,8 @@ indirectly (e.g. applied to a containing module or item). Note that a single macro can have both `attr` and non-`attr` rules. Attribute invocations can only match the `attr` rules, and non-attribute invocations can -only match the non-`attr` rules. +only match the non-`attr` rules. This allows adding `attr` rules to an existing +macro without breaking backwards compatibility. For simplicity, an attribute macro may not recursively invoke its `attr` rules; to recurse, invoke a non-`attr` rule or another macro. @@ -79,6 +80,8 @@ Attribute macros declared using `macro_rules!` are [active](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes), just like those declared using proc macros. +Adding `attr` rules to an existing macro is a semver-compatible change. + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 26e69698840715a0ebe6a200fd77ebd450dbeffc Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 25 Sep 2024 20:27:17 -0700 Subject: [PATCH 12/32] Note that both `MacroMatcher`s share the same namespace --- text/3697-declarative-attribute-macros.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 8c66cc5b6a1..e21a51a8d9e 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -72,6 +72,11 @@ empty token tree if not present. The second _MacroMatcher_ matches the entire construct the attribute was applied to, receiving precisely what a proc-macro-based attribute would in the same place. +Only a rule matching both the arguments to the attribute and the construct the +attribute was applied to will apply. Note that the captures in both +`MacroMatcher`s share the same namespace; attempting to use the same name for +two captures will give a "duplicate matcher binding" error. + This grammar addition is backwards compatible: previously, a _MacroRule_ could only start with `(`, `[`, or `{`, so the parser can easily distinguish the identifier `attr`. From 5cfb62021690b8cc76562df957ce1bf8dbf6edbe Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 25 Sep 2024 20:36:00 -0700 Subject: [PATCH 13/32] Document the cases of no arguments and empty arguments --- text/3697-declarative-attribute-macros.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index e21a51a8d9e..a27da3a71e8 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -68,9 +68,10 @@ The grammar for macros is extended as follows: >    ( `attr` _MacroMatcher_ )? _MacroMatcher_ `=>` _MacroTranscriber_ The first _MacroMatcher_ matches the attribute's arguments, which will be an -empty token tree if not present. The second _MacroMatcher_ matches the entire -construct the attribute was applied to, receiving precisely what a -proc-macro-based attribute would in the same place. +empty token tree if either not present (`#[myattr]`) or empty (`#[myattr()]`). +The second _MacroMatcher_ matches the entire construct the attribute was +applied to, receiving precisely what a proc-macro-based attribute would in the +same place. Only a rule matching both the arguments to the attribute and the construct the attribute was applied to will apply. Note that the captures in both From 10f011dd3cbbac115670c246e9eac374f5f45265 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 25 Sep 2024 20:37:40 -0700 Subject: [PATCH 14/32] Future possibilities: better error reporting --- text/3697-declarative-attribute-macros.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index a27da3a71e8..d88efda57f1 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -150,3 +150,6 @@ invocation syntax as a proc macro. [future-possibilities]: #future-possibilities We should provide a way to define `derive` macros declaratively, as well. + +We should provide a way for `macro_rules!` macros to provide better error +reporting, with spans, rather than just pointing to the macro. From 1cfec3d6048a46b48f98e349199029aefc78c724 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 30 Sep 2024 13:22:52 -0700 Subject: [PATCH 15/32] Add drawbacks section mentioning impact on crate maintainers --- text/3697-declarative-attribute-macros.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index d88efda57f1..692a2748ae3 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -88,6 +88,20 @@ just like those declared using proc macros. Adding `attr` rules to an existing macro is a semver-compatible change. +# Drawbacks +[drawbacks]: #drawbacks + +This feature will not be sufficient for *all* uses of proc macros in the +ecosystem, and its existence may create social pressure for crate maintainers +to switch even if the result is harder to maintain. + +Before stabilizing this feature, we should receive feedback from crate +maintainers, and potentially make further improvements to `macro_rules` to make +it easier to use for their use cases. This feature will provide motivation to +evaluate many new use cases that previously weren't written using +`macro_rules`, and we should consider quality-of-life improvements to better +support those use cases. + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From b7d4e0685b84418c00f1e132f70727e3beae21ed Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 30 Sep 2024 13:38:04 -0700 Subject: [PATCH 16/32] Expand future work --- text/3697-declarative-attribute-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 692a2748ae3..9f98a2e8be1 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -167,3 +167,7 @@ We should provide a way to define `derive` macros declaratively, as well. We should provide a way for `macro_rules!` macros to provide better error reporting, with spans, rather than just pointing to the macro. + +As people test this feature and run into limitations of `macro_rules!` parsing, +we should consider additional features to make this easier to use for various +use cases. From 01103635011048bafef9d41060121fd82a78ef35 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 30 Sep 2024 21:58:42 -0700 Subject: [PATCH 17/32] Future possibilities: consider the case of multiple attributes in any order --- text/3697-declarative-attribute-macros.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 9f98a2e8be1..17443e8193e 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -171,3 +171,12 @@ reporting, with spans, rather than just pointing to the macro. As people test this feature and run into limitations of `macro_rules!` parsing, we should consider additional features to make this easier to use for various use cases. + +Some use cases involve multiple attribute macros that users expect to be able +to apply in any order. For instance, `#[test]` and `#[should_panic]` can appear +on the same function in any order. Implementing that via this mechanism for +attribute macros would require making both of those attributes into macros that +both do all the parsing regardless of which got invoked first, likely by +invoking a common helper. We should consider if we consider that mechanism +sufficient, or if we should provide another mechanism for a set of related +attribute macros to appear in any order. From 23cd82f613c12e2dc0bb85fad3c968bc689395ea Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 1 Oct 2024 18:25:23 -0700 Subject: [PATCH 18/32] Recursion --- text/3697-declarative-attribute-macros.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 17443e8193e..0dca5726282 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -160,6 +160,14 @@ demonstrating a demand for this. This feature would allow defining such attributes without requiring proc macros at all, and would support the same invocation syntax as a proc macro. +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +Is an attribute macro allowed to recursively invoke itself by emitting the +attriute in its output? If there is no technical issue with allowing this, then +we should do so, to allow simle recursion (e.g. handling defaults by invoking +the same rule as if they were explicitly specified). + # Future possibilities [future-possibilities]: #future-possibilities From 9fbf852ad32629bd6410df852f8571d1591f39c4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 1 Oct 2024 18:25:57 -0700 Subject: [PATCH 19/32] Clarify recursive invocation Co-authored-by: Mads Marquart --- text/3697-declarative-attribute-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 0dca5726282..f0d0f90451b 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -56,7 +56,7 @@ invocations can only match the `attr` rules, and non-attribute invocations can only match the non-`attr` rules. This allows adding `attr` rules to an existing macro without breaking backwards compatibility. -For simplicity, an attribute macro may not recursively invoke its `attr` rules; +For simplicity, no special syntax is given to allow an attribute macro to recursively invoke its `attr` rules; to recurse, invoke a non-`attr` rule or another macro. # Reference-level explanation From 95838c4cc88da2055c08dfef7e36c13e20ef6eaf Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 1 Oct 2024 22:54:42 -0700 Subject: [PATCH 20/32] Fix typo Co-authored-by: Jacob Lifshay --- text/3697-declarative-attribute-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index f0d0f90451b..be358f111da 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -165,7 +165,7 @@ invocation syntax as a proc macro. Is an attribute macro allowed to recursively invoke itself by emitting the attriute in its output? If there is no technical issue with allowing this, then -we should do so, to allow simle recursion (e.g. handling defaults by invoking +we should do so, to allow simple recursion (e.g. handling defaults by invoking the same rule as if they were explicitly specified). # Future possibilities From ff26c823c6e0b66f2f549e96106dcf5feb341b65 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 2 Oct 2024 10:13:44 -0700 Subject: [PATCH 21/32] Add unresolved question to make sure we don't have awful error messages --- text/3697-declarative-attribute-macros.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index be358f111da..6ff7eeb29e4 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -168,6 +168,9 @@ attriute in its output? If there is no technical issue with allowing this, then we should do so, to allow simple recursion (e.g. handling defaults by invoking the same rule as if they were explicitly specified). +Before stabilizing this feature, we should make sure it doesn't produce wildly +worse error messages in common cases. + # Future possibilities [future-possibilities]: #future-possibilities From 25ab0003bb942f8d87bae34d51675e9400299687 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 22 Oct 2024 05:36:35 -0700 Subject: [PATCH 22/32] Word-wrapping --- text/3697-declarative-attribute-macros.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 6ff7eeb29e4..c5d74e5d8ec 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -56,8 +56,9 @@ invocations can only match the `attr` rules, and non-attribute invocations can only match the non-`attr` rules. This allows adding `attr` rules to an existing macro without breaking backwards compatibility. -For simplicity, no special syntax is given to allow an attribute macro to recursively invoke its `attr` rules; -to recurse, invoke a non-`attr` rule or another macro. +For simplicity, no special syntax is given to allow an attribute macro to +recursively invoke its `attr` rules; to recurse, invoke a non-`attr` rule or +another macro. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 66ac59fbc19e825dbf00e0be4ca168f832da9adb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 22 Oct 2024 05:53:09 -0700 Subject: [PATCH 23/32] Support unsafe attributes --- text/3697-declarative-attribute-macros.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index c5d74e5d8ec..24cef3b8e37 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -60,13 +60,17 @@ For simplicity, no special syntax is given to allow an attribute macro to recursively invoke its `attr` rules; to recurse, invoke a non-`attr` rule or another macro. +An `attr` rule may be prefixed with `unsafe`. Invoking an attribute macro in a +way that makes use of a rule declared with `unsafe attr` requires the unsafe +attribute syntax `#[unsafe(attribute_name)]`. + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation The grammar for macros is extended as follows: > _MacroRule_ :\ ->    ( `attr` _MacroMatcher_ )? _MacroMatcher_ `=>` _MacroTranscriber_ +>    ( `unsafe`? `attr` _MacroMatcher_ )? _MacroMatcher_ `=>` _MacroTranscriber_ The first _MacroMatcher_ matches the attribute's arguments, which will be an empty token tree if either not present (`#[myattr]`) or empty (`#[myattr()]`). @@ -79,9 +83,16 @@ attribute was applied to will apply. Note that the captures in both `MacroMatcher`s share the same namespace; attempting to use the same name for two captures will give a "duplicate matcher binding" error. +An attribute macro invocation that uses an `unsafe attr` rule will produce an +error if invoked without using the `unsafe` attribute syntax. An attribute +macro invocation that uses an `attr` rule will trigger the "unused unsafe" lint +if invoked using the `unsafe` attribute syntax. A single attribute macro may +have both `attr` and `unsafe attr` rules, such as if only some invocations are +unsafe. + This grammar addition is backwards compatible: previously, a _MacroRule_ could -only start with `(`, `[`, or `{`, so the parser can easily distinguish the -identifier `attr`. +only start with `(`, `[`, or `{`, so the parser can easily distinguish rules +that start with `attr` or `unsafe. Attribute macros declared using `macro_rules!` are [active](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes), @@ -180,6 +191,11 @@ We should provide a way to define `derive` macros declaratively, as well. We should provide a way for `macro_rules!` macros to provide better error reporting, with spans, rather than just pointing to the macro. +We may want to provide more fine-grained control over the requirement for +`unsafe`, to make it easier for attribute macros to be safe in some +circumstances and unsafe in others (e.g. unsafe only if a given parameter is +provided). + As people test this feature and run into limitations of `macro_rules!` parsing, we should consider additional features to make this easier to use for various use cases. From 5fdd0d8af103ebbbdff5f1ff962863909705a25e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 22 Oct 2024 05:53:15 -0700 Subject: [PATCH 24/32] Copy a drawback to the unresolved questions section --- text/3697-declarative-attribute-macros.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 24cef3b8e37..5c61b0e7dd8 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -183,6 +183,13 @@ the same rule as if they were explicitly specified). Before stabilizing this feature, we should make sure it doesn't produce wildly worse error messages in common cases. +Before stabilizing this feature, we should receive feedback from crate +maintainers, and potentially make further improvements to `macro_rules` to make +it easier to use for their use cases. This feature will provide motivation to +evaluate many new use cases that previously weren't written using +`macro_rules`, and we should consider quality-of-life improvements to better +support those use cases. + # Future possibilities [future-possibilities]: #future-possibilities From f2eb3ed1cbaf2a3d2bca7fa04df76a1d7cd00813 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 27 Oct 2024 20:08:56 -0700 Subject: [PATCH 25/32] Fix missing backquote --- text/3697-declarative-attribute-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 5c61b0e7dd8..152d1efaa3a 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -92,7 +92,7 @@ unsafe. This grammar addition is backwards compatible: previously, a _MacroRule_ could only start with `(`, `[`, or `{`, so the parser can easily distinguish rules -that start with `attr` or `unsafe. +that start with `attr` or `unsafe`. Attribute macros declared using `macro_rules!` are [active](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes), From 27efc29cbb8f54b40dabbaff1783e3e560faeb1f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 27 Oct 2024 20:12:37 -0700 Subject: [PATCH 26/32] Example --- text/3697-declarative-attribute-macros.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 152d1efaa3a..a8f509da00e 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -172,6 +172,11 @@ demonstrating a demand for this. This feature would allow defining such attributes without requiring proc macros at all, and would support the same invocation syntax as a proc macro. +Some macros in the ecosystem already implement the equivalent of attribute +using declarative macros; for instance, see +[smol-macros](https://crates.io/crates/smol-macros), which provides a `main!` +macro and recommends using it with `macro_rules_attribute::apply`. + # Unresolved questions [unresolved-questions]: #unresolved-questions From e43905ec77fe9049fa6fe54e8e95c357d83ef7dd Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 9 Nov 2024 07:05:53 -0800 Subject: [PATCH 27/32] Document that an attribute macro may invoke another, or itself --- text/3697-declarative-attribute-macros.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index a8f509da00e..8f9d9cb855d 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -56,9 +56,9 @@ invocations can only match the `attr` rules, and non-attribute invocations can only match the non-`attr` rules. This allows adding `attr` rules to an existing macro without breaking backwards compatibility. -For simplicity, no special syntax is given to allow an attribute macro to -recursively invoke its `attr` rules; to recurse, invoke a non-`attr` rule or -another macro. +An attribute macro may emit code containing another attribute, including one +provided by an attribute macro. An attribute macro may use this to recursively +invoke itself. An `attr` rule may be prefixed with `unsafe`. Invoking an attribute macro in a way that makes use of a rule declared with `unsafe attr` requires the unsafe From 31f4eb7a7a5ea84141dc1959c9f67d9b3253e742 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 18 Nov 2024 10:26:02 -0800 Subject: [PATCH 28/32] Document how the compiler should handle using a non-attr macro as an attribute Inspired by https://github.com/rust-lang/rust/issues/132928 --- text/3697-declarative-attribute-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 8f9d9cb855d..3c7d1f91761 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -100,6 +100,10 @@ just like those declared using proc macros. Adding `attr` rules to an existing macro is a semver-compatible change. +If a user invokes a macro as an attribute and that macro does not have any +`attr` rules, the compiler should give a clear error stating that the macro is +not usable as an attribute because it does not have any `attr` rules. + # Drawbacks [drawbacks]: #drawbacks From cd2c9f51d3663b95154e1edcf63dddeb199c9532 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 20 Nov 2024 10:40:42 -0800 Subject: [PATCH 29/32] Future possibility: attribute macros that don't modify the original tokens --- text/3697-declarative-attribute-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 3c7d1f91761..6ab2c80912f 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -224,3 +224,7 @@ both do all the parsing regardless of which got invoked first, likely by invoking a common helper. We should consider if we consider that mechanism sufficient, or if we should provide another mechanism for a set of related attribute macros to appear in any order. + +If it turns out many users of attribute macros want to emit new tokens but +leave the tokens they were applied to unmodified, we may want to have a +convenient mechanism for that. From a7dde80256b1b10b5f0a33bf440eea07b6475cf4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 4 Jun 2025 10:19:05 -0700 Subject: [PATCH 30/32] Copy motivating examples from prior art to motivation --- text/3697-declarative-attribute-macros.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 6ab2c80912f..86f4b658ce2 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -22,6 +22,17 @@ allow many crates to avoid defining proc macros, reduce dependencies and compilation time, and provide these macros unconditionally without requiring the user to enable a feature. +The [`macro_rules_attribute`](https://crates.io/crates/macro_rules_attribute) +crate defines proc macros that allow invoking declarative macros as attributes, +demonstrating a demand for this. This feature would allow defining such +attributes without requiring proc macros at all, and would support the same +invocation syntax as a proc macro. + +Some macros in the ecosystem already implement the equivalent of attribute +using declarative macros; for instance, see +[smol-macros](https://crates.io/crates/smol-macros), which provides a `main!` +macro and recommends using it with `macro_rules_attribute::apply`. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From be3c8237a3a37cb0b623aef5c4462eb45d6354fa Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 12 Jun 2025 13:13:35 -0700 Subject: [PATCH 31/32] Fix typo --- text/3697-declarative-attribute-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index 86f4b658ce2..d73904a9a3b 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -196,7 +196,7 @@ macro and recommends using it with `macro_rules_attribute::apply`. [unresolved-questions]: #unresolved-questions Is an attribute macro allowed to recursively invoke itself by emitting the -attriute in its output? If there is no technical issue with allowing this, then +attribute in its output? If there is no technical issue with allowing this, then we should do so, to allow simple recursion (e.g. handling defaults by invoking the same rule as if they were explicitly specified). From 7594f6216b329e5d5cb7e9717f8433a1ace1e9c5 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 12 Jun 2025 13:15:14 -0700 Subject: [PATCH 32/32] Document an additional unresolved question for evaluation during implementation --- text/3697-declarative-attribute-macros.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3697-declarative-attribute-macros.md b/text/3697-declarative-attribute-macros.md index d73904a9a3b..892e0a39aa4 100644 --- a/text/3697-declarative-attribute-macros.md +++ b/text/3697-declarative-attribute-macros.md @@ -200,6 +200,11 @@ attribute in its output? If there is no technical issue with allowing this, then we should do so, to allow simple recursion (e.g. handling defaults by invoking the same rule as if they were explicitly specified). +Are there any places where we currently allow an attribute, but where +implementation considerations make it difficult to allow a `macro_rules` +attribute? (For instance, places where we currently allow attributes but don't +allow proc-macro attributes.) + Before stabilizing this feature, we should make sure it doesn't produce wildly worse error messages in common cases.