rust-lang / rust-lang/rust

"deny" lints are ignored in macro expansion

Open
#126,499 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-lints A-macros C-bug T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code:

// In a lib crate
pub trait Val {
    const VAL: [u8; 8];
}

impl Val for u8 {
    const VAL: [u8; 8] = [0u8; 8];
}

impl Val for u16 {
    const VAL: [u8; 8] = [1u8; 8];
}


#[macro_export]
macro_rules! example {
    ($val: ident, $($name:ty),*) => {
        // We want this macro to fire if there are dupes
        #[deny(unreachable_patterns)]
        match $val {
            $(
                <$name as Val>::VAL => println!("$name"),
            )*
            _ => println!("Other"),
        }
    };
}

then:

// in a crate that depends on the above crate's code:
#![deny(unreachable_patterns)]

use macro_source::Val;

fn main() {
    let val = [2u8; 8];
    macro_source::example!(val, u8, u8, u16);
}

I expected to see this happen:

I would expect the deny lint to fire and halt compilation

Instead, this happened:

The crate compiles with no warnings or errors

You can see the issue in the expanded code:

#![feature(prelude_import)]
#![deny(unreachable_patterns)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use macro_source::Val;
fn main() {
    let val = [2u8; 8];
    #[deny(unreachable_patterns)]
    match val {
        <u8 as Val>::VAL => {
            ::std::io::_print(format_args!("$name\n"));
        }
        <u8 as Val>::VAL => {
//      ^^^^^^^^^^^^^^^^ duplicate arm - will never match!
            ::std::io::_print(format_args!("$name\n"));
        }
        <u16 as Val>::VAL => {
            ::std::io::_print(format_args!("$name\n"));
        }
        _ => {
            ::std::io::_print(format_args!("Other\n"));
        }
    };
}
Meta

I asked about this in Zulip, and it was mentioned that only a select number of lints still fire during macro expansion.

However:

  • It seems questionable to me to ignore "deny" lints, rather than just silencing "warn" lints
  • It was surprising to me that adding the deny level at the expansion point was also ignored
  • Unreachable patterns seems like a somewhat important lint, but there's probably a number of other ones that could also be argued for
  • It was surprising to me that the lint DOES fire in the declaring crate (where I tested it), but DOESN'T fire when included in another crate

But more importantly:

If the compiler could potentially know that a lint will never fire, it might be good to add a warning for that at the macro declaration site, something like:

#[macro_export]
macro_rules! example {
    ($val: ident, $($name:ty),*) => {
        #[deny(unreachable_patterns)]
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: lints in macros don't fire at their expansion point
        match $val {
            $(
                <$name as Val>::VAL => println!("$name"),
            )*
            _ => println!("Other"),
        }
    };
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the shown macro_rules! example across the library crate and its dependent crate, then inspect how lint levels are handled during macro expansion. Done should establish and implement a clear behavior for deny(unreachable_patterns) in expanded macros, including the proposed declaration-site warning or an explicitly tested alternative.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.