Skip to content

Commit f3f19dd

Browse files
committed
Permit {This} in diagnostic attribute format literals
1 parent 913e4be commit f3f19dd

23 files changed

Lines changed: 367 additions & 182 deletions

File tree

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

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::ops::Range;
22

3-
use rustc_errors::E0232;
3+
use rustc_errors::{Diagnostic, E0232};
44
use rustc_hir::AttrPath;
55
use rustc_hir::attrs::diagnostic::{
66
Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue,
77
OnUnimplementedCondition, Piece, Predicate,
88
};
9-
use rustc_hir::lints::{AttributeLintKind, FormatWarning};
9+
use rustc_hir::lints::AttributeLintKind;
1010
use rustc_macros::Diagnostic;
1111
use rustc_parse_format::{
1212
Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
@@ -18,6 +18,7 @@ use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym};
1818
use thin_vec::{ThinVec, thin_vec};
1919

2020
use crate::context::{AcceptContext, Stage};
21+
use crate::errors::FormatWarning;
2122
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser};
2223

2324
pub(crate) mod do_not_recommend;
@@ -83,6 +84,26 @@ impl Mode {
8384
Self::DiagnosticOnUnmatchArgs => DEFAULT,
8485
}
8586
}
87+
88+
fn allowed_format_arguments(&self) -> &'static str {
89+
match self {
90+
Self::RustcOnUnimplemented => {
91+
"see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented> for allowed format arguments"
92+
}
93+
Self::DiagnosticOnUnimplemented => {
94+
"only `Self` and generics of the trait are allowed as a format argument"
95+
}
96+
Self::DiagnosticOnConst => {
97+
"only `Self` and generics of the implementation are allowed as a format argument"
98+
}
99+
Self::DiagnosticOnMove => {
100+
"only `This`, `Self` and generics of the type are allowed as a format argument"
101+
}
102+
Self::DiagnosticOnUnknown | Self::DiagnosticOnUnmatchArgs => {
103+
"only `This` is allowed as a format argument"
104+
}
105+
}
106+
}
86107
}
87108

