From 3c67da6404f8269505e8953c7db8fef04a5534ef Mon Sep 17 00:00:00 2001 From: darkraider01 <95766741+darkraider01@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:33:32 +0530 Subject: [PATCH 1/4] Cover macro export on macro_rules definitions --- tests/ui/attributes/macro_export_on_decl_macro.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/ui/attributes/macro_export_on_decl_macro.rs b/tests/ui/attributes/macro_export_on_decl_macro.rs index e6fe66ac6c30e..413b844cc1b83 100644 --- a/tests/ui/attributes/macro_export_on_decl_macro.rs +++ b/tests/ui/attributes/macro_export_on_decl_macro.rs @@ -6,4 +6,10 @@ #[macro_export] //~ ERROR `#[macro_export]` has no effect on declarative macro definitions pub macro foo() {} +// `#[macro_export]` on `macro_rules!` is valid and should not emit unused_attributes +#[macro_export] +macro_rules! bar { + () => {}; +} + fn main() {} From 7f542b6a12648df131e1dc9406d3c763a5dc2d0e Mon Sep 17 00:00:00 2001 From: darkraider01 <95766741+darkraider01@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:33:33 +0530 Subject: [PATCH 2/4] Check declarative macro exports during attribute finalization --- .../src/attributes/macro_attrs.rs | 18 +++++++++++++-- .../rustc_attr_parsing/src/diagnostics.rs | 7 ++++++ compiler/rustc_passes/src/check_attr.rs | 23 +------------------ compiler/rustc_passes/src/diagnostics.rs | 7 ------ 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index dde10225e652f..968a613209248 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,10 +1,12 @@ +use rustc_ast::ItemKind; use rustc_attr_ir::{MacroUseArgs, find_attr}; use rustc_feature::AttributeStability; -use rustc_lint_defs::builtin::INVALID_MACRO_EXPORT_ARGUMENTS; +use rustc_lint_defs::builtin::{INVALID_MACRO_EXPORT_ARGUMENTS, UNUSED_ATTRIBUTES}; use rustc_structures::CollapseMacroDebuginfo; use super::prelude::*; -use crate::diagnostics::MacroOnlyAttribute; +use crate::context::ShouldEmit; +use crate::diagnostics::{MacroExport, MacroOnlyAttribute}; pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { @@ -175,8 +177,20 @@ impl SingleAttributeParser for MacroExportParser { return None; } }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) } + + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { + if cx.target != Target::MacroDef || matches!(cx.should_emit, ShouldEmit::Nothing) { + return; + } + + let item = cx.target_item.unwrap(); + if let ItemKind::MacroDef(_, macro_def) = &item.kind && !macro_def.macro_rules { + cx.emit_lint(UNUSED_ATTRIBUTES, MacroExport::OnDeclMacro, attr_span); + } + } } pub(crate) struct CollapseDebugInfoParser; diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index bf630c6bf91a0..879f8da67ae15 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2063,3 +2063,10 @@ pub(crate) struct UnusedDuplicate { )] pub warning: bool, } + +#[derive(Diagnostic)] +pub(crate) enum MacroExport { + #[diag("`#[macro_export]` has no effect on declarative macro definitions")] + #[note("declarative macros follow the same exporting rules as regular items")] + OnDeclMacro, +} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3c920d14006bb..d6501ee672bff 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -207,9 +207,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } AttributeKind::Naked(..) => self.check_naked(hir_id, target), AttributeKind::MayDangle(attr_span) => self.check_may_dangle(hir_id, *attr_span), - AttributeKind::MacroExport { span, .. } => { - self.check_macro_export(hir_id, *span, target) - } AttributeKind::RustcLegacyConstGenerics { attr_span, fn_indexes } => { self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes) } @@ -275,6 +272,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::LinkSection { .. } => (), AttributeKind::LoopMatch(..) => (), AttributeKind::MacroEscape => (), + AttributeKind::MacroExport { .. } => (), AttributeKind::MacroUse { .. } => (), AttributeKind::Marker => (), AttributeKind::MoveSizeLimit { .. } => (), @@ -1281,25 +1279,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { - if target != Target::MacroDef { - return; - } - - // special case when `#[macro_export]` is applied to a macro 2.0 - let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); - let is_decl_macro = !macro_definition.macro_rules; - - if is_decl_macro { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr_span, - diagnostics::MacroExport::OnDeclMacro, - ); - } - } - fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { // Warn on useless empty attributes. // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 7c82811f99276..76b00864c35fe 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -199,13 +199,6 @@ pub(crate) struct RustcForceInlineCoro { pub span: Span, } -#[derive(Diagnostic)] -pub(crate) enum MacroExport { - #[diag("`#[macro_export]` has no effect on declarative macro definitions")] - #[note("declarative macros follow the same exporting rules as regular items")] - OnDeclMacro, -} - #[derive(Subdiagnostic)] pub(crate) enum UnusedNote { #[note("attribute `{$name}` with an empty list has no effect")] From bf2b89cb1946c20726136606062caf1f79d2214f Mon Sep 17 00:00:00 2001 From: darkraider01 <95766741+darkraider01@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:53:05 +0530 Subject: [PATCH 3/4] Format macro export finalization check --- compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 968a613209248..8bca20cda67aa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -187,7 +187,9 @@ impl SingleAttributeParser for MacroExportParser { } let item = cx.target_item.unwrap(); - if let ItemKind::MacroDef(_, macro_def) = &item.kind && !macro_def.macro_rules { + if let ItemKind::MacroDef(_, macro_def) = &item.kind + && !macro_def.macro_rules + { cx.emit_lint(UNUSED_ATTRIBUTES, MacroExport::OnDeclMacro, attr_span); } } From 1ea0af2b635db880e64e9c1083d3f89c5d757fb5 Mon Sep 17 00:00:00 2001 From: darkraider01 <95766741+darkraider01@users.noreply.github.com> Date: Thu, 1 Oct 2026 05:02:05 +0530 Subject: [PATCH 4/4] Skip deferred attribute checks when not emitting --- .../src/attributes/macro_attrs.rs | 3 +- compiler/rustc_attr_parsing/src/interface.rs | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 8bca20cda67aa..8bb5e6b7fc270 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -5,7 +5,6 @@ use rustc_lint_defs::builtin::{INVALID_MACRO_EXPORT_ARGUMENTS, UNUSED_ATTRIBUTES use rustc_structures::CollapseMacroDebuginfo; use super::prelude::*; -use crate::context::ShouldEmit; use crate::diagnostics::{MacroExport, MacroOnlyAttribute}; pub(crate) struct MacroEscapeParser; @@ -182,7 +181,7 @@ impl SingleAttributeParser for MacroExportParser { } fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { - if cx.target != Target::MacroDef || matches!(cx.should_emit, ShouldEmit::Nothing) { + if cx.target != Target::MacroDef { return; } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index df89ffe57b9a0..95412c34be371 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -510,23 +510,25 @@ impl<'sess> AttributeParser<'sess> { // Now that all attributes have been parsed, run the deferred checks. These can // inspect the fully parsed attributes via `FinalizeCheckContext::parsed_attrs`. - for (check, attr_span) in deferred_checks { - check( - &mut FinalizeCheckContext { - shared: SharedContext { - cx: self, - target_span, - target, - emit_lint: &mut emit_lint, - #[cfg(debug_assertions)] - has_lint_been_emitted: AtomicBool::new(false), + if !matches!(self.should_emit, ShouldEmit::Nothing) { + for (check, attr_span) in deferred_checks { + check( + &mut FinalizeCheckContext { + shared: SharedContext { + cx: self, + target_span, + target, + emit_lint: &mut emit_lint, + #[cfg(debug_assertions)] + has_lint_been_emitted: AtomicBool::new(false), + }, + all_attrs: &attr_paths, + parsed_attrs: &attributes, + target_item, }, - all_attrs: &attr_paths, - parsed_attrs: &attributes, - target_item, - }, - attr_span, - ); + attr_span, + ); + } } if !matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate {