Description
.
├── Cargo.toml
├── the-derive
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── the-library
├── Cargo.toml
└── src
└── lib.rs
Cargo.toml
[workspace]
resolver = "3"
members = ["the-derive", "the-library"]
the-derive/Cargo.toml
[package]
name = "the-derive"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
the-derive/src/lib.rs
#![feature(proc_macro_quote)]
extern crate proc_macro;
use proc_macro::{quote, TokenStream, TokenTree};
#[proc_macro_derive(GenerateStuff, attributes(vis))]
pub fn generate_stuff(item: TokenStream) -> TokenStream {
let tokens = item.into_iter().collect::<Vec<_>>();
let TokenTree::Group(attr_group) = &tokens[1] else { panic!("malformed") };
let attr_group_tokens = attr_group.stream().into_iter().collect::<Vec<_>>();
let TokenTree::Group(vis_group) = &attr_group_tokens[1] else { panic!("malformed") };
let vis_group_tokens = vis_group.stream();
quote! {
$vis_group_tokens struct GeneratedType;
impl GeneratedType {
$vis_group_tokens fn unused_method(&self) {}
}
}
}
the-library/Cargo.toml
[package]
name = "the-library"
version = "0.1.0"
edition = "2024"
[dependencies]
the-derive = { path = "../the-derive" }
the-library/src/lib.rs
#![deny(dead_code)]
// This version works
#[derive(the_derive::GenerateStuff)]
#[vis(pub(crate))]
struct Parent;
// This version fails
// macro_rules! invoke {
// () => {
// #[derive(the_derive::GenerateStuff)]
// #[vis(pub(crate))]
// struct Parent;
// };
// }
// invoke!();
pub fn usage() {
// Assume that the base type is used somewhere
let _ = Parent;
}
Execution
% cargo +nightly check --all
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
Then switch to the version inside the declarative macro:
% cargo +nightly check --all
Checking the-library v0.1.0 (/private/tmp/repro/the-library)
error: struct `GeneratedType` is never constructed
--> the-library/src/lib.rs:16:1
|
16 | invoke!();
| ^^^^^^^^^
|
note: the lint level is defined here
--> the-library/src/lib.rs:1:9
|
1 | #![deny(dead_code)]
| ^^^^^^^^^
= note: this error originates in the macro `invoke` (in Nightly builds, run with -Z macro-backtrace for more info)
error: method `unused_method` is never used
--> the-library/src/lib.rs:16:1
|
16 | invoke!();
| ^^^^^^^^^ method in this implementation
|
= note: this error originates in the macro `invoke` (in Nightly builds, run with -Z macro-backtrace for more info)
Expected
I expected the behavior of the dead_code
lint to be consistent if the procedural macro is invoked from inside a declarative macro or not.
Actual
The behavior of the dead_code
lint is not consistent, it changes if the procedural macro is invoked from inside a declarative macro or not.
Notes
-
Changing the procedural macro to hard-code the
pub(crate)
as a string that is parsed causes both forms to behave the same:#[proc_macro_derive(GenerateStuff, attributes(vis))] pub fn generate_stuff(_item: TokenStream) -> TokenStream { r##" pub(crate) struct GeneratedType; impl GeneratedType { pub(crate) fn unused_method(&self) {} } "##.parse().unwrap() }
-
Changing the procedural macro to hard-code the
pub(crate)
inside thequote!
macro causes both forms to produce a surprising (unrelated?) error:pub fn generate_stuff(_item: TokenStream) -> TokenStream { quote! { pub(crate) struct GeneratedType; impl GeneratedType { pub(crate) fn unused_method(&self) {} } } }
error[E0742]: visibilities can only be restricted to ancestor modules --> the-library/src/lib.rs:4:10 | 4 | #[derive(the_derive::GenerateStuff)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the derive macro `the_derive::GenerateStuff` (in Nightly builds, run with -Z macro-backtrace for more info)
Meta
rustc --version --verbose
:
rustc 1.88.0-nightly (2fa8b11f0 2025-04-06)
binary: rustc
commit-hash: 2fa8b11f0933dae9b4e5d287cc10c989218e8b36
commit-date: 2025-04-06
host: aarch64-apple-darwin
release: 1.88.0-nightly
LLVM version: 20.1.2
For the reproduction case above, I'm using nightly features. In my real case, I'm using the syn/proc-macro2/quote crates and the problem occurs on stable Rust:
rustc +stable --version --verbose
rustc 1.86.0 (05f9846f8 2025-03-31)
binary: rustc
commit-hash: 05f9846f893b09a1be1fc8560e33fc3c815cfecb
commit-date: 2025-03-31
host: aarch64-apple-darwin
release: 1.86.0
LLVM version: 19.1.7