88109
fn merge_directives<S: Stage>(
@@ -242,12 +263,13 @@ fn parse_directive_items<'p, S: Stage>(
242263
match parse_format_string(input.name, snippet, input.span, mode) {
243264
Ok((f, warnings)) => {
244265
for warning in warnings {
245-
let (FormatWarning::InvalidSpecifier { span, .. }
246-
| FormatWarning::PositionalArgument { span, .. }
247-
| FormatWarning::DisallowedPlaceholder { span }) = warning;
248-
cx.emit_lint(
266+
let (FormatWarning::InvalidSpecifier { span }
267+
| FormatWarning::PositionalArgument { span }
268+
| FormatWarning::IndexedArgument { span }
269+
| FormatWarning::DisallowedPlaceholder { span, .. }) = warning;
270+
cx.emit_dyn_lint(
249271
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
250-
AttributeLintKind::MalformedDiagnosticFormat { warning },
272+
move |dcx, level| warning.into_diag(dcx, level),
251273
span,
252274
);
253275
}
@@ -394,38 +416,74 @@ fn parse_arg(
394416
is_source_literal: bool,
395417
) -> FormatArg {
396418
let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
397-
if matches!(mode, Mode::DiagnosticOnUnknown) {
398-
warnings.push(FormatWarning::DisallowedPlaceholder { span });
399-
return FormatArg::AsIs(sym::empty_braces);
400-
}
401419

402420
match arg.position {
403421
// Something like "hello {name}"
404422
Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
405-
// Only `#[rustc_on_unimplemented]` can use these
406-
(Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
407-
(Mode::RustcOnUnimplemented { .. } | Mode::DiagnosticOnUnmatchArgs, sym::This) => {
408-
FormatArg::This
423+
(Mode::RustcOnUnimplemented, sym::ItemContext) => FormatArg::ItemContext,
424+
425+
// Like `{This}`, but sugared.
426+
// FIXME(mejrs) maybe rename/rework this or something
427+
// if we want to apply this to other attrs?
428+
(Mode::RustcOnUnimplemented, sym::Trait) => FormatArg::Trait,
429+
430+
// Some diagnostic attributes can use `{This}` to refer to the annotated item.
431+
// For those that don't, we continue and maybe use it as a generic parameter.
432+
//
433+
// FIXME(mejrs) `DiagnosticOnUnimplemented` is intentionally not here;
434+
// that requires lang approval which is best kept for a standalone PR.
435+
(
436+
Mode::RustcOnUnimplemented
437+
| Mode::DiagnosticOnUnknown
438+
| Mode::DiagnosticOnMove
439+
| Mode::DiagnosticOnUnmatchArgs,
440+
sym::This,
441+
) => FormatArg::This,
442+
443+
// `{Self}`; the self type.
444+
// - For trait declaration attributes that's the type that does not implement it.
445+
// - for trait impl attributes, the implemented for type.
446+
// - For ADT attributes, that's the type (which will be identical to `{This}`)
447+
// - For everything else it doesn't make sense.
448+
(
449+
Mode::RustcOnUnimplemented
450+
| Mode::DiagnosticOnUnimplemented
451+
| Mode::DiagnosticOnMove
452+
| Mode::DiagnosticOnConst,
453+
kw::SelfUpper,
454+
) => FormatArg::SelfUpper,
455+
456+
// Generic parameters.
457+
// FIXME(mejrs) unfortunately, all the "special" symbols above might fall through,
458+
// but at this time we are not aware of what generic parameters the trait actually has.
459+
// If we find `ItemContext` or something we have to assume that's a generic parameter.
460+
// We lint against that in `check_attr.rs` though.
461+
(
462+
Mode::RustcOnUnimplemented
463+
| Mode::DiagnosticOnUnimplemented
464+
| Mode::DiagnosticOnMove
465+
| Mode::DiagnosticOnConst,
466+
generic_param,
467+
) => FormatArg::GenericParam { generic_param, span },
468+
469+
// Generics are explicitly not allowed, we print those back as is.
470+
(Mode::DiagnosticOnUnknown | Mode::DiagnosticOnUnmatchArgs, as_is) => {
471+
warnings.push(FormatWarning::DisallowedPlaceholder {
472+
span,
473+
attr: mode.as_str(),
474+
allowed: mode.allowed_format_arguments(),
475+
});
476+
return FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}")));
409477
}
410-
(Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
411-
// Any attribute can use these
412-
(_, kw::SelfUpper) => FormatArg::SelfUpper,
413-
(_, generic_param) => FormatArg::GenericParam { generic_param, span },
414478
},
415479

416480
// `{:1}` and `{}` are ignored
417481
Position::ArgumentIs(idx) => {
418-
warnings.push(FormatWarning::PositionalArgument {
419-
span,
420-
help: format!("use `{{{idx}}}` to print a number in braces"),
421-
});
482+
warnings.push(FormatWarning::IndexedArgument { span });
422483
FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}")))
423484
}
424485
Position::ArgumentImplicitlyIs(_) => {
425-
warnings.push(FormatWarning::PositionalArgument {
426-
span,
427-
help: String::from("use `{{}}` to print empty braces"),
428-
});
486+
warnings.push(FormatWarning::PositionalArgument { span });
429487
FormatArg::AsIs(sym::empty_braces)
430488
}
431489
}
@@ -445,7 +503,7 @@ fn warn_on_format_spec(
445503
.as_ref()
446504
.map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
447505
.unwrap_or(input_span);
448-
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
506+
warnings.push(FormatWarning::InvalidSpecifier { span })
449507
}
450508
}
451509

compiler/rustc_attr_parsing/src/errors.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,34 @@ pub(crate) struct IncorrectDoNotRecommendLocation {
325325
#[label("not a trait implementation")]
326326
pub target_span: Span,
327327
}
328+
329+
#[derive(Diagnostic, Clone, Copy)]
330+
pub(crate) enum FormatWarning {
331+
#[diag("positional arguments are not permitted in diagnostic attributes")]
332+
#[help("you can print empty braces by escaping them")]
333+
PositionalArgument {
334+
#[label("remove this format argument")]
335+
span: Span,
336+
},
337+
338+
#[diag("indexed format arguments are not permitted in diagnostic attributes")]
339+
IndexedArgument {
340+
#[label("remove this format argument")]
341+
span: Span,
342+
},
343+
344+
#[diag("format specifiers are not permitted in diagnostic attributes")]
345+
InvalidSpecifier {
346+
#[label("remove this format specifier")]
347+
span: Span,
348+
},
349+
350+
#[diag("this format argument is not allowed in `#[{$attr}]`")]
351+
#[note("{$allowed}")]
352+
DisallowedPlaceholder {
353+
#[label("remove this format argument")]
354+
span: Span,
355+
attr: &'static str,
356+
allowed: &'static str,
357+
},
358+
}

compiler/rustc_hir/src/lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use rustc_data_structures::sync::{DynSend, DynSync};
22
use rustc_error_messages::MultiSpan;
33
use rustc_errors::{Diag, DiagCtxtHandle, Level};
4+
pub use rustc_lint_defs::AttributeLintKind;
45
use rustc_lint_defs::LintId;
5-
pub use rustc_lint_defs::{AttributeLintKind, FormatWarning};
66

77
use crate::HirId;
88

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::any::Any;
22

33
use rustc_data_structures::sync::DynSend;
44
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
5-
use rustc_hir::lints::{AttributeLintKind, FormatWarning};
5+
use rustc_hir::lints::AttributeLintKind;
66
use rustc_middle::ty::TyCtxt;
77
use rustc_session::Session;
88

@@ -52,17 +52,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
5252
lints::MalFormedDiagnosticAttributeLint { attribute, options, span }
5353
.into_diag(dcx, level)
5454
}
55-
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
56-
FormatWarning::PositionalArgument { .. } => {
57-
lints::DisallowedPositionalArgument.into_diag(dcx, level)
58-
}
59-
FormatWarning::InvalidSpecifier { .. } => {
60-
lints::InvalidFormatSpecifier.into_diag(dcx, level)
61-
}
62-
FormatWarning::DisallowedPlaceholder { .. } => {
63-
lints::DisallowedPlaceholder.into_diag(dcx, level)
64-
}
65-
},
6655
AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => {
6756
lints::WrappedParserError { description, label, span: *span }.into_diag(dcx, level)
6857
}

compiler/rustc_lint/src/lints.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,23 +3303,6 @@ pub(crate) struct ExpectedNoArgs;
33033303
)]
33043304
pub(crate) struct ExpectedNameValue;
33053305

