"deny" lints are ignored in macro expansion
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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