Skip to content

Commit d3030bc

Browse files
committed
rustc_on_unimplemented: introduce format specifiers as printing options for the annotated item.
1 parent cb40c25 commit d3030bc

12 files changed

Lines changed: 141 additions & 72 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,7 @@ pub(crate) fn parse_format_string(
396396
.map(|piece| match piece {
397397
RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)),
398398
RpfPiece::NextArgument(arg) => {
399-
warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
400-
let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal);
401-
Piece::Arg(arg)
399+
Piece::Arg(parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal))
402400
}
403401
})
404402
.collect();
@@ -415,26 +413,33 @@ fn parse_arg(
415413
) -> FormatArg {
416414
let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
417415

418-
match arg.position {
416+
let mut check_format = true;
417+
418+
let ret = match arg.position {
419419
// Something like "hello {name}"
420420
Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
421421
(Mode::RustcOnUnimplemented, sym::ItemContext) => FormatArg::ItemContext,
422422

423-
// Like `{This}`, but sugared.
424-
// FIXME(mejrs) maybe rename/rework this or something
425-
// if we want to apply this to other attrs?
426-
(Mode::RustcOnUnimplemented, sym::Trait) => FormatArg::Trait,
423+
// `{This:ty}`
424+
(Mode::RustcOnUnimplemented, sym::This) => match arg.format.ty {
425+
"resolved" => {
426+
check_format = false;
427+
FormatArg::ThisResolved
428+
}
429+
"path" => {
430+
check_format = false;
431+
FormatArg::ThisPath
432+
}
433+
_ => FormatArg::This,
434+
},
427435

428436
// Some diagnostic attributes can use `{This}` to refer to the annotated item.
429437
// For those that don't, we continue and maybe use it as a generic parameter.
430438
//
431439
// FIXME(mejrs) `DiagnosticOnUnimplemented` is intentionally not here;
432440
// that requires lang approval which is best kept for a standalone PR.
433441
(
434-
Mode::RustcOnUnimplemented
435-
| Mode::DiagnosticOnUnknown
436-
| Mode::DiagnosticOnMove
437-
| Mode::DiagnosticOnUnmatchArgs,
442+
Mode::DiagnosticOnUnknown | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnmatchArgs,
438443
sym::This,
439444
) => FormatArg::This,
440445

@@ -471,11 +476,11 @@ fn parse_arg(
471476
attr: mode.as_str(),
472477
allowed: mode.allowed_format_arguments(),
473478
});
474-
return FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}")));
479+
FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}")))
475480
}
476481
},
477482

478-
// `{:1}` and `{}` are ignored
483+
// `{1}` and `{}` are ignored
479484
Position::ArgumentIs(idx) => {
480485
warnings.push(FormatWarning::IndexedArgument { span });
481486
FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}")))
@@ -484,7 +489,11 @@ fn parse_arg(
484489
warnings.push(FormatWarning::PositionalArgument { span });
485490
FormatArg::AsIs(sym::empty_braces)
486491
}
492+
};
493+
if check_format {
494+
warn_on_format_spec(&arg.format, warnings, input_span, is_source_literal);
487495
}
496+
ret
488497
}
489498

490499
/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
@@ -495,12 +504,8 @@ fn warn_on_format_spec(
495504
input_span: Span,
496505
is_source_literal: bool,
497506
) {
498-
if spec.ty != "" {
499-
let span = spec
500-
.ty_span
501-
.as_ref()
502-
.map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
503-
.unwrap_or(input_span);
507+
if let Some(ty_span) = &spec.ty_span {
508+
let span = slice_span(input_span, ty_span.clone(), is_source_literal);
504509
warnings.push(FormatWarning::InvalidSpecifier { span })
505510
}
506511
}

compiler/rustc_hir/src/attrs/diagnostic.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,12 @@ impl FormatString {
147147
};
148148
ret.push_str(&slf);
149149
}
150+
Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
150151

151152
// It's only `rustc_onunimplemented` from here
152-
Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
153-
Piece::Arg(FormatArg::Trait) => {
154-
let _ = fmt::write(&mut ret, format_args!("{}", &args.this_sugared));
153+
Piece::Arg(FormatArg::ThisPath) => ret.push_str(&args.this_path),
154+
Piece::Arg(FormatArg::ThisResolved) => {
155+
let _ = fmt::write(&mut ret, format_args!("{}", &args.this_resolved));
155156
}
156157
Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
157158
}
@@ -197,7 +198,7 @@ impl FormatString {
197198
/// ```rust,ignore (just an example)
198199
/// FormatArgs {
199200
/// this: "FromResidual",
200-
/// this_sugared: "FromResidual<Option<Infallible>>",
201+
/// this_resolved: "FromResidual<Option<Infallible>>",
201202
/// item_context: "an async function",
202203
/// generic_args: [("Self", "u32"), ("R", "Option<Infallible>")],
203204
/// }
@@ -206,7 +207,8 @@ impl FormatString {
206207
pub struct FormatArgs {
207208
/// The name of the item the attribute is on.
208209
pub this: String,
209-
pub this_sugared: String = String::new(),
210+
pub this_resolved: String = String::new(),
211+
pub this_path: String = String::new(),
210212
pub item_context: &'static str = "",
211213
pub generic_args: Vec<(Symbol, String)> = Vec::new(),
212214
}
@@ -226,10 +228,12 @@ pub enum FormatArg {
226228
},
227229
// `{Self}`
228230
SelfUpper,
229-
/// `{This}` or `{TraitName}`
231+
/// `{This}` and `{This:name}`.
230232
This,
231-
/// The sugared form of the trait
232-
Trait,
233+
/// The sugared form: `{This:sugared}`.
234+
ThisResolved,
235+
/// The full path: `{This:path}`.
236+
ThisPath,
233237
/// what we're in, like a function, method, closure etc.
234238
ItemContext,
235239
/// What the user typed, if it doesn't match anything we can use.

compiler/rustc_parse_format/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -750,15 +750,15 @@ impl<'input> Parser<'input> {
750750
spec
751751
}
752752

753-
/// Always returns an empty `FormatSpec`
753+
/// Always returns an empty `FormatSpec`, except for the `ty` and `ty_span` fields.
754754
fn diagnostic(&mut self) -> FormatSpec<'input> {
755755
let mut spec = FormatSpec::default();
756756

757-
let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else {
757+
let Some((Range { start, .. }, _)) = self.consume_pos(':') else {
758758
return spec;
759759
};
760760

761-
spec.ty = self.string(start_idx);
761+
spec.ty = self.string(self.input_vec_index);
762762
spec.ty_span = {
763763
let end = self.input_vec_index2range(self.input_vec_index).start;
764764
Some(start..end)

compiler/rustc_span/src/symbol.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,6 @@ symbols! {
317317
Target,
318318
This,
319319
TokenStream,
320-
Trait,
321320
TrivialClone,
322321
Try,
323322
TryCaptureGeneric,

compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
217217
}));
218218

219219
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
220-
let this_sugared = trait_pred.trait_ref.print_trait_sugared().to_string();
220+
let this_resolved = trait_pred.trait_ref.print_trait_sugared().to_string();
221+
let this_path =
222+
ty::TraitRef::identity(self.tcx, def_id).print_only_trait_path().to_string();
221223

222224
let filter_options =
223225
FilterOptions { self_types, from_desugaring, cause, crate_local, direct, generic_args };
@@ -249,7 +251,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
249251
})
250252
.collect();
251253

252-
let format_args = FormatArgs { this, this_sugared, generic_args, item_context };
254+
let format_args = FormatArgs { this, this_path, this_resolved, generic_args, item_context };
253255
(filter_options, format_args)
254256
}
255257
}