3306-
#[derive(Diagnostic)]
3307-
#[diag("positional format arguments are not allowed here")]
3308-
#[help(
3309-
"only named format arguments with the name of one of the generic types are allowed in this context"
3310-
)]
3311-
pub(crate) struct DisallowedPositionalArgument;
3312-
3313-
#[derive(Diagnostic)]
3314-
#[diag("format arguments are not allowed here")]
3315-
#[help("consider removing this format argument")]
3316-
pub(crate) struct DisallowedPlaceholder;
3317-
3318-
#[derive(Diagnostic)]
3319-
#[diag("invalid format specifier")]
3320-
#[help("no format specifier are supported in this position")]
3321-
pub(crate) struct InvalidFormatSpecifier;
3322-
33233306
#[derive(Diagnostic)]
33243307
#[diag("{$description}")]
33253308
pub(crate) struct WrappedParserError<'a> {

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -660,20 +660,12 @@ pub enum AttributeLintKind {
660660
ExpectedNoArgs,
661661
ExpectedNameValue,
662662
MalFormedDiagnosticAttribute { attribute: &'static str, options: &'static str, span: Span },
663-
MalformedDiagnosticFormat { warning: FormatWarning },
664663
DiagnosticWrappedParserError { description: String, label: String, span: Span },
665664
IgnoredDiagnosticOption { option_name: Symbol, first_span: Span, later_span: Span },
666665
MissingOptionsForDiagnosticAttribute { attribute: &'static str, options: &'static str },
667666
NonMetaItemDiagnosticAttribute,
668667
}
669668

670-
#[derive(Debug, Clone, HashStable_Generic)]
671-
pub enum FormatWarning {
672-
PositionalArgument { span: Span, help: String },
673-
InvalidSpecifier { name: String, span: Span },
674-
DisallowedPlaceholder { span: Span },
675-
}
676-
677669
pub type RegisteredTools = FxIndexSet<Ident>;
678670

679671
/// Declares a static item of type `&'static Lint`.

