diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index dbb4a9de9e03a..de0361c73b89d 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -29,7 +29,7 @@ use rustc_session::parse::feature_err; use rustc_session::{Limit, Session}; use rustc_span::hygiene::SyntaxContext; use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym}; -use smallvec::SmallVec; +use smallvec::{SmallVec, smallvec}; use crate::base::*; use crate::config::{StripUnconfigured, attr_into_trace}; @@ -46,6 +46,22 @@ use crate::module::{ use crate::placeholders::{PlaceholderExpander, placeholder}; use crate::stats::*; +/// Visitor to apply diagnostic attributes to expanded AST fragments +struct DiagnosticAttrApplier { + attrs: Vec, +} + +impl MutVisitor for DiagnosticAttrApplier { + fn flat_map_item(&mut self, item: Box) -> SmallVec<[Box; 1]> { + let mut new_item = item; + // Prepend diagnostic attributes to preserve their position + let mut new_attrs = self.attrs.clone(); + new_attrs.extend(new_item.attrs); + new_item.attrs = new_attrs.into(); + smallvec![new_item] + } +} + macro_rules! ast_fragments { ( $($Kind:ident($AstTy:ty) { @@ -818,12 +834,43 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let inner_tokens = attr_item.args.inner_tokens(); match expander.expand(self.cx, span, inner_tokens, tokens) { Ok(tok_result) => { - let fragment = self.parse_ast_fragment( + // Extract diagnostic attributes from the original item before parsing + let diagnostic_attrs = { + match &item { + Annotatable::Item(item) => item + .attrs + .iter() + .filter(|attr| { + attr.name().map_or(false, |name| { + matches!( + name, + sym::expect + | sym::allow + | sym::warn + | sym::deny + | sym::forbid + ) + }) + }) + .cloned() + .collect::>(), + _ => Vec::new(), + } + }; + + let mut fragment = self.parse_ast_fragment( tok_result, fragment_kind, &attr_item.path, span, ); + + // Apply diagnostic attributes to the expanded fragment + if !diagnostic_attrs.is_empty() { + fragment.mut_visit_with(&mut DiagnosticAttrApplier { + attrs: diagnostic_attrs, + }); + } if macro_stats { update_attr_macro_stats( self.cx, diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 9bfda8764f552..1ee1a381651a8 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -4,6 +4,7 @@ use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::Span; use rustc_span::profiling::SpannedEventArgRecorder; +use rustc_span::sym; use {rustc_ast as ast, rustc_proc_macro as pm}; use crate::base::{self, *}; @@ -122,6 +123,27 @@ impl MultiItemModifier for DeriveProcMacro { // Eventually we might remove the special case hard error check // altogether. See #73345. crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.psess); + + // Extract diagnostic attributes from the original item before expansion + let diagnostic_attrs = { + match &item { + base::Annotatable::Item(item) => item + .attrs + .iter() + .filter(|attr| { + attr.name().map_or(false, |name| { + matches!( + name, + sym::expect | sym::allow | sym::warn | sym::deny | sym::forbid + ) + }) + }) + .cloned() + .collect::>(), + _ => Vec::new(), // Other annotatable types don't have attributes + } + }; + let input = item.to_tokens(); let stream = { let _timer = @@ -158,7 +180,15 @@ impl MultiItemModifier for DeriveProcMacro { loop { match parser.parse_item(ForceCollect::No) { Ok(None) => break, - Ok(Some(item)) => { + Ok(Some(mut item)) => { + // Apply diagnostic attributes from the original item to the expanded item + if !diagnostic_attrs.is_empty() { + // Prepend diagnostic attributes to preserve their position + let mut new_attrs = diagnostic_attrs.clone(); + new_attrs.extend(item.attrs); + item.attrs = new_attrs.into(); + } + if is_stmt { items.push(Annotatable::Stmt(Box::new(ecx.stmt_item(span, item)))); } else { diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 23e7c7251cf15..c1c3fba5083b1 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -1,22 +1,21 @@ - use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Attribute, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, + TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{ExpnId, MacroKind, Span}; -use crate::utils::attr_collector::AttrStorage; - declare_clippy_lint! { /// ### What it does /// Denies the configured macros in clippy.toml @@ -69,14 +68,10 @@ pub struct DisallowedMacros { // Track the most recently seen node that can have a `derive` attribute. // Needed to use the correct lint level. derive_src: Option, - - // When a macro is disallowed in an early pass, it's stored - // and emitted during the late pass. This happens for attributes. - early_macro_cache: AttrStorage, } impl DisallowedMacros { - pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, early_macro_cache: AttrStorage) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_macros, @@ -89,7 +84,6 @@ impl DisallowedMacros { disallowed, seen: FxHashSet::default(), derive_src: None, - early_macro_cache, } } @@ -126,15 +120,90 @@ impl DisallowedMacros { } impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); - impl LateLintPass<'_> for DisallowedMacros { - fn check_crate(&mut self, cx: &LateContext<'_>) { - // once we check a crate in the late pass we can emit the early pass lints - if let Some(attr_spans) = self.early_macro_cache.clone().0.get() { - for span in attr_spans { - self.check(cx, *span, None); - } - } + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { + let span = match attr { + Attribute::Unparsed(attr_item) => attr_item.span, + Attribute::Parsed(kind) => match kind { + AttributeKind::Align { span, .. } + | AttributeKind::AllowConstFnUnstable(_, span) + | AttributeKind::AllowIncoherentImpl(span) + | AttributeKind::AllowInternalUnstable(_, span) + | AttributeKind::AsPtr(span) + | AttributeKind::AutomaticallyDerived(span) + | AttributeKind::BodyStability { span, .. } + | AttributeKind::Coinductive(span) + | AttributeKind::Cold(span) + | AttributeKind::Confusables { first_span: span, .. } + | AttributeKind::ConstContinue(span) + | AttributeKind::ConstStability { span, .. } + | AttributeKind::ConstTrait(span) + | AttributeKind::Coverage(span, _) + | AttributeKind::CrateName { attr_span: span, .. } + | AttributeKind::DenyExplicitImpl(span) + | AttributeKind::Deprecation { span, .. } + | AttributeKind::DoNotImplementViaObject(span) + | AttributeKind::DocComment { span, .. } + | AttributeKind::ExportName { span, .. } + | AttributeKind::FfiConst(span) + | AttributeKind::FfiPure(span) + | AttributeKind::Ignore { span, .. } + | AttributeKind::Inline(_, span) + | AttributeKind::LinkName { span, .. } + | AttributeKind::LinkOrdinal { span, .. } + | AttributeKind::LinkSection { span, .. } + | AttributeKind::LoopMatch(span) + | AttributeKind::MacroEscape(span) + | AttributeKind::MacroUse { span, .. } + | AttributeKind::Marker(span) + | AttributeKind::MayDangle(span) + | AttributeKind::MustUse { span, .. } + | AttributeKind::Naked(span) + | AttributeKind::NoImplicitPrelude(span) + | AttributeKind::NoMangle(span) + | AttributeKind::NonExhaustive(span) + | AttributeKind::Optimize(_, span) + | AttributeKind::ParenSugar(span) + | AttributeKind::PassByValue(span) + | AttributeKind::Path(_, span) + | AttributeKind::Pointee(span) + | AttributeKind::ProcMacro(span) + | AttributeKind::ProcMacroAttribute(span) + | AttributeKind::ProcMacroDerive { span, .. } + | AttributeKind::PubTransparent(span) + | AttributeKind::Repr { first_span: span, .. } + | AttributeKind::RustcBuiltinMacro { span, .. } + | AttributeKind::RustcLayoutScalarValidRangeEnd(_, span) + | AttributeKind::RustcLayoutScalarValidRangeStart(_, span) + | AttributeKind::Sanitize { span, .. } + | AttributeKind::SkipDuringMethodDispatch { span, .. } + | AttributeKind::SpecializationTrait(span) + | AttributeKind::Stability { span, .. } + | AttributeKind::StdInternalSymbol(span) + | AttributeKind::TargetFeature { attr_span: span, .. } + | AttributeKind::TrackCaller(span) + | AttributeKind::TypeConst(span) + | AttributeKind::UnsafeSpecializationMarker(span) + | AttributeKind::AllowInternalUnsafe(span) + | AttributeKind::Coroutine(span) + | AttributeKind::Linkage(_, span) + | AttributeKind::ShouldPanic { span, .. } + | AttributeKind::CustomMir(_, _, span) + | AttributeKind::Used { span, .. } => *span, + + AttributeKind::CoherenceIsCore + | AttributeKind::ConstStabilityIndirect + | AttributeKind::Dummy + | AttributeKind::ExportStable + | AttributeKind::Fundamental + | AttributeKind::MacroTransparency(_) + | AttributeKind::RustcObjectLifetimeDefault + | AttributeKind::UnstableFeatureBound(_) => { + return; + }, + }, + }; + self.check(cx, span, self.derive_src); } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d468993e74445..2582e378a4448 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -658,8 +658,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); - let attrs = attr_storage.clone(); - store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()))); + store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml index 8b4f3b713bbb5..b91969eee0084 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml @@ -1,4 +1,5 @@ disallowed-macros = [ + "std::panic", "std::println", "std::vec", { path = "std::cfg" }, diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index 589995fa87d91..2aeaa2af60e90 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -1,33 +1,11 @@ -error: use of a disallowed macro `serde::Serialize` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:22:14 - | -LL | #[derive(Serialize)] - | ^^^^^^^^^ - | - = note: no serializing - = note: `-D clippy::disallowed-macros` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]` - -error: use of a disallowed macro `macros::attr` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:42:1 - | -LL | / macros::attr! { -LL | | -LL | | struct S; -LL | | } - | |_^ - -error: use of a disallowed macro `proc_macros::Derive` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:62:10 - | -LL | #[derive(Derive)] - | ^^^^^^ - error: use of a disallowed macro `std::println` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:13:5 | LL | println!("one"); | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-macros` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]` error: use of a disallowed macro `std::println` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:15:5 @@ -47,6 +25,14 @@ error: use of a disallowed macro `std::vec` LL | vec![1, 2, 3]; | ^^^^^^^^^^^^^ +error: use of a disallowed macro `serde::Serialize` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:22:14 + | +LL | #[derive(Serialize)] + | ^^^^^^^^^ + | + = note: no serializing + error: use of a disallowed macro `macros::expr` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:26:13 | @@ -83,6 +69,15 @@ error: use of a disallowed macro `macros::binop` LL | let _ = macros::binop!(1); | ^^^^^^^^^^^^^^^^^ +error: use of a disallowed macro `macros::attr` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:42:1 + | +LL | / macros::attr! { +LL | | +LL | | struct S; +LL | | } + | |_^ + error: use of a disallowed macro `macros::item` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:48:5 | @@ -101,5 +96,11 @@ error: use of a disallowed macro `macros::item` LL | macros::item!(); | ^^^^^^^^^^^^^^^ +error: use of a disallowed macro `proc_macros::Derive` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:62:10 + | +LL | #[derive(Derive)] + | ^^^^^^ + error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs new file mode 100644 index 0000000000000..adaad23300770 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs @@ -0,0 +1,70 @@ +fn test_allow_on_function() { + #![allow(clippy::disallowed_macros)] + + panic!(); + panic!("message"); + panic!("{}", 123); + panic!("{} {}", 123, 456); + println!("test"); + println!("{}", 123); + vec![1, 2, 3]; +} + +fn test_expect_on_function() { + #![expect(clippy::disallowed_macros)] + + panic!(); + panic!("message"); + panic!("{}", 123); + panic!("{} {}", 123, 456); + println!("test"); + println!("{}", 123); + vec![1, 2, 3]; +} + +fn test_no_attributes() { + panic!(); + //~^ disallowed_macros + panic!("message"); + //~^ disallowed_macros + panic!("{}", 123); + //~^ disallowed_macros + panic!("{} {}", 123, 456); + //~^ disallowed_macros + println!("test"); + //~^ disallowed_macros + println!("{}", 123); + //~^ disallowed_macros + vec![1, 2, 3]; + //~^ disallowed_macros +} + +#[allow(clippy::disallowed_macros)] +mod allowed_module { + pub fn test() { + panic!(); + panic!("message"); + panic!("{}", 123); + println!("test"); + vec![1, 2, 3]; + } +} + +#[expect(clippy::disallowed_macros)] +mod expected_module { + pub fn test() { + panic!(); + panic!("message"); + panic!("{}", 123); + println!("test"); + vec![1, 2, 3]; + } +} + +fn main() { + test_allow_on_function(); + test_expect_on_function(); + test_no_attributes(); + allowed_module::test(); + expected_module::test(); +} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros_regression.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros_regression.stderr new file mode 100644 index 0000000000000..5e740b546bad6 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros_regression.stderr @@ -0,0 +1,47 @@ +error: use of a disallowed macro `std::panic` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:26:5 + | +LL | panic!(); + | ^^^^^^^^ + | + = note: `-D clippy::disallowed-macros` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]` + +error: use of a disallowed macro `std::panic` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:28:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::panic` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:30:5 + | +LL | panic!("{}", 123); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::panic` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:32:5 + | +LL | panic!("{} {}", 123, 456); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::println` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:34:5 + | +LL | println!("test"); + | ^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::println` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:36:5 + | +LL | println!("{}", 123); + | ^^^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::vec` + --> tests/ui-toml/disallowed_macros/disallowed_macros_regression.rs:38:5 + | +LL | vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors +