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

Declarative macro_rules! attribute macros #3697

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
661912e
Declarative `macro_rules!` attribute macros
joshtriplett Sep 21, 2024
6c9944b
RFC 3697
joshtriplett Sep 21, 2024
6fd82e8
Attribute macros are active
joshtriplett Sep 21, 2024
2b4989b
Fix typo
joshtriplett Sep 21, 2024
258608e
Remove the extra `=>`
joshtriplett Sep 21, 2024
fa7820f
Abbreviate to `attr`, with `cfg_attr` as precedent
joshtriplett Sep 22, 2024
0e4e46c
Mention `$crate`
joshtriplett Sep 24, 2024
0fea475
Caching
joshtriplett Sep 24, 2024
b11f474
Add an alternative way of marking and handling attr/non-attr macros
joshtriplett Sep 25, 2024
d13e2ba
Clarify justification for having a single macro support attr and non-…
joshtriplett Sep 25, 2024
0900f9f
Add notes about backwards compatibility of adding `attr` rules
joshtriplett Sep 25, 2024
26e6969
Note that both `MacroMatcher`s share the same namespace
joshtriplett Sep 26, 2024
5cfb620
Document the cases of no arguments and empty arguments
joshtriplett Sep 26, 2024
10f011d
Future possibilities: better error reporting
joshtriplett Sep 26, 2024
1cfec3d
Add drawbacks section mentioning impact on crate maintainers
joshtriplett Sep 30, 2024
b7d4e06
Expand future work
joshtriplett Sep 30, 2024
0110363
Future possibilities: consider the case of multiple attributes in any…
joshtriplett Oct 1, 2024
23cd82f
Recursion
joshtriplett Oct 2, 2024
9fbf852
Clarify recursive invocation
joshtriplett Oct 2, 2024
95838c4
Fix typo
joshtriplett Oct 2, 2024
ff26c82
Add unresolved question to make sure we don't have awful error messages
joshtriplett Oct 2, 2024
25ab000
Word-wrapping
joshtriplett Oct 22, 2024
66ac59f
Support unsafe attributes
joshtriplett Oct 22, 2024
5fdd0d8
Copy a drawback to the unresolved questions section
joshtriplett Oct 22, 2024
f2eb3ed
Fix missing backquote
joshtriplett Oct 28, 2024
27efc29
Example
joshtriplett Oct 28, 2024
e43905e
Document that an attribute macro may invoke another, or itself
joshtriplett Nov 9, 2024
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
62 changes: 57 additions & 5 deletions text/3697-declarative-attribute-macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ 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;
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
Expand All @@ -67,9 +68,15 @@ The grammar for macros is extended as follows:
> &nbsp;&nbsp; ( `attr` _MacroMatcher_ )<sup>?</sup> _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
`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
Expand All @@ -79,6 +86,22 @@ 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.

# 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

Expand Down Expand Up @@ -108,6 +131,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.
Expand All @@ -132,7 +160,31 @@ 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
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
the same rule as if they were explicitly specified).

# Future possibilities
[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.

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.