compiler/rustc_resolve/src/imports.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::mem;
44

5+
use itertools::Itertools;
56
use rustc_ast::{Item, NodeId};
67
use rustc_attr_parsing::AttributeParser;
78
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
@@ -879,8 +880,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
879880
let (message, label, notes) =
880881
// Feature gating for `on_unknown_attr` happens initialization of the field
881882
if let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) {
883+
let this = errors.iter().map(|(_import, err)| {
884+
err.segment.unwrap_or(kw::Underscore)
885+
}).join(", ");
886+
882887
let args = FormatArgs {
883-
this: paths.join(", "),
888+
this,
884889
// Unused
885890
this_sugared: String::new(),
886891
// Unused

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use rustc_hir as hir;
44
use rustc_hir::attrs::diagnostic::{ConditionOptions, CustomDiagnostic, FormatArgs};
55
use rustc_hir::def_id::LocalDefId;
66
use rustc_hir::find_attr;
7-
pub use rustc_hir::lints::FormatWarning;
87
use rustc_middle::ty::print::PrintTraitRefExt;
98
use rustc_middle::ty::{self, GenericParamDef, GenericParamDefKind};
109
use rustc_span::Symbol;

tests/ui/diagnostic_namespace/multiline_spans.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ pub trait MultiLine4 {}
3131
#[diagnostic::on_unimplemented(message = "here is a big \
3232
multiline string \
3333
{Self:+}")]
34-
//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
34+
//~^ ERROR format specifiers are not permitted in diagnostic attributes [malformed_diagnostic_format_literals]
3535
pub trait MultiLineFmt {}
3636

3737
#[diagnostic::on_unimplemented(message = "here is a big \
3838
multiline string {Self:X}")]
39-
//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
39+
//~^ ERROR format specifiers are not permitted in diagnostic attributes [malformed_diagnostic_format_literals]
4040
pub trait MultiLineFmt2 {}
4141

4242
#[diagnostic::on_unimplemented(message = "here is a big \
4343
multiline string {Self:#}")]
44-
//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
44+
//~^ ERROR format specifiers are not permitted in diagnostic attributes [malformed_diagnostic_format_literals]
4545
pub trait MultiLineFmt3 {}
4646

4747

@@ -51,5 +51,5 @@ pub trait MultiLineFmt3 {}
5151
\
5252
\
5353
multiline string {Self:?}")]
54-
//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
54+
//~^ ERROR format specifiers are not permitted in diagnostic attributes [malformed_diagnostic_format_literals]
5555
pub trait MultiLineFmt4 {}

tests/ui/diagnostic_namespace/multiline_spans.stderr

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,37 +36,29 @@ LL | multiline string {unknown}")]
3636
|
3737
= help: expect either a generic argument name or `{Self}` as format argument
3838

39-
error: invalid format specifier
39+
error: format specifiers are not permitted in diagnostic attributes
4040
--> $DIR/multiline_spans.rs:33:47
4141
|
4242
LL | ... {Self:+}")]
43-
| ^^
44-
|
45-
= help: no format specifier are supported in this position
43+
| ^^ remove this format specifier
4644

47-
error: invalid format specifier
45+
error: format specifiers are not permitted in diagnostic attributes
4846
--> $DIR/multiline_spans.rs:38:64
4947
|
5048
LL | ... multiline string {Self:X}")]
51-
| ^^
52-
|
53-
= help: no format specifier are supported in this position
49+
| ^^ remove this format specifier
5450

55-
error: invalid format specifier
51+
error: format specifiers are not permitted in diagnostic attributes
5652
--> $DIR/multiline_spans.rs:43:27
5753
|
5854
LL | multiline string {Self:#}")]
59-
| ^^
60-
|
61-
= help: no format specifier are supported in this position
55+
| ^^ remove this format specifier
6256

63-
error: invalid format specifier
57+
error: format specifiers are not permitted in diagnostic attributes
6458
--> $DIR/multiline_spans.rs:53:27
6559
|
6660
LL | multiline string {Self:?}")]
67-
| ^^
68-
|
69-
= help: no format specifier are supported in this position
61+
| ^^ remove this format specifier
7062

7163
error: aborting due to 8 previous errors
7264

0 commit comments

Comments
 (0)