library/core/src/ops/function.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ use crate::marker::Tuple;
6767
// SAFETY: tidy is not smart enough to tell that the below unsafe block is a string
6868
label = "call the function in a closure: `|| unsafe {{ /* code */ }}`"
6969
),
70-
message = "expected a `{Trait}` closure, found `{Self}`",
71-
label = "expected an `{Trait}` closure, found `{Self}`"
70+
message = "expected a `{This:resolved}` closure, found `{Self}`",
71+
label = "expected an `{This:resolved}` closure, found `{Self}`"
7272
)]
7373
#[fundamental] // so that regex can rely that `&str: !FnMut`
7474
#[must_use = "closures are lazy and do nothing unless called"]
@@ -154,8 +154,8 @@ pub const trait Fn<Args: Tuple>: [const] FnMut<Args> {
154154
// SAFETY: tidy is not smart enough to tell that the below unsafe block is a string
155155
label = "call the function in a closure: `|| unsafe {{ /* code */ }}`"
156156
),
157-
message = "expected a `{Trait}` closure, found `{Self}`",
158-
label = "expected an `{Trait}` closure, found `{Self}`"
157+
message = "expected a `{This:resolved}` closure, found `{Self}`",
158+
label = "expected an `{This:resolved}` closure, found `{Self}`"
159159
)]
160160
#[fundamental] // so that regex can rely that `&str: !FnMut`
161161
#[must_use = "closures are lazy and do nothing unless called"]
@@ -233,8 +233,8 @@ pub const trait FnMut<Args: Tuple>: FnOnce<Args> {
233233
// SAFETY: tidy is not smart enough to tell that the below unsafe block is a string
234234
label = "call the function in a closure: `|| unsafe {{ /* code */ }}`"
235235
),
236-
message = "expected a `{Trait}` closure, found `{Self}`",
237-
label = "expected an `{Trait}` closure, found `{Self}`"
236+
message = "expected a `{This:resolved}` closure, found `{Self}`",
237+
label = "expected an `{This:resolved}` closure, found `{Self}`"
238238
)]
239239
#[fundamental] // so that regex can rely that `&str: !FnMut`
240240
#[must_use = "closures are lazy and do nothing unless called"]

src/doc/rustc-dev-guide/src/diagnostics.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,12 +1018,13 @@ pub trait From<T>: Sized {
10181018
### Formatting
10191019

10201020
The string literals are format strings that accept parameters wrapped in braces
1021-
but positional and listed parameters and format specifiers are not accepted.
1021+
but positional and listed parameters are not accepted.
10221022
The following parameter names are valid:
10231023
- `Self` and all generic parameters of the trait.
10241024
- `This`: the name of the trait the attribute is on, without generics.
1025-
- `Trait`: the name of the "sugared" trait.
1026-
See `TraitRefPrintSugared`.
1025+
- `This:path`: the full path of the trait the attribute is on, with unresolved generics.
1026+
- `This:resolved`: the full path of the trait the attribute is on, with resolved generics.
1027+
Additionally, this will "sugar" the `Fn(...)` traits.
10271028
- `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`,
10281029
`"a function"`, `"an async function"`, etc.
10291030

tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")]
1616
|
1717
= help: you can print empty braces by escaping them
1818

19-
warning: format specifiers are not permitted in diagnostic attributes
20-
--> $DIR/broken_format.rs:10:50
21-
|
22-
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
23-
| ^ remove this format specifier
24-
2519
warning: indexed format arguments are not permitted in diagnostic attributes
2620
--> $DIR/broken_format.rs:10:49
2721
|
2822
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
2923
| ^ remove this format argument
3024

25+
warning: format specifiers are not permitted in diagnostic attributes
26+
--> $DIR/broken_format.rs:10:50
27+
|
28+
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
29+
| ^ remove this format specifier
30+
3131
warning: format specifiers are not permitted in diagnostic attributes
3232
--> $DIR/broken_format.rs:15:53
3333
|

tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ impl Bar for i32 {}
3535
//~|WARN there is no parameter `r#struct` on trait `Baz`
3636
//~|WARN there is no parameter `r#enum` on trait `Baz`
3737
//~|WARN there is no parameter `union` on trait `Baz`
38-
label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
38+
label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}"
3939
//~^WARN there is no parameter `float` on trait `Baz`
4040
//~|WARN there is no parameter `_Self` on trait `Baz`
4141
//~|WARN there is no parameter `crate_local` on trait `Baz`
42-
//~|WARN there is no parameter `Trait` on trait `Baz`
4342
//~|WARN there is no parameter `ItemContext` on trait `Baz`
4443
//~|WARN there is no parameter `This` on trait `Baz`
44+
//~|WARN there is no parameter `This` on trait `Baz`
45+
//~|WARN there is no parameter `This` on trait `Baz`
46+
//~|WARN format specifiers are not permitted in diagnostic attributes
47+
//~|WARN format specifiers are not permitted in diagnostic attributes
4548
)]
4649
trait Baz {}
4750

0 commit comments

Comments
 (0)