Skip to content

Implement declarative (macro_rules!) attribute macros (RFC 3697) #144579

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

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

joshtriplett
Copy link
Member

@joshtriplett joshtriplett commented Jul 28, 2025

This implements RFC 3697, "Declarative (macro_rules!) attribute macros".

I would suggest reading this commit-by-commit. This first introduces the
feature gate, then adds parsing for attribute rules (doing nothing with them),
then adds the ability to look up and apply macro_rules! attributes by path,
then adds support for local attributes, then adds a test, and finally makes
various improvements to errors.

@rustbot
Copy link
Collaborator

rustbot commented Jul 28, 2025

r? @petrochenkov

rustbot has assigned @petrochenkov.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 28, 2025
@rustbot
Copy link
Collaborator

rustbot commented Jul 28, 2025

The Miri subtree was changed

cc @rust-lang/miri

This handles various kinds of errors, but does not allow applying the
attributes yet.
Add infrastructure to apply an attribute macro given argument tokens and
body tokens.

Teach the resolver to consider `macro_rules` macros when looking for an
attribute via a path.

This does not yet handle local `macro_rules` attributes.
Teach the resolver to consider `macro_rules` macros when looking for a
local attribute. When looking for an attribute and considering a
`macro_rules` macro, load the macro in order to see if it has attribute
rules.
Test macros via path and local macros.
Now that `macro_rules` macros can define attribute rules, make sure
error messages account for that.
…ttribute

Avoid saying "a declarative macro cannot be used as an attribute macro";
instead, say that the macro has no `attr` rules.
@rust-log-analyzer

This comment was marked as outdated.

@jdonszelmann jdonszelmann self-assigned this Jul 28, 2025
@theemathas
Copy link
Contributor

Here's an edge case that probably needs a test.

Code and error
#![feature(macro_attr)]

#[foo]
macro_rules! foo {
    attr() { $($body:tt)* } => {
        $($body)*
    }
}
use foo;
warning: unused macro definition: `foo`
 --> src/lib.rs:4:14
  |
4 | macro_rules! foo {
  |              ^^^
  |
  = note: `#[warn(unused_macros)]` on by default

error: cannot determine resolution for the attribute macro `foo`
 --> src/lib.rs:3:3
  |
3 | #[foo]
  |   ^^^
  |
  = note: import resolution is stuck, try simplifying macro imports

warning: `foo` (lib) generated 1 warning
error: could not compile `foo` (lib) due to 1 previous error; 1 warning emitted

@rust-log-analyzer

This comment was marked as outdated.

@joshtriplett
Copy link
Member Author

@theemathas I can't reproduce that here. I get a different error instead:

error: cannot find attribute `foo` in this scope
  --> $DIR/macro-rules-attr-cycle.rs:4:3
   |
LL | #[foo]
   |   ^^^
LL | macro_rules! foo {
   |              --- `foo` exists, but has no `attr` rules

This isn't the right error message, but the error is getting caught.

(Suggestions welcome for how to catch and handle this case better.)

@rust-log-analyzer

This comment was marked as outdated.

@joshtriplett
Copy link
Member Author

@theemathas I looked into your test case more. I managed to find a way to reproduce the error you got; it only reproduces if there aren't other errors. So, I can reproduce it with the following test:

#![crate_type = "lib"]
#![feature(macro_attr)]

#[attr]
macro_rules! attr {
    attr() {} => {}
}

#[attr]
struct S;

This gives:

error: cannot find attribute `attr` in this scope
  --> /home/josh/src/rust/tests/ui/macros/macro-rules-attr-apply-to-self.rs:4:3
   |
LL | #[attr]
   |   ^^^^
LL | macro_rules! attr {
   |              ---- `attr` exists, but has no `attr` rules

error: cannot determine resolution for the attribute macro `attr`
  --> /home/josh/src/rust/tests/ui/macros/macro-rules-attr-apply-to-self.rs:9:3
   |
LL | #[attr]
   |   ^^^^
   |
   = note: import resolution is stuck, try simplifying macro imports

The second error seems reasonable for attempting to invoke an attribute on itself.

The first error also largely seems fine, except that it shouldn't say has no `attr` rules when the real problem is a cycle.

Note that "cannot find" is the same error that you get if you try this:

mac! {
    macro_rules! mac {
        {} => {}
    }
}

mac! {
    struct S;
}

This gives:

error: cannot find macro `mac` in this scope
 --> src/main.rs:1:1
  |
1 | mac! {
  | ^^^

error: cannot find macro `mac` in this scope
 --> src/main.rs:7:1
  |
7 | mac! {
  | ^^^

So, I think "cannot find ... in this scope" is an acceptable error, albeit one we might be able to improve. I think the main problem here is the spurious `attr` exists, but has no `attr` rules.

@rust-log-analyzer
Copy link
Collaborator

The job pr-check-2 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[RUSTC-TIMING] rustc_public test:false 5.657
error: using `.clone()` on a ref-counted pointer
   --> compiler/rustc_resolve/src/macros.rs:838:85
    |
838 |                 Some(MacroKind::Attr) if let Some(ref ext) = macro_data.attr_ext => ext.clone(),
    |                                                                                     ^^^^^^^^^^^ help: try: `Arc::<rustc_expand::base::SyntaxExtension>::clone(&ext)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
    = note: requested on the command line with `-D clippy::clone-on-ref-ptr`

error: using `.clone()` on a ref-counted pointer
   --> compiler/rustc_resolve/src/macros.rs:839:22
    |
839 |                 _ => macro_data.ext.clone(),
    |                      ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Arc::<rustc_expand::base::SyntaxExtension>::clone(&macro_data.ext)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr

[RUSTC-TIMING] rustc_resolve test:false 8.357
error: could not compile `rustc_resolve` (lib) due to 2 previous errors

`failed_to_match_macro` and `failed_to_match_macro_attr` had a fair bit
in common. Unify the two, and handle the differences with conditionals.
…cursively

This allows a macro attribute to implement default arguments by
reapplying itself with the defaults filled in, for instance.
@joshtriplett
Copy link
Member Author

I think this is ready for review. The error messages could always be better, and there are additional cases that need handling (notably glob imports), but I think it would help to have some review before tackling those.

@theemathas

This comment was marked as resolved.

@petrochenkov
Copy link
Contributor

(I started looking today, but will continue only on ~Monday.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants