From f351cd4b3164f159389794a1bc6cb25da0d231fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 13 Jun 2025 01:36:52 +0200 Subject: [PATCH 1/6] rewrite #[naked] parser --- .../src/attributes.rs | 4 ++++ .../src/attributes/codegen_attrs.rs | 19 ++++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 ++- .../rustc_codegen_ssa/src/codegen_attrs.rs | 2 +- .../rustc_hir_analysis/src/check/check.rs | 3 ++- compiler/rustc_hir_typeck/src/expr.rs | 3 ++- compiler/rustc_hir_typeck/src/lib.rs | 5 +++-- .../rustc_hir_typeck/src/naked_functions.rs | 5 +++-- compiler/rustc_passes/src/check_attr.rs | 16 +++++++++------- compiler/rustc_passes/src/liveness.rs | 3 ++- 10 files changed, 46 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9ebf4c1793dde..9b8338bda7c7c 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -233,6 +233,10 @@ pub enum AttributeKind { /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), + + /// Represents #[naked] + Naked(Span), + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 1b03525a5ce84..e1c173f5f42c2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -51,8 +51,25 @@ impl SingleAttributeParser for ColdParser { if !args.no_args() { cx.expected_no_args(args.span().unwrap_or(cx.attr_span)); return None; - }; + } Some(AttributeKind::Cold(cx.attr_span)) } } + +pub(crate) struct NakedParser; + +impl SingleAttributeParser for NakedParser { + const PATH: &[rustc_span::Symbol] = &[sym::naked]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + if !args.no_args() { + cx.expected_no_args(args.span().unwrap_or(cx.attr_span)); + return None; + } + Some(AttributeKind::Naked(cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8311fccacd894..2c5e8dfe745bf 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,7 +15,7 @@ use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; -use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser}; +use crate::attributes::codegen_attrs::{ColdParser, NakedParser, OptimizeParser}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -110,6 +110,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 39818be5bde5a..bfdfa28a0b4ad 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -121,6 +121,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { .max(); } AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, + AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), _ => {} } @@ -140,7 +141,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } - sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, sym::no_mangle => { no_mangle_span = Some(attr.span()); if tcx.opt_item_name(did.to_def_id()).is_some() { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 32fec0604c0f1..677508597b8a1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use rustc_abi::FieldIdx; use rustc_attr_data_structures::ReprAttr::ReprPacked; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{EmissionGuarantee, MultiSpan}; @@ -103,7 +104,7 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) { if fn_sig.abi == ExternAbi::Custom { // Function definitions that use `extern "custom"` must be naked functions. - if !tcx.has_attr(def_id, sym::naked) { + if !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(_)) { tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction { span: fn_sig_span, naked_span: tcx.def_span(def_id).shrink_to_lo(), diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index eca9c5faa3229..6edc5306c7a12 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -7,6 +7,7 @@ use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx}; use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; @@ -3784,7 +3785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) -> Ty<'tcx> { if let rustc_ast::AsmMacro::NakedAsm = asm.asm_macro { - if !self.tcx.has_attr(self.body_id, sym::naked) { + if !find_attr!(self.tcx.get_all_attrs(self.body_id), AttributeKind::Naked(..)) { self.tcx.dcx().emit_err(NakedAsmOutsideNakedFn { span }); } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 043a687914b70..1cc618e2aeeca 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -42,6 +42,7 @@ mod writeback; pub use coercion::can_coerce; use fn_ctxt::FnCtxt; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; @@ -55,8 +56,8 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; use tracing::{debug, instrument}; use typeck_root_ctxt::TypeckRootCtxt; @@ -173,7 +174,7 @@ fn typeck_with_inspect<'tcx>( .map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)), ); - if tcx.has_attr(def_id, sym::naked) { + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..)) { naked_functions::typeck_naked_fn(tcx, def_id, body); } diff --git a/compiler/rustc_hir_typeck/src/naked_functions.rs b/compiler/rustc_hir_typeck/src/naked_functions.rs index 2518d6478e655..d055fa68fc34a 100644 --- a/compiler/rustc_hir_typeck/src/naked_functions.rs +++ b/compiler/rustc_hir_typeck/src/naked_functions.rs @@ -1,12 +1,13 @@ //! Checks validity of naked functions. +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirIdSet, StmtKind}; use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; -use rustc_span::{Span, sym}; +use rustc_span::Span; use crate::errors::{ NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns, ParamsNotAllowed, @@ -20,7 +21,7 @@ pub(crate) fn typeck_naked_fn<'tcx>( def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, ) { - debug_assert!(tcx.has_attr(def_id, sym::naked)); + debug_assert!(find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..))); check_no_patterns(tcx, body.params); check_no_parameters_use(tcx, body); check_asm(tcx, def_id, body); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5fb9514e5caff..0eeaa1f2b6546 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -155,6 +155,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => { self.check_align(span, target, *align, *repr_span) } + Attribute::Parsed(AttributeKind::Naked(attr_span)) => { + self.check_naked(hir_id, *attr_span, span, target, attrs) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -201,7 +204,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_std_internal_symbol, ..] => { self.check_rustc_std_internal_symbol(attr, span, target) } - [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } @@ -611,7 +613,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_naked( &self, hir_id: HirId, - attr: &Attribute, + attr_span: Span, span: Span, target: Target, attrs: &[Attribute], @@ -647,7 +649,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::link_section, sym::linkage, sym::no_mangle, - sym::naked, sym::instruction_set, sym::repr, sym::align, @@ -688,14 +689,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. } | AttributeKind::Align { .. } - | AttributeKind::Cold(..), + | AttributeKind::Cold(..) + | AttributeKind::Naked(..), ) => { continue; } Attribute::Parsed(AttributeKind::Inline(.., span)) => { self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { span: *span, - naked_span: attr.span(), + naked_span: attr_span, attr: sym::inline.to_string(), }); @@ -732,7 +734,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { span: other_attr.span(), - naked_span: attr.span(), + naked_span: attr_span, attr: other_attr_name, }); @@ -742,7 +744,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span(), + attr_span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 763d9fda80494..125730377ef87 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -85,6 +85,7 @@ use std::io; use std::io::prelude::*; use std::rc::Rc; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::*; @@ -145,7 +146,7 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) { } // Don't run unused pass for #[naked] - if tcx.has_attr(def_id.to_def_id(), sym::naked) { + if find_attr!(tcx.get_all_attrs(def_id.to_def_id()), AttributeKind::Naked(..)) { return; } From 909e55477f9a7e7eeb614479683dac9c8df3d794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 19 Jun 2025 08:26:18 +0200 Subject: [PATCH 2/6] make warnings methods on cx so it's easier to emit them elsewhere too --- .../rustc_attr_parsing/src/attributes/mod.rs | 10 ++----- compiler/rustc_attr_parsing/src/context.rs | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 78a696afa2738..593279467dc71 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -187,14 +187,8 @@ impl OnDuplicate { unused: Span, ) { match self { - OnDuplicate::Warn => cx.emit_lint( - AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false }, - unused, - ), - OnDuplicate::WarnButFutureError => cx.emit_lint( - AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true }, - unused, - ), + OnDuplicate::Warn => cx.warn_unused_duplicate(used, unused), + OnDuplicate::WarnButFutureError => cx.warn_unused_duplicate_future_error(used, unused), OnDuplicate::Error => { cx.emit_err(UnusedMultiple { this: used, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 2c5e8dfe745bf..425143ae457d6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -195,6 +195,32 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { (self.emit_lint)(AttributeLint { id, span, kind: lint }); } + pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { + self.emit_lint( + AttributeLintKind::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: false, + }, + unused_span, + ) + } + + pub(crate) fn warn_unused_duplicate_future_error( + &mut self, + used_span: Span, + unused_span: Span, + ) { + self.emit_lint( + AttributeLintKind::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, + }, + unused_span, + ) + } + pub(crate) fn unknown_key( &self, span: Span, From 9cd2d023ff4f08eeb5d806f7712d97ddf257c917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 13 Jun 2025 02:28:33 +0200 Subject: [PATCH 3/6] move naked checks out of check_attr.rs --- .../src/attributes.rs | 2 +- compiler/rustc_attr_parsing/messages.ftl | 4 + .../src/attributes/codegen_attrs.rs | 114 +++++++++++++++-- .../src/attributes/inline.rs | 6 +- .../rustc_attr_parsing/src/attributes/mod.rs | 1 - compiler/rustc_attr_parsing/src/context.rs | 68 +++++++--- compiler/rustc_attr_parsing/src/parser.rs | 8 ++ .../src/session_diagnostics.rs | 11 ++ compiler/rustc_passes/messages.ftl | 5 - compiler/rustc_passes/src/check_attr.rs | 119 +----------------- compiler/rustc_passes/src/errors.rs | 11 -- tests/ui/asm/naked-functions-inline.stderr | 14 +-- tests/ui/asm/naked-invalid-attr.stderr | 18 +-- ...gate-naked_functions_target_feature.stderr | 4 +- .../error-with-naked.stderr | 8 +- 15 files changed, 205 insertions(+), 188 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9b8338bda7c7c..7677213bfe198 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -234,7 +234,7 @@ pub enum AttributeKind { /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), - /// Represents #[naked] + /// Represents `#[naked]` Naked(Span), /// Represents `#[optimize(size|speed)]` diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 0891afc003e14..2bb27ede86055 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -89,6 +89,10 @@ attr_parsing_missing_since = attr_parsing_multiple_stability_levels = multiple stability levels +attr_parsing_naked_functions_incompatible_attribute = + attribute incompatible with `#[unsafe(naked)]` + .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]` + .naked_attribute = function marked with `#[unsafe(naked)]` here attr_parsing_non_ident_feature = 'feature' is not an identifier diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index e1c173f5f42c2..519618422314e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,10 +1,12 @@ use rustc_attr_data_structures::{AttributeKind, OptimizeAttr}; use rustc_feature::{AttributeTemplate, template}; -use rustc_span::sym; +use rustc_session::parse::feature_err; +use rustc_span::{Span, sym}; -use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, Stage}; +use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; +use crate::session_diagnostics::NakedFunctionIncompatibleAttribute; pub(crate) struct OptimizeParser; @@ -57,19 +59,105 @@ impl SingleAttributeParser for ColdParser { } } -pub(crate) struct NakedParser; +#[derive(Default)] +pub(crate) struct NakedParser { + span: Option, +} -impl SingleAttributeParser for NakedParser { - const PATH: &[rustc_span::Symbol] = &[sym::naked]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); +impl AttributeParser for NakedParser { + const ATTRIBUTES: AcceptMapping = + &[(&[sym::naked], template!(Word), |this, cx, args| { + if !args.no_args() { + cx.expected_no_args(args.span().unwrap_or(cx.attr_span)); + return; + } - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if !args.no_args() { - cx.expected_no_args(args.span().unwrap_or(cx.attr_span)); + if let Some(earlier) = this.span { + let span = cx.attr_span; + cx.warn_unused_duplicate(earlier, span); + } else { + this.span = Some(cx.attr_span); + } + })]; + + fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option { + // FIXME(jdonszelmann): upgrade this list to *parsed* attributes + // once all of these have parsed forms. That'd make the check much nicer... + // + // many attributes don't make sense in combination with #[naked]. + // Notable attributes that are incompatible with `#[naked]` are: + // + // * `#[inline]` + // * `#[track_caller]` + // * `#[test]`, `#[ignore]`, `#[should_panic]` + // + // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains + // accurate. + const ALLOW_LIST: &[rustc_span::Symbol] = &[ + // conditional compilation + sym::cfg_trace, + sym::cfg_attr_trace, + // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`) + sym::test, + sym::ignore, + sym::should_panic, + sym::bench, + // diagnostics + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::deprecated, + sym::must_use, + // abi, linking and FFI + sym::cold, + sym::export_name, + sym::link_section, + sym::linkage, + sym::no_mangle, + sym::instruction_set, + sym::repr, + sym::rustc_std_internal_symbol, + sym::align, + // obviously compatible with self + sym::naked, + // documentation + sym::doc, + ]; + + let Some(span) = self.span else { return None; + }; + + // only if we found a naked attribute do we do the somewhat expensive check + 'outer: for other_attr in cx.all_attrs { + for allowed_attr in ALLOW_LIST { + if other_attr.word_is(*allowed_attr) || other_attr.starts_with(&[sym::rustfmt]) { + // effectively skips the error message being emitted below + continue 'outer; + } + + if other_attr.word_is(sym::target_feature) { + if !cx.features().naked_functions_target_feature() { + feature_err( + &cx.sess(), + sym::naked_functions_target_feature, + other_attr.span(), + "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions", + ).emit(); + } + + continue 'outer; + } + } + + cx.emit_err(NakedFunctionIncompatibleAttribute { + span: other_attr.span(), + naked_span: span, + attr: other_attr.get_attribute_path().to_string(), + }); } - Some(AttributeKind::Naked(cx.attr_span)) + + Some(AttributeKind::Naked(span)) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 25efc3ae49b9d..11844f4cd950f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -45,10 +45,8 @@ impl SingleAttributeParser for InlineParser { ArgParser::NameValue(_) => { let suggestions = >::TEMPLATE.suggestions(false, "inline"); - cx.emit_lint( - AttributeLintKind::IllFormedAttributeInput { suggestions }, - cx.attr_span, - ); + let span = cx.attr_span; + cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); return None; } } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 593279467dc71..37c1359b5017f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,7 +17,6 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::lints::AttributeLintKind; use rustc_feature::AttributeTemplate; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 425143ae457d6..a8134dcd4fc4a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,7 +26,7 @@ use crate::attributes::stability::{ }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single}; -use crate::parser::{ArgParser, MetaItemParser}; +use crate::parser::{ArgParser, MetaItemParser, PathParser}; use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem}; macro_rules! group_type { @@ -95,6 +95,7 @@ attribute_parsers!( BodyStabilityParser, ConfusablesParser, ConstStabilityParser, + NakedParser, StabilityParser, // tidy-alphabetical-end @@ -110,7 +111,6 @@ attribute_parsers!( Single, Single, Single, - Single, Single, Single, Single, @@ -169,7 +169,7 @@ pub struct Late; /// /// Gives [`AttributeParser`]s enough information to create errors, for example. pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { - pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>, + pub(crate) shared: SharedContext<'f, 'sess, S>, /// The span of the attribute currently being parsed pub(crate) attr_span: Span, @@ -182,7 +182,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { pub(crate) attr_path: AttrPath, } -impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { +impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { S::emit_err(&self.sess, diag) } @@ -220,7 +220,9 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { unused_span, ) } +} +impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { pub(crate) fn unknown_key( &self, span: Span, @@ -353,16 +355,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { - type Target = FinalizeContext<'f, 'sess, S>; + type Target = SharedContext<'f, 'sess, S>; fn deref(&self) -> &Self::Target { - &self.finalize_cx + &self.shared } } impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.finalize_cx + &mut self.shared } } @@ -370,7 +372,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { /// /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create /// errors, for example. -pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { +pub(crate) struct SharedContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. pub(crate) cx: &'p mut AttributeParser<'sess, S>, @@ -379,10 +381,40 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to pub(crate) target_id: S::Id, - pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint), + emit_lint: &'p mut dyn FnMut(AttributeLint), +} + +/// Context given to every attribute parser during finalization. +/// +/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create +/// errors, for example. +pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { + pub(crate) shared: SharedContext<'p, 'sess, S>, + + /// A list of all attribute on this syntax node. + /// + /// Useful for allowlists in finalize. + /// + /// Usually, you should use normal attribute parsing logic instead, + /// especially when making a *denylist* of other attributes. + pub(crate) all_attrs: &'p [PathParser<'p>], } impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { + type Target = SharedContext<'p, 'sess, S>; + + fn deref(&self) -> &Self::Target { + &self.shared + } +} + +impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.shared + } +} + +impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> { type Target = AttributeParser<'sess, S>; fn deref(&self) -> &Self::Target { @@ -390,7 +422,7 @@ impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { } } -impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { +impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> { fn deref_mut(&mut self) -> &mut Self::Target { self.cx } @@ -494,6 +526,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { mut emit_lint: impl FnMut(AttributeLint), ) -> Vec { let mut attributes = Vec::new(); + let mut attr_paths = Vec::new(); for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. @@ -537,6 +570,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { // })) // } ast::AttrKind::Normal(n) => { + attr_paths.push(PathParser::Ast(&n.item.path)); + let parser = MetaItemParser::from_attr(n, self.dcx()); let path = parser.path(); let args = parser.args(); @@ -545,7 +580,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { if let Some(accepts) = S::parsers().0.get(parts.as_slice()) { for (template, accept) in accepts { let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { - finalize_cx: FinalizeContext { + shared: SharedContext { cx: self, target_span, target_id, @@ -589,10 +624,13 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let mut parsed_attributes = Vec::new(); for f in &S::parsers().1 { if let Some(attr) = f(&mut FinalizeContext { - cx: self, - target_span, - target_id, - emit_lint: &mut emit_lint, + shared: SharedContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }, + all_attrs: &attr_paths, }) { parsed_attributes.push(Attribute::Parsed(attr)); } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 1edbe3a9d27aa..84837bbb0088c 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -87,6 +87,14 @@ impl<'a> PathParser<'a> { pub fn word_is(&self, sym: Symbol) -> bool { self.word().map(|i| i.name == sym).unwrap_or(false) } + + /// Checks whether the first segments match the givens. + /// + /// Unlike [`segments_is`](Self::segments_is), + /// `self` may contain more segments than the number matched against. + pub fn starts_with(&self, segments: &[Symbol]) -> bool { + self.segments().zip(segments).all(|(a, b)| a.name == *b) + } } impl Display for PathParser<'_> { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 29f2e44a98a0b..cb1d663c9ddff 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -473,6 +473,17 @@ pub(crate) struct UnrecognizedReprHint { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] +pub(crate) struct NakedFunctionIncompatibleAttribute { + #[primary_span] + #[label] + pub span: Span, + #[label(attr_parsing_naked_attribute)] + pub naked_span: Span, + pub attr: String, +} + pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, ExpectedStringLiteral { byte_string: Option }, diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index c1a2b3b29733f..29526817257b1 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -491,11 +491,6 @@ passes_must_not_suspend = passes_must_use_no_effect = `#[must_use]` has no effect when applied to {$article} {$target} -passes_naked_functions_incompatible_attribute = - attribute incompatible with `#[unsafe(naked)]` - .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]` - .naked_attribute = function marked with `#[unsafe(naked)]` here - passes_no_link = attribute should be applied to an `extern crate` item .label = not an `extern crate` item diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0eeaa1f2b6546..595d03c892573 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, kw, sym}; +use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; @@ -156,7 +156,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_align(span, target, *align, *repr_span) } Attribute::Parsed(AttributeKind::Naked(attr_span)) => { - self.check_naked(hir_id, *attr_span, span, target, attrs) + self.check_naked(hir_id, *attr_span, span, target) } Attribute::Parsed( AttributeKind::BodyStability { .. } @@ -610,53 +610,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[naked]` is applied to a function definition. - fn check_naked( - &self, - hir_id: HirId, - attr_span: Span, - span: Span, - target: Target, - attrs: &[Attribute], - ) { - // many attributes don't make sense in combination with #[naked]. - // Notable attributes that are incompatible with `#[naked]` are: - // - // * `#[inline]` - // * `#[track_caller]` - // * `#[test]`, `#[ignore]`, `#[should_panic]` - // - // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains - // accurate. - const ALLOW_LIST: &[rustc_span::Symbol] = &[ - // conditional compilation - sym::cfg_trace, - sym::cfg_attr_trace, - // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`) - sym::test, - sym::ignore, - sym::should_panic, - sym::bench, - // diagnostics - sym::allow, - sym::warn, - sym::deny, - sym::forbid, - // FIXME(jdonszelmann): not used, because already a new-style attr (ugh) - sym::deprecated, - sym::must_use, - // abi, linking and FFI - sym::export_name, - sym::link_section, - sym::linkage, - sym::no_mangle, - sym::instruction_set, - sym::repr, - sym::align, - sym::rustc_std_internal_symbol, - // documentation - sym::doc, - ]; - + fn check_naked(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { match target { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => { @@ -674,73 +628,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) .emit(); } - - for other_attr in attrs { - // this covers "sugared doc comments" of the form `/// ...` - // it does not cover `#[doc = "..."]`, which is handled below - if other_attr.is_doc_comment() { - continue; - } - - // FIXME(jdonszelmann): once naked uses new-style parsing, - // this check can be part of the parser and be removed here - match other_attr { - Attribute::Parsed( - AttributeKind::Deprecation { .. } - | AttributeKind::Repr { .. } - | AttributeKind::Align { .. } - | AttributeKind::Cold(..) - | AttributeKind::Naked(..), - ) => { - continue; - } - Attribute::Parsed(AttributeKind::Inline(.., span)) => { - self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { - span: *span, - naked_span: attr_span, - attr: sym::inline.to_string(), - }); - - return; - } - // FIXME(jdonszelmann): make exhaustive - _ => {} - } - - if other_attr.has_name(sym::target_feature) { - if !self.tcx.features().naked_functions_target_feature() { - feature_err( - &self.tcx.sess, - sym::naked_functions_target_feature, - other_attr.span(), - "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions", - ).emit(); - - return; - } else { - continue; - } - } - - if !other_attr.has_any_name(ALLOW_LIST) - && !matches!(other_attr.path().as_slice(), [sym::rustfmt, ..]) - { - let path = other_attr.path(); - let path: Vec<_> = path - .iter() - .map(|s| if *s == kw::PathRoot { "" } else { s.as_str() }) - .collect(); - let other_attr_name = path.join("::"); - - self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { - span: other_attr.span(), - naked_span: attr_span, - attr: other_attr_name, - }); - - return; - } - } } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 587d9170f067c..94c8ae77ed767 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1070,17 +1070,6 @@ pub(crate) struct FeaturePreviouslyDeclared<'a> { pub prev_declared: &'a str, } -#[derive(Diagnostic)] -#[diag(passes_naked_functions_incompatible_attribute, code = E0736)] -pub(crate) struct NakedFunctionIncompatibleAttribute { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_naked_attribute)] - pub naked_span: Span, - pub attr: String, -} - #[derive(Diagnostic)] #[diag(passes_attr_only_in_functions)] pub(crate) struct AttrOnlyInFunctions { diff --git a/tests/ui/asm/naked-functions-inline.stderr b/tests/ui/asm/naked-functions-inline.stderr index 07d5f3bc49a94..91140a301edc9 100644 --- a/tests/ui/asm/naked-functions-inline.stderr +++ b/tests/ui/asm/naked-functions-inline.stderr @@ -1,26 +1,26 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:12:1 + --> $DIR/naked-functions-inline.rs:12:3 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here LL | #[inline] - | ^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` + | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:19:1 + --> $DIR/naked-functions-inline.rs:19:3 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` + | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:26:1 + --> $DIR/naked-functions-inline.rs:26:3 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here LL | #[inline(never)] - | ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` + | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` --> $DIR/naked-functions-inline.rs:33:19 @@ -28,7 +28,7 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here LL | #[cfg_attr(all(), inline(never))] - | ^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` + | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error: aborting due to 4 previous errors diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index 6661084861eee..915b54b3fc236 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -4,6 +4,15 @@ error[E0433]: failed to resolve: use of unresolved module or unlinked crate `a` LL | #[::a] | ^ use of unresolved module or unlinked crate `a` +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-invalid-attr.rs:56:3 + | +LL | #[::a] + | ^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]` +... +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:13:1 | @@ -33,15 +42,6 @@ LL | #[unsafe(naked)] LL | || {}; | ----- not a function definition -error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-invalid-attr.rs:56:1 - | -LL | #[::a] - | ^^^^^^ the `::a` attribute is incompatible with `#[unsafe(naked)]` -... -LL | #[unsafe(naked)] - | ---------------- function marked with `#[unsafe(naked)]` here - error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:22:5 | diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr index 8e601a14753bb..e57ec9cc59b2d 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr +++ b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr @@ -1,8 +1,8 @@ error[E0658]: `#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions - --> $DIR/feature-gate-naked_functions_target_feature.rs:7:1 + --> $DIR/feature-gate-naked_functions_target_feature.rs:7:3 | LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = note: see issue #138568 for more information = help: add `#![feature(naked_functions_target_feature)]` to the crate attributes to enable diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr index d3cafbc635086..3036080613882 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr @@ -1,17 +1,17 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/error-with-naked.rs:5:1 + --> $DIR/error-with-naked.rs:5:3 | LL | #[track_caller] - | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` + | ^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` LL | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/error-with-naked.rs:17:5 + --> $DIR/error-with-naked.rs:17:7 | LL | #[track_caller] - | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` + | ^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` LL | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here From 7462bc4a216c3cfe047d527be7ea5118954413b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 21 Jun 2025 11:29:05 +0200 Subject: [PATCH 4/6] Rewrite #[track_caller] --- Cargo.lock | 1 + compiler/rustc_ast_lowering/Cargo.toml | 1 + compiler/rustc_ast_lowering/src/expr.rs | 3 +- .../src/attributes.rs | 3 ++ .../src/attributes/codegen_attrs.rs | 22 +++++++- compiler/rustc_attr_parsing/src/context.rs | 5 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 54 +++++++++---------- .../rustc_hir_analysis/src/check/entry.rs | 9 ++-- compiler/rustc_lint/src/builtin.rs | 5 +- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 6 +-- .../lint/unused/unused-attr-duplicate.stderr | 24 ++++----- 12 files changed, 82 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a170ece0a0ddc..36c8677883dab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3282,6 +3282,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 6ac258155fe94..dc571f5c3671a 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -11,6 +11,7 @@ doctest = false rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f297bf9f4cfed..c2140514e3117 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use rustc_ast::ptr::P as AstP; use rustc_ast::*; use rustc_ast_pretty::pprust::expr_to_string; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::HirId; @@ -831,7 +832,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) { if self.tcx.features().async_fn_track_caller() && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) - && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)) + && find_attr!(*attrs, AttributeKind::TrackCaller(_)) { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 7677213bfe198..aaf9fd94c15a0 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -248,5 +248,8 @@ pub enum AttributeKind { /// Span of the attribute. span: Span, }, + + /// Represents `#[track_caller]` + TrackCaller(Span), // tidy-alphabetical-end } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 519618422314e..e725ded875286 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -11,7 +11,7 @@ use crate::session_diagnostics::NakedFunctionIncompatibleAttribute; pub(crate) struct OptimizeParser; impl SingleAttributeParser for OptimizeParser { - const PATH: &[rustc_span::Symbol] = &[sym::optimize]; + const PATH: &[Symbol] = &[sym::optimize]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none"); @@ -44,7 +44,7 @@ impl SingleAttributeParser for OptimizeParser { pub(crate) struct ColdParser; impl SingleAttributeParser for ColdParser { - const PATH: &[rustc_span::Symbol] = &[sym::cold]; + const PATH: &[Symbol] = &[sym::cold]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const TEMPLATE: AttributeTemplate = template!(Word); @@ -161,3 +161,21 @@ impl AttributeParser for NakedParser { Some(AttributeKind::Naked(span)) } } + +pub(crate) struct TrackCallerParser; + +impl SingleAttributeParser for TrackCallerParser { + const PATH: &[Symbol] = &[sym::track_caller]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + if !args.no_args() { + cx.expected_no_args(args.span().unwrap_or(cx.attr_span)); + return None; + } + + Some(AttributeKind::TrackCaller(cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index a8134dcd4fc4a..af9d66a5abab9 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,7 +15,9 @@ use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; -use crate::attributes::codegen_attrs::{ColdParser, NakedParser, OptimizeParser}; +use crate::attributes::codegen_attrs::{ + ColdParser, NakedParser, OptimizeParser, TrackCallerParser, +}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -113,6 +115,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, // tidy-alphabetical-end ]; diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index bfdfa28a0b4ad..26c18962925dc 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -95,17 +95,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // In these cases, we bail from performing further checks that are only meaningful for // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also // report a delayed bug, just in case `check_attr` isn't doing its job. - let fn_sig = || { + let fn_sig = |attr_span| { use DefKind::*; let def_kind = tcx.def_kind(did); if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { - tcx.dcx().span_delayed_bug( - attr.span(), - "this attribute can only be applied to functions", - ); + tcx.dcx() + .span_delayed_bug(attr_span, "this attribute can only be applied to functions"); None } }; @@ -123,6 +121,29 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), + AttributeKind::TrackCaller(attr_span) => { + let is_closure = tcx.is_closure_like(did.to_def_id()); + + if !is_closure + && let Some(fn_sig) = fn_sig(*attr_span) + && fn_sig.skip_binder().abi() != ExternAbi::Rust + { + tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); + } + if is_closure + && !tcx.features().closure_track_caller() + && !attr_span.allows_unstable(sym::closure_track_caller) + { + feature_err( + &tcx.sess, + sym::closure_track_caller, + *attr_span, + "`#[track_caller]` on closures is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER + } _ => {} } } @@ -205,29 +226,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, - sym::track_caller => { - let is_closure = tcx.is_closure_like(did.to_def_id()); - - if !is_closure - && let Some(fn_sig) = fn_sig() - && fn_sig.skip_binder().abi() != ExternAbi::Rust - { - tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() }); - } - if is_closure - && !tcx.features().closure_track_caller() - && !attr.span().allows_unstable(sym::closure_track_caller) - { - feature_err( - &tcx.sess, - sym::closure_track_caller, - attr.span(), - "`#[track_caller]` on closures is currently unstable", - ) - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER - } sym::export_name => { if let Some(s) = attr.value_str() { if s.as_str().contains('\0') { diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 3bad36da99909..b556683e80a54 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -1,14 +1,15 @@ use std::ops::Not; use rustc_abi::ExternAbi; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_hir as hir; use rustc_hir::Node; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_session::config::EntryFnType; +use rustc_span::Span; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; -use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; @@ -98,8 +99,10 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } - for attr in tcx.get_attrs(main_def_id, sym::track_caller) { - tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span }); + if let Some(attr_span) = + find_attr!(tcx.get_all_attrs(main_def_id), AttributeKind::TrackCaller(span) => *span) + { + tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span }); error = true; } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index dedea54f8e081..4b8d4c7efd232 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -21,6 +21,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::expr_to_string; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_feature::GateIssue; use rustc_hir as hir; @@ -1181,11 +1182,11 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { if fn_kind.asyncness().is_async() && !cx.tcx.features().async_fn_track_caller() // Now, check if the function has the `#[track_caller]` attribute - && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller) + && let Some(attr_span) = find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::TrackCaller(span) => *span) { cx.emit_span_lint( UNGATED_ASYNC_FN_TRACK_CALLER, - attr.span(), + attr_span, BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess }, ); } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index ed1737bee3306..8c937cbd4b8dc 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -293,6 +293,7 @@ fn emit_malformed_attribute( | sym::deprecated | sym::optimize | sym::cold + | sym::track_caller ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 595d03c892573..594da0fc5c924 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -158,6 +158,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::Naked(attr_span)) => { self.check_naked(hir_id, *attr_span, span, target) } + Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => { + self.check_track_caller(hir_id, *attr_span, attrs, span, target) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -184,9 +187,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_target_feature(hir_id, attr, span, target, attrs) } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), - [sym::track_caller, ..] => { - self.check_track_caller(hir_id, attr.span(), attrs, span, target) - } [sym::doc, ..] => self.check_doc_attrs( attr, hir_id, diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 03ce97570144e..3fa395d2614b5 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -102,18 +102,6 @@ note: attribute also specified here LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:79:1 - | -LL | #[track_caller] - | ^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:78:1 - | -LL | #[track_caller] - | ^^^^^^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:92:1 | @@ -289,5 +277,17 @@ note: attribute also specified here LL | #[cold] | ^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:79:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:78:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + error: aborting due to 23 previous errors From 9275099e903a0cc9a6f9062ce65ba8d027d459cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 21 Jun 2025 12:42:24 +0200 Subject: [PATCH 5/6] fix clippy --- src/tools/clippy/clippy_lints/src/eta_reduction.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 6ed7c87915b27..b0077a9b05fe8 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -7,6 +7,7 @@ use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, }; use rustc_abi::ExternAbi; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; @@ -155,7 +156,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx let sig = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => { // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` - if cx.tcx.has_attr(*def, sym::track_caller) { + if find_attr!(cx.tcx.get_all_attrs(*def), AttributeKind::TrackCaller(..)) { return; } @@ -236,7 +237,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }, ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) - && !cx.tcx.has_attr(method_def_id, sym::track_caller) + && !find_attr!(cx.tcx.get_all_attrs(method_def_id), AttributeKind::TrackCaller(..)) && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) { let mut app = Applicability::MachineApplicable; From ae97fcdcbe1976f96ea857e2315fa032bf286c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 21 Jun 2025 13:04:24 +0200 Subject: [PATCH 6/6] fix 142783 --- .../rustc_attr_parsing/src/attributes/codegen_attrs.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 4 +--- .../ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr | 8 ++++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index e725ded875286..7c7f1d0c55bbb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,7 +1,7 @@ use rustc_attr_data_structures::{AttributeKind, OptimizeAttr}; use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; use crate::context::{AcceptContext, FinalizeContext, Stage}; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 594da0fc5c924..3c8a23723ebc6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -704,9 +704,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - for attr in attrs { - self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "track_caller"); - } + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "track_caller"); } _ => { self.dcx().emit_err(errors::TrackedCallerWrongLocation { diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr index e22d812c8b038..6088945b829cb 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr @@ -1,8 +1,12 @@ -error: malformed `track_caller` attribute input +error[E0565]: malformed `track_caller` attribute input --> $DIR/error-odd-syntax.rs:1:1 | LL | #[track_caller(1)] - | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]` + | ^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[track_caller]` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0565`.