From 90b278f4cf94374f0ca2ed3786079ac8655a2416 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 4 Oct 2026 12:16:45 +0200 Subject: [PATCH 1/6] Add track-diagnostics test --- tests/ui/track-diagnostics/track8.rs | 12 ++++++++++++ tests/ui/track-diagnostics/track8.stderr | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/ui/track-diagnostics/track8.rs create mode 100644 tests/ui/track-diagnostics/track8.stderr diff --git a/tests/ui/track-diagnostics/track8.rs b/tests/ui/track-diagnostics/track8.rs new file mode 100644 index 0000000000000..13fe959a30ed5 --- /dev/null +++ b/tests/ui/track-diagnostics/track8.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Z track-diagnostics +//@ dont-require-annotations: NOTE + +// Normalize the emitted location so this doesn't need +// updating everytime someone adds or removes a line. +//@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" +#![crate_type = "lib"] + +#[repr] //~ ERROR: malformed `repr` attribute input +#[diagnostic::ferris] //~ WARN unknown diagnostic attribute +pub mod empty {} \ No newline at end of file diff --git a/tests/ui/track-diagnostics/track8.stderr b/tests/ui/track-diagnostics/track8.stderr new file mode 100644 index 0000000000000..0e1ee1f5a9333 --- /dev/null +++ b/tests/ui/track-diagnostics/track8.stderr @@ -0,0 +1,21 @@ +error[E0539]: malformed `repr` attribute input + --> $DIR/track8.rs:LL:CC + | +LL | #[repr] + | ^^^^ expected this to be a list + | + = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/interface.rs:LL:CC + = note: for more information, visit + +warning: unknown diagnostic attribute + --> $DIR/track8.rs:LL:CC + | +LL | #[diagnostic::ferris] + | ^^^^^^ + | + = note: -Ztrack-diagnostics: created at compiler/rustc_interface/src/passes.rs:LL:CC + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0539`. From b4af75e56294740c8f3b2d400b2df7ee270a889e Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 4 Oct 2026 12:25:20 +0200 Subject: [PATCH 2/6] rustc_attr_parsing: properly track error locations --- compiler/rustc_attr_parsing/src/context.rs | 33 ++++++++++++++++++++ compiler/rustc_attr_parsing/src/interface.rs | 1 + tests/ui/track-diagnostics/track8.stderr | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 72839643b4dc3..b4221048cfc82 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -408,6 +408,7 @@ pub struct AcceptContext<'f, 'sess> { } impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { + #[track_caller] pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { self.cx.emit_err(diag) } @@ -497,6 +498,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// The provided span is used as a fallback for diagnostic generation in case `arg` does not /// contain any. It should be the span of the node that contains `arg`. + #[track_caller] pub(crate) fn expect_single_element_list<'arg>( &mut self, arg: &'arg ArgParser, @@ -529,6 +531,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_list<'arg>( &mut self, args: &'arg ArgParser, @@ -550,6 +553,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_single<'arg>( &mut self, list: &'arg MetaItemListParser, @@ -589,6 +593,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_name_value<'arg, Arg>( &mut self, arg: &'arg Arg, @@ -609,6 +614,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_no_args<'arg>(&mut self, arg: &'arg ArgParser) -> Option<()> { if let Err(span) = arg.as_no_args() { self.adcx().expected_no_args(span); @@ -631,6 +637,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_string_literal<'arg, Arg>(&mut self, arg: &'arg Arg) -> Option where Arg: ExpectStringLiteral, @@ -644,6 +651,7 @@ pub(crate) trait ExpectNameValue { where Self: 'a; + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -655,6 +663,7 @@ pub(crate) trait ExpectNameValue { impl ExpectNameValue for MetaItemOrLitParser { type Output<'a> = (Ident, &'a NameValueParser); + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -673,6 +682,7 @@ impl ExpectNameValue for MetaItemOrLitParser { impl ExpectNameValue for MetaItemParser { type Output<'a> = (Ident, &'a NameValueParser); + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -697,6 +707,7 @@ impl ExpectNameValue for MetaItemParser { impl ExpectNameValue for ArgParser { type Output<'a> = &'a NameValueParser; + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -713,11 +724,13 @@ impl ExpectNameValue for ArgParser { } pub(crate) trait ExpectStringLiteral { + #[track_caller] fn expect_string_literal<'f, 'sess>(&self, cx: &mut AcceptContext<'f, 'sess>) -> Option; } impl ExpectStringLiteral for NameValueParser { + #[track_caller] fn expect_string_literal<'f, 'sess>( &self, cx: &mut AcceptContext<'f, 'sess>, @@ -731,6 +744,7 @@ impl ExpectStringLiteral for NameValueParser { } impl ExpectStringLiteral for MetaItemOrLitParser { + #[track_caller] fn expect_string_literal<'f, 'sess>( &self, cx: &mut AcceptContext<'f, 'sess>, @@ -893,6 +907,7 @@ pub enum ShouldEmit { } impl ShouldEmit { + #[track_caller] pub(crate) fn emit_err(self, diag: Diag<'_>) -> ErrorGuaranteed { match self { ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit_err(), @@ -912,6 +927,7 @@ pub(crate) struct AttributeDiagnosticContext<'a, 'f, 'sess> { } impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { + #[track_caller] fn emit_parse_error( &mut self, mut span: Span, @@ -950,6 +966,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// Helpers that can be used to generate errors during attribute parsing. impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { + #[track_caller] pub(crate) fn expected_integer_literal_in_range( &mut self, span: Span, @@ -964,6 +981,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// The provided span is used as a fallback in case `args` does not contain any. It should be /// the span of the node that contains `args`. + #[track_caller] pub(crate) fn expected_list(&mut self, span: Span, args: &ArgParser) -> ErrorGuaranteed { let span = match args { ArgParser::NoArgs => span, @@ -973,6 +991,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList) } + #[track_caller] pub(crate) fn expected_list_with_num_args_or_more( &mut self, args: usize, @@ -984,45 +1003,54 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ) } + #[track_caller] pub(crate) fn expected_list_or_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs) } + #[track_caller] pub(crate) fn expected_nv_or_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs) } + #[track_caller] pub(crate) fn expected_non_empty_string_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNonEmptyStringLiteral) } + #[track_caller] pub(crate) fn expected_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } /// Emit an error that a `name` was expected here + #[track_caller] pub(crate) fn expected_identifier(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier) } /// Emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. + #[track_caller] fn expected_name_value(&mut self, span: Span, name: Option) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } /// Emit an error that a `name = value` argument is missing in a list of name-value pairs. + #[track_caller] pub(crate) fn missing_name_value(&mut self, span: Span, name: Symbol) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::MissingNameValue(name)) } /// Emit an error that a `name = value` pair was found where that name was already seen. + #[track_caller] pub(crate) fn duplicate_key(&mut self, span: Span, key: Symbol) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key)) } /// An error that should be emitted when a [`MetaItemOrLitParser`] /// was expected *not* to be a literal, but instead a meta item. + #[track_caller] pub(crate) fn expected_not_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNotLiteral) } @@ -1031,6 +1059,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// The `provided_arguments` argument allows distinguishing between "expected an argument here" /// (when zero arguments are provided) and "expect a single argument here" (when two or more /// arguments are provided). + #[track_caller] pub(crate) fn expected_single_argument( &mut self, span: Span, @@ -1045,11 +1074,13 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { self.emit_parse_error(span, reason) } + #[track_caller] pub(crate) fn expected_at_least_one_argument(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument) } /// Produces an error along the lines of `expected one of [foo, meow]` + #[track_caller] pub(crate) fn expected_specific_argument( &mut self, span: Span, @@ -1067,6 +1098,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// Produces an error along the lines of `expected one of [foo, meow] as an argument`. /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument). + #[track_caller] pub(crate) fn expected_specific_argument_and_list( &mut self, span: Span, @@ -1083,6 +1115,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { } /// produces an error along the lines of `expected one of ["foo", "meow"]` + #[track_caller] pub(crate) fn expected_specific_argument_strings( &mut self, span: Span, diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 95412c34be371..4ce284a0a9d5a 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -304,6 +304,7 @@ impl<'sess> AttributeParser<'sess> { self.sess().dcx() } + #[track_caller] pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { self.should_emit.emit_err(self.sess.dcx().create_err(diag)) } diff --git a/tests/ui/track-diagnostics/track8.stderr b/tests/ui/track-diagnostics/track8.stderr index 0e1ee1f5a9333..fdc33519e3cf0 100644 --- a/tests/ui/track-diagnostics/track8.stderr +++ b/tests/ui/track-diagnostics/track8.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `repr` attribute input LL | #[repr] | ^^^^ expected this to be a list | - = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/interface.rs:LL:CC + = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/attributes/repr.rs:LL:CC = note: for more information, visit warning: unknown diagnostic attribute From 6d998877ff20d595e010789ba77cdc6d78bdc482 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 4 Oct 2026 23:25:08 +0200 Subject: [PATCH 3/6] rustc_attr_parsing: properly track lint locations --- compiler/rustc_attr_parsing/src/context.rs | 10 ++++- compiler/rustc_middle/src/lint.rs | 4 +- .../clippy/clippy_utils/src/diagnostics.rs | 8 +++- tests/ui/track-diagnostics/track8.rs | 17 ++++++-- tests/ui/track-diagnostics/track8.stderr | 42 +++++++++++++++++-- 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b4221048cfc82..060fd74c1db21 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -12,7 +12,7 @@ use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{AttrPath, Attribute, AttributeKind}; use rustc_data_structures::sync::{DynSend, DynSync}; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; +use rustc_errors::{Diag, DiagCtxtHandle, DiagLocation, Diagnostic, Level, MultiSpan}; use rustc_feature::AttributeStability; use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; use rustc_lint_defs::{Lint, LintId}; @@ -416,15 +416,21 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. + #[track_caller] pub(crate) fn emit_lint( &mut self, lint: &'static Lint, diagnostic: impl for<'x> Diagnostic<'x> + DynSend + DynSync + 'static, span: impl Into, ) { + let emitted_at = DiagLocation::caller(); self.emit_lint_inner( lint, - EmitAttribute(Box::new(move |dcx, level, _| diagnostic.into_diag(dcx, level))), + EmitAttribute(Box::new(move |dcx, level, _| { + let mut diag = diagnostic.into_diag(dcx, level); + diag.emitted_at = emitted_at; + diag + })), span, ); } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 3655d4594aa38..8822ffee65c29 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp::min; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, DiagLocation, Diagnostic, EmissionOverride, MultiSpan}; +use rustc_errors::{Diag, Diagnostic, EmissionOverride, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_lint_defs::{ EditionFcw, FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, @@ -494,8 +494,6 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( } else { Diag::new(sess.dcx(), err_level, "") }; - // FIXME: Find a nicer way to expose the `DiagLocation` - err.emitted_at = DiagLocation::caller(); if let Some(span) = span && err.span.primary_span().is_none() diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index e063b3d6cc632..922e965bbb06e 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,7 +8,7 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, Diagnostic, Level, MultiSpan}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagLocation, DiagMessage, Diagnostic, Level, MultiSpan}; #[cfg(debug_assertions)] use rustc_errors::{SubstitutionPart, Suggestions}; use rustc_hir::HirId; @@ -251,12 +251,15 @@ where } let sp = sp.into(); + let emitted_at = DiagLocation::caller(); + #[expect(clippy::disallowed_methods)] cx.emit_span_lint( lint, sp.clone(), ClippyDiag(|diag: &mut Diag<'_>| { diag.primary_message(msg); + diag.emitted_at = emitted_at; diag.span(sp); f(diag); docs_link(diag, lint); @@ -329,6 +332,8 @@ pub fn span_lint_hir_and_then( msg: impl Into, f: impl FnOnce(&mut Diag<'_>), ) { + let emitted_at = DiagLocation::caller(); + #[expect(clippy::disallowed_methods)] cx.tcx.emit_node_span_lint( lint, @@ -336,6 +341,7 @@ pub fn span_lint_hir_and_then( sp, rustc_errors::DiagDecorator(|diag| { diag.primary_message(msg); + diag.emitted_at = emitted_at; f(diag); docs_link(diag, lint); diff --git a/tests/ui/track-diagnostics/track8.rs b/tests/ui/track-diagnostics/track8.rs index 13fe959a30ed5..6690d8fca63df 100644 --- a/tests/ui/track-diagnostics/track8.rs +++ b/tests/ui/track-diagnostics/track8.rs @@ -1,12 +1,23 @@ //@ compile-flags: -Z track-diagnostics +//@ forbid-output:interface //@ dont-require-annotations: NOTE +#![warn(unreachable_cfg_select_predicates)] // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" //@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" -#![crate_type = "lib"] -#[repr] //~ ERROR: malformed `repr` attribute input +cfg_select!{ + _ => {}, + _ => {}, //~ WARN unreachable configuration predicate +} + +#[repr(ferris)] //~ ERROR: malformed `repr` attribute input #[diagnostic::ferris] //~ WARN unknown diagnostic attribute -pub mod empty {} \ No newline at end of file +#[doc(ferris)] //~ WARN unknown `doc` attribute `ferris` +fn main(){ + // Also check locations for buffered lints + #[doc(ferris)] //~ WARN unknown `doc` attribute `ferris` + println!(); +} diff --git a/tests/ui/track-diagnostics/track8.stderr b/tests/ui/track-diagnostics/track8.stderr index fdc33519e3cf0..8a729ca91eb32 100644 --- a/tests/ui/track-diagnostics/track8.stderr +++ b/tests/ui/track-diagnostics/track8.stderr @@ -1,8 +1,34 @@ +warning: unreachable configuration predicate + --> $DIR/track8.rs:LL:CC + | +LL | _ => {}, + | - always matches +LL | _ => {}, + | ^ this configuration predicate is never reached + | + = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/attributes/cfg_select.rs:LL:CC +note: the lint level is defined here + --> $DIR/track8.rs:LL:CC + | +LL | #![warn(unreachable_cfg_select_predicates)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unknown `doc` attribute `ferris` + --> $DIR/track8.rs:LL:CC + | +LL | #[doc(ferris)] + | ^^^^^^ + | + = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/attributes/doc.rs:LL:CC + = note: `#[warn(invalid_doc_attributes)]` on by default + error[E0539]: malformed `repr` attribute input --> $DIR/track8.rs:LL:CC | -LL | #[repr] - | ^^^^ expected this to be a list +LL | #[repr(ferris)] + | ^^^^^------^ + | | + | valid arguments are `align`, `packed`, `Rust`, `C`, `simd`, `transparent`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize` or `usize` | = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/attributes/repr.rs:LL:CC = note: for more information, visit @@ -13,9 +39,17 @@ warning: unknown diagnostic attribute LL | #[diagnostic::ferris] | ^^^^^^ | - = note: -Ztrack-diagnostics: created at compiler/rustc_interface/src/passes.rs:LL:CC + = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs:LL:CC = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default -error: aborting due to 1 previous error; 1 warning emitted +warning: unknown `doc` attribute `ferris` + --> $DIR/track8.rs:LL:CC + | +LL | #[doc(ferris)] + | ^^^^^^ + | + = note: -Ztrack-diagnostics: created at compiler/rustc_attr_parsing/src/attributes/doc.rs:LL:CC + +error: aborting due to 1 previous error; 4 warnings emitted For more information about this error, try `rustc --explain E0539`. From 9aa65cd49e0c76c499bf0b7a0ac4b80cab6acaf2 Mon Sep 17 00:00:00 2001 From: Noa Date: Wed, 23 Sep 2026 14:49:21 -0500 Subject: [PATCH 4/6] Document panics --- library/core/src/fmt/builders.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 6a44f9ef5374e..c148c2873a90e 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1075,8 +1075,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// # Panics /// - /// `key` must be called before `value` and each call to `key` must be followed - /// by a corresponding call to `value`. Otherwise this method will panic. + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. /// /// # Examples /// @@ -1136,6 +1137,12 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// This method is equivalent to [`DebugMap::key`], but formats the /// key using a provided closure rather than by calling [`Debug::fmt`]. /// + /// # Panics + /// + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. + /// /// # Examples /// /// ``` @@ -1179,8 +1186,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// # Panics /// - /// `key` must be called before `value` and each call to `key` must be followed - /// by a corresponding call to `value`. Otherwise this method will panic. + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. /// /// # Examples /// @@ -1229,6 +1237,12 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// This method is equivalent to [`DebugMap::value`], but formats the /// value using a provided closure rather than by calling [`Debug::fmt`]. /// + /// # Panics + /// + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. + /// /// # Examples /// /// ``` @@ -1359,8 +1373,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// # Panics /// - /// `key` must be called before `value` and each call to `key` must be followed - /// by a corresponding call to `value`. Otherwise this method will panic. + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. /// /// # Examples /// From b47661be5fe3b572912e74a8dec54818a4b3a504 Mon Sep 17 00:00:00 2001 From: Noa Date: Wed, 23 Sep 2026 14:12:14 -0500 Subject: [PATCH 5/6] Replace type parameters with APIT --- library/core/src/fmt/builders.rs | 54 +++++++++++++++----------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index c148c2873a90e..5f973eee8bc9e 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -212,10 +212,11 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// ); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + pub fn field_with( + &mut self, + name: &str, + value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.field(name, &DebugOnce(Cell::new(Some(value_fmt)))) } @@ -430,10 +431,10 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// ); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + pub fn field_with( + &mut self, + value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.field(&DebugOnce(Cell::new(Some(value_fmt)))) } @@ -552,10 +553,7 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { self.has_fields = true; } - fn entry_with(&mut self, entry_fmt: F) - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + fn entry_with(&mut self, entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) { self.entry(&DebugOnce(Cell::new(Some(entry_fmt)))); } @@ -661,10 +659,10 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// ); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn entry_with(&mut self, entry_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + pub fn entry_with( + &mut self, + entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.inner.entry_with(entry_fmt); self } @@ -878,10 +876,10 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// ); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn entry_with(&mut self, entry_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + pub fn entry_with( + &mut self, + entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.inner.entry_with(entry_fmt); self } @@ -1171,10 +1169,10 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn key_with(&mut self, key_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + pub fn key_with( + &mut self, + key_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.key(&DebugOnce(Cell::new(Some(key_fmt)))) } @@ -1271,10 +1269,10 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn value_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + pub fn value_with( + &mut self, + value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.value(&DebugOnce(Cell::new(Some(value_fmt)))) } From 384b61a67afcdd1c45c0454c36f6bdb1a8033ec9 Mon Sep 17 00:00:00 2001 From: Noa Date: Wed, 23 Sep 2026 13:36:42 -0500 Subject: [PATCH 6/6] Stabilize `debug_closure_helpers` --- compiler/rustc_middle/src/lib.rs | 2 +- compiler/rustc_parse/src/lib.rs | 2 +- library/core/src/fmt/builders.rs | 24 ++++++------------------ library/coretests/tests/lib.rs | 1 - 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index d964faa30b1f8..695eca6225858 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -27,6 +27,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::direct_use_of_rustc_type_ir)] +#![cfg_attr(bootstrap, feature(debug_closure_helpers))] #![cfg_attr(doc, feature(intra_doc_pointers))] #![feature(allocator_ext)] #![feature(associated_type_defaults)] @@ -34,7 +35,6 @@ #![feature(const_default)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] -#![feature(debug_closure_helpers)] #![feature(decl_macro)] #![feature(default_field_values)] #![feature(deref_patterns)] diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 8a73215351aea..edd280743e437 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,8 +1,8 @@ //! The main parser interface. // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(debug_closure_helpers))] #![cfg_attr(test, feature(iter_order_by))] -#![feature(debug_closure_helpers)] #![feature(default_field_values)] #![feature(deref_patterns)] #![feature(iter_intersperse)] diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 5f973eee8bc9e..20ce752dd7c41 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -187,8 +187,6 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Bar { @@ -211,7 +209,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// r#"Bar { bar: 0x0000000a, another: "Hello World" }"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] pub fn field_with( &mut self, name: &str, @@ -409,8 +407,6 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(i32, String); @@ -430,7 +426,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// r#"Foo(0x0000000a, "Hello World")"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] pub fn field_with( &mut self, value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, @@ -637,8 +633,6 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec, Vec); @@ -658,7 +652,7 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// "{[10, 11], {12, 13}}", /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] pub fn entry_with( &mut self, entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, @@ -854,8 +848,6 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec, Vec); @@ -875,7 +867,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// "[[10, 11], {12, 13}]", /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] pub fn entry_with( &mut self, entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, @@ -1144,8 +1136,6 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec<(String, i32)>); @@ -1168,7 +1158,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// r#"{entry A: 0x0000000a, entry B: 0x0000000b}"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] pub fn key_with( &mut self, key_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, @@ -1244,8 +1234,6 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec<(String, i32)>); @@ -1268,7 +1256,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// r#"{entry A: 0x0000000a, entry B: 0x0000000b}"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] pub fn value_with( &mut self, value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 9b623d1c5d25b..1fdb0c820d662 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -47,7 +47,6 @@ #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] -#![feature(debug_closure_helpers)] #![feature(dec2flt)] #![feature(duration_constants)] #![feature(duration_constructors)]