Skip to content

Warn about unused macro arms. #73576

@iago-lito

Description

@iago-lito
Contributor

rustc now warns about unused macros. With the following code:

macro_rules! used {
    () => {};
}

macro_rules! unused { // <- This should be cleaned up.
    () => {};
}

fn main() {
    used!();
} 

we get the warning:

warning: unused macro definition
 --> src/main.rs:5:1
  |
5 | / macro_rules! unused {
6 | |     () => {};
7 | | }
  | |_^
  |
  = note: `#[warn(unused_macros)]` on by default

This is really helpful while cleaning up a large meta-programming mess.

However, it does not warn about unused macro arms, so the following:

macro_rules! used_macro {
    (used_arm) => {};
    (unused_arm) => {}; // <-- This should be cleaned up.
}

fn main() {
    used_macro!(used_arm);
} 

yields no warning yet.

Following #34938, I open this issue for discussing this feature. Is this something desirable to have rustc warn about unused macro arms?

Activity

lcnr

lcnr commented on Jun 21, 2020

@lcnr
Contributor

I personally think that we should not warn for unused macro arms by default, as I tend to fairly often write the following:

macro_rules! used_macro {
    ($($e:expr),*) => {};
    ($($e:expr),*,) => {}; // <-- This is unused.
}

fn main() {
    used_macro!(7, 13);
} 

To not have to worry about this in case I ever change a macro invocation to

use_macro!(
    7,
    13,
);
iago-lito

iago-lito commented on Jun 21, 2020

@iago-lito
ContributorAuthor

@lcnr This is convenient indeed :) But I don't think it should be blocking, for several reasons:

  • First, ($($e:expr),*$(,)?) already works fine in this situation:
macro_rules! used_macro {
    ($($e:expr),*$(,)?) => {}; // Only one used arm.
}

fn main() {
    used_macro!(7, 13); // Without the comma.
    used_macro!(
        7,
        13, // With the comma.
        );
}
  • Or, if this alternate solution is still not acceptable for some reason:

    • Then this problem is still only a matter of style, so it could maybe be special-cased and/or handled by rustfmt?

    • In any case, like we have the option to opt-out from this warning on a local basis for unused variables (let _unused_var = ();) with a _ prefix, we might consider an option to opt-out from this warning for unused macro arms, although I'm afraid it would need new syntax, like

      _(unused_arm) => { ... } // or
      (unused_arm) _=> { ... } // (eew.) or
      (unused_arm) => _{ ... } // (ew.)
      
    • Alternately, we could consider the warning to be a way to discourage such syntax extensions with redundant syntactic forms (like a macro working both with and without the comma).

In any case, I think that this is a good point, but also that it does not hinder the need for a warning about unused macro arms.

added
A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)
C-feature-requestCategory: A feature request, i.e: not implemented / a PR.
T-langRelevant to the language team
on Jun 21, 2020
est31

est31 commented on Jun 21, 2020

@est31
Member

One concern I have is that it's not possible to cfg-gate single macro arms, which would be needed in situations I've outlined in this comment: #34938 (comment)

petrochenkov

petrochenkov commented on Jun 22, 2020

@petrochenkov
Contributor

@est31
You could put an allow on the whole macro.

#[allow(unused_macro_arms)]
macro foo {
    used_arm => {},
    conditionally_used_arm => {}
}

Not perfect, but still a pretty good approximation.
Macros with platform-dependent use of arms are not common.

est31

est31 commented on Jun 22, 2020

@est31
Member

Good point. Concern resolved! Would love to see this implemented. Begone, unused stuff!

iago-lito

iago-lito commented on Jun 22, 2020

@iago-lito
ContributorAuthor

@petrochenkov Is this finer-grained alternate allow line straightforward to feature, or would it need new syntax?

macro foo {
    used_arm => {},
    #[allow(unused_macro_arm)]
    conditionally_used_arm => {}
}
petrochenkov

petrochenkov commented on Jun 22, 2020

@petrochenkov
Contributor

@iago-lito
It will need some work, right now the whole macro body is treated as a single token stream.
The individual arms will need to be kept as nodes in AST and assigned NodeIds, then you'll be able to attach lint attributes like allow to them.
Also, the attributes on macro arms are indeed a new syntax.

est31

est31 commented on Jun 22, 2020

@est31
Member

You'd likely require ids to implement the lint in the first place, so at least that cost has to be paid already. At least that's how I did it in #41907 (with a set of ids of unused macros), idk how much it has changed since then.

petrochenkov

petrochenkov commented on Jun 22, 2020

@petrochenkov
Contributor

For uniquely identifying the arm having only the macro's NodeId + the arm's index should be enough, then this pair can be used to track which arms are used and which are not.

For applying allows to macro arms you need real NodeIds for them because that's what the lint level infra works with.

est31

est31 commented on Apr 16, 2022

@est31
Member

The lint might also be useful from a performance point of view, as each unused arm causes additional compile time. According to this blog post, there is sometimes a lot of compile time spent in expansion, it also reports about PRs that have reordered match arms and removed unused ones to increase whole-crate compile times by double digit percentages.

est31

est31 commented on Apr 16, 2022

@est31
Member

I'm wondering about the name for the lint. I couldn't find the term "arm" used anywhere in official documentation to refer to macro matchers, like the reference. Instead I found the term "rule", while arm was only used for match expressions, which follow a similar, but not the same, model as declarative macros.

Per that, the name of the lint should be unused_macro_rules, not unused_macro_arms. Which is a bit of a problem, because it can be read as (unused) (macro_rules), making people think it is meant for entire macros, not just their rules. One solution would be to abandon the plural i.e. unused_macro_rule. But that would be inconsistent with other lints. The lints that use singular forms for their name like unused_unsafe, unused_mut usually don't have corresponding plurals (singularia tantum).

I'm thinking about unused_macro_matchers or unused_macro_transcribers but I don't know, those only describe parts of the rules... What do you think?

est31

est31 commented on Apr 17, 2022

@est31
Member

I've filed a PR here: #96150 . It uses the unused_macro_rules right now. If you have alternative suggestions for the name, please tell me!

added a commit that references this issue on Apr 26, 2022
0aec217
added 2 commits that reference this issue on May 6, 2022
914f947
9ac8200
added a commit that references this issue on May 16, 2022
A1-Triard

A1-Triard commented on May 19, 2022

@A1-Triard
* First, `($($e:expr),*$(,)?)` already works fine in this situation:

It accepts single comma as an input. There is better way: $($($e:expr),+ $(,)?)?.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)C-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @jonas-schievink@petrochenkov@A1-Triard@est31@iago-lito

      Issue actions

        Warn about unused macro arms. · Issue #73576 · rust-lang/rust