From 008ea3a340e7a1c9dc8d6e274e9e440e01f34e87 Mon Sep 17 00:00:00 2001
From: mejrs <59372212+mejrs@users.noreply.github.com>
Date: Tue, 5 May 2026 18:26:18 +0200
Subject: [PATCH 01/12] rustc_on_unimplemented: introduce format specifiers as
printing options for the annotated item.
---
.../src/attributes/diagnostic/mod.rs | 45 ++++++++------
compiler/rustc_hir/src/attrs/diagnostic.rs | 20 +++---
compiler/rustc_parse_format/src/lib.rs | 6 +-
compiler/rustc_parse_format/src/tests.rs | 4 +-
compiler/rustc_span/src/symbol.rs | 1 -
.../traits/on_unimplemented.rs | 6 +-
library/core/src/ops/function.rs | 12 ++--
src/doc/rustc-dev-guide/src/diagnostics.md | 7 ++-
.../on_unimplemented/broken_format.stderr | 12 ++--
...options_of_the_internal_rustc_attribute.rs | 7 ++-
...ons_of_the_internal_rustc_attribute.stderr | 62 ++++++++++++-------
tests/ui/on-unimplemented/format_specs.rs | 13 ++++
tests/ui/on-unimplemented/format_specs.stderr | 22 +++++++
13 files changed, 143 insertions(+), 74 deletions(-)
create mode 100644 tests/ui/on-unimplemented/format_specs.rs
create mode 100644 tests/ui/on-unimplemented/format_specs.stderr
diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
index 9b4ffc7ea27f6..c2dfc4a147ee3 100644
--- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
@@ -396,9 +396,7 @@ pub(crate) fn parse_format_string(
.map(|piece| match piece {
RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)),
RpfPiece::NextArgument(arg) => {
- warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
- let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal);
- Piece::Arg(arg)
+ Piece::Arg(parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal))
}
})
.collect();
@@ -415,15 +413,25 @@ fn parse_arg(
) -> FormatArg {
let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
- match arg.position {
+ let mut check_format = true;
+
+ let ret = match arg.position {
// Something like "hello {name}"
Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
(Mode::RustcOnUnimplemented, sym::ItemContext) => FormatArg::ItemContext,
- // Like `{This}`, but sugared.
- // FIXME(mejrs) maybe rename/rework this or something
- // if we want to apply this to other attrs?
- (Mode::RustcOnUnimplemented, sym::Trait) => FormatArg::Trait,
+ // `{This:ty}`
+ (Mode::RustcOnUnimplemented, sym::This) => match arg.format.ty {
+ "resolved" => {
+ check_format = false;
+ FormatArg::ThisResolved
+ }
+ "path" => {
+ check_format = false;
+ FormatArg::ThisPath
+ }
+ _ => FormatArg::This,
+ },
// Some diagnostic attributes can use `{This}` to refer to the annotated item.
// For those that don't, we continue and maybe use it as a generic parameter.
@@ -431,10 +439,7 @@ fn parse_arg(
// FIXME(mejrs) `DiagnosticOnUnimplemented` is intentionally not here;
// that requires lang approval which is best kept for a standalone PR.
(
- Mode::RustcOnUnimplemented
- | Mode::DiagnosticOnUnknown
- | Mode::DiagnosticOnMove
- | Mode::DiagnosticOnUnmatchArgs,
+ Mode::DiagnosticOnUnknown | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnmatchArgs,
sym::This,
) => FormatArg::This,
@@ -471,11 +476,11 @@ fn parse_arg(
attr: mode.as_str(),
allowed: mode.allowed_format_arguments(),
});
- return FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}")));
+ FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}")))
}
},
- // `{:1}` and `{}` are ignored
+ // `{1}` and `{}` are ignored
Position::ArgumentIs(idx) => {
warnings.push(FormatWarning::IndexedArgument { span });
FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}")))
@@ -484,7 +489,11 @@ fn parse_arg(
warnings.push(FormatWarning::PositionalArgument { span });
FormatArg::AsIs(sym::empty_braces)
}
+ };
+ if check_format {
+ warn_on_format_spec(&arg.format, warnings, input_span, is_source_literal);
}
+ ret
}
/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
@@ -495,12 +504,8 @@ fn warn_on_format_spec(
input_span: Span,
is_source_literal: bool,
) {
- if spec.ty != "" {
- let span = spec
- .ty_span
- .as_ref()
- .map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
- .unwrap_or(input_span);
+ if let Some(ty_span) = &spec.ty_span {
+ let span = slice_span(input_span, ty_span.clone(), is_source_literal);
warnings.push(FormatWarning::InvalidSpecifier { span })
}
}
diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs
index 7cbb0ea45b969..7d4ae803cee59 100644
--- a/compiler/rustc_hir/src/attrs/diagnostic.rs
+++ b/compiler/rustc_hir/src/attrs/diagnostic.rs
@@ -147,11 +147,12 @@ impl FormatString {
};
ret.push_str(&slf);
}
+ Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
// It's only `rustc_onunimplemented` from here
- Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
- Piece::Arg(FormatArg::Trait) => {
- let _ = fmt::write(&mut ret, format_args!("{}", &args.this_sugared));
+ Piece::Arg(FormatArg::ThisPath) => ret.push_str(&args.this_path),
+ Piece::Arg(FormatArg::ThisResolved) => {
+ let _ = fmt::write(&mut ret, format_args!("{}", &args.this_resolved));
}
Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
}
@@ -197,7 +198,7 @@ impl FormatString {
/// ```rust,ignore (just an example)
/// FormatArgs {
/// this: "FromResidual",
-/// this_sugared: "FromResidual