Skip to content

Commit 7738ecd

Browse files
committed
Introduce ParseMode::diagnostic and fix multiline spans
1 parent 5747cc8 commit 7738ecd

File tree

8 files changed

+170
-80
lines changed

8 files changed

+170
-80
lines changed

compiler/rustc_parse_format/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ pub enum ParseMode {
2929
Format,
3030
/// An inline assembly template string for `asm!`.
3131
InlineAsm,
32+
/// A format string for use in diagnostic attributes.
33+
///
34+
/// Similar to `format_args!`, however only named ("captured") arguments
35+
/// are allowed, and no format modifiers are permitted.
36+
Diagnostic,
3237
}
3338

3439
/// A piece is a portion of the format string which represents the next part
@@ -506,6 +511,7 @@ impl<'input> Parser<'input> {
506511
let format = match self.mode {
507512
ParseMode::Format => self.format(),
508513
ParseMode::InlineAsm => self.inline_asm(),
514+
ParseMode::Diagnostic => self.diagnostic(),
509515
};
510516

511517
// Resolve position after parsing format spec.
@@ -715,6 +721,22 @@ impl<'input> Parser<'input> {
715721
spec
716722
}
717723

724+
/// Always returns an empty `FormatSpec`
725+
fn diagnostic(&mut self) -> FormatSpec<'input> {
726+
let mut spec = FormatSpec::default();
727+
728+
let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else {
729+
return spec;
730+
};
731+
732+
spec.ty = self.string(start_idx);
733+
spec.ty_span = {
734+
let end = self.input_vec_index2range(self.input_vec_index).start;
735+
Some(start..end)
736+
};
737+
spec
738+
}
739+
718740
/// Parses a `Count` parameter at the current position. This does not check
719741
/// for 'CountIsNextParam' because that is only used in precision, not
720742
/// width.

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
@@ -810,7 +810,8 @@ impl<'tcx> OnUnimplementedFormatString {
810810

811811
let mut result = Ok(());
812812

813-
match FormatString::parse(self.symbol, self.span, &ctx) {
813+
let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok();
814+
match FormatString::parse(self.symbol, snippet, self.span, &ctx) {
814815
// Warnings about format specifiers, deprecated parameters, wrong parameters etc.
815816
// In other words we'd like to let the author know, but we can still try to format the string later
816817
Ok(FormatString { warnings, .. }) => {
@@ -889,7 +890,8 @@ impl<'tcx> OnUnimplementedFormatString {
889890
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
890891
};
891892

892-
if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
893+
// No point passing a snippet here, we already did that in `verify`
894+
if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) {
893895
s.format(args)
894896
} else {
895897
// we cannot return errors from processing the format string as hard error here

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ enum LitOrArg {
198198

199199
impl FilterFormatString {
200200
fn parse(input: Symbol) -> Self {
201-
let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format)
201+
let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic)
202202
.map(|p| match p {
203203
Piece::Lit(s) => LitOrArg::Lit(s.to_owned()),
204204
// We just ignore formatspecs here

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

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use errors::*;
55
use rustc_middle::ty::print::TraitRefPrintSugared;
66
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
77
use rustc_parse_format::{
8-
Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece,
9-
Position,
8+
Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
109
};
1110
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
1211
use rustc_span::def_id::DefId;
@@ -158,9 +157,14 @@ impl FormatString {
158157
self.span
159158
}
160159

161-
pub fn parse<'tcx>(input: Symbol, span: Span, ctx: &Ctx<'tcx>) -> Result<Self, ParseError> {
160+
pub fn parse<'tcx>(
161+
input: Symbol,
162+
snippet: Option<String>,
163+
span: Span,
164+
ctx: &Ctx<'tcx>,
165+
) -> Result<Self, ParseError> {
162166
let s = input.as_str();
163-
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
167+
let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
164168
let pieces: Vec<_> = parser.by_ref().collect();
165169

166170
if let Some(err) = parser.errors.into_iter().next() {
@@ -279,24 +283,7 @@ fn parse_arg<'tcx>(
279283
/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
280284
/// with specifiers, so emit a warning if they are used.
281285
fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) {
282-
if !matches!(
283-
spec,
284-
FormatSpec {
285-
fill: None,
286-
fill_span: None,
287-
align: Alignment::AlignUnknown,
288-
sign: None,
289-
alternate: false,
290-
zero_pad: false,
291-
debug_hex: None,
292-
precision: Count::CountImplied,
293-
precision_span: None,
294-
width: Count::CountImplied,
295-
width_span: None,
296-
ty: _,
297-
ty_span: _,
298-
},
299-
) {
286+
if spec.ty != "" {
300287
let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span);
301288
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
302289
}

tests/ui/diagnostic_namespace/multiline_spans.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#[diagnostic::on_unimplemented(message = "here is a big \
66
multiline string \
77
{unknown}")]
8-
//~^^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes]
8+
//~^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes]
99
pub trait MultiLine {}
1010

1111
#[diagnostic::on_unimplemented(message = "here is a big \
@@ -25,22 +25,23 @@ pub trait MultiLine3 {}
2525
\
2626
\
2727
multiline string {unknown}")]
28-
//~^^^^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes]
28+
//~^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes]
2929
pub trait MultiLine4 {}
3030

3131
#[diagnostic::on_unimplemented(message = "here is a big \
3232
multiline string \
3333
{Self:+}")]
34-
//~^^^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
34+
//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
3535
pub trait MultiLineFmt {}
3636

3737
#[diagnostic::on_unimplemented(message = "here is a big \
3838
multiline string {Self:X}")]
39+
//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
3940
pub trait MultiLineFmt2 {}
4041

4142
#[diagnostic::on_unimplemented(message = "here is a big \
4243
multiline string {Self:#}")]
43-
//~^^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
44+
//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
4445
pub trait MultiLineFmt3 {}
4546

4647

@@ -50,4 +51,5 @@ pub trait MultiLineFmt3 {}
5051
\
5152
\
5253
multiline string {Self:?}")]
54+
//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
5355
pub trait MultiLineFmt4 {}
Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: there is no parameter `unknown` on trait `MultiLine`
2-
--> $DIR/multiline_spans.rs:6:17
2+
--> $DIR/multiline_spans.rs:7:43
33
|
4-
LL | ... multiline string \
5-
| ^^^^^^^
4+
LL | ... {unknown}")]
5+
| ^^^^^^^
66
|
77
= help: expect either a generic argument name or `{Self}` as format argument
88
note: the lint level is defined here
@@ -12,50 +12,60 @@ LL | #![deny(unknown_or_malformed_diagnostic_attributes)]
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1313

1414
error: there is no parameter `unknown` on trait `MultiLine2`
15-
--> $DIR/multiline_spans.rs:12:17
15+
--> $DIR/multiline_spans.rs:12:60
1616
|
1717
LL | ... multiline string {unknown}")]
18-
| ^^^^^^^
18+
| ^^^^^^^
1919
|
2020
= help: expect either a generic argument name or `{Self}` as format argument
2121

2222
error: there is no parameter `unknown` on trait `MultiLine3`
23-
--> $DIR/multiline_spans.rs:17:17
23+
--> $DIR/multiline_spans.rs:17:23
2424
|
2525
LL | multiline string {unknown}")]
26-
| ^^^^^^^
26+
| ^^^^^^^
2727
|
2828
= help: expect either a generic argument name or `{Self}` as format argument
2929

3030
error: there is no parameter `unknown` on trait `MultiLine4`
31-
--> $DIR/multiline_spans.rs:24:15
31+
--> $DIR/multiline_spans.rs:27:23
3232
|
33-
LL | / \
34-
LL | | \
35-
| |___^
33+
LL | multiline string {unknown}")]
34+
| ^^^^^^^
3635
|
3736
= help: expect either a generic argument name or `{Self}` as format argument
3837

3938
error: invalid format specifier
40-
--> $DIR/multiline_spans.rs:31:42
39+
--> $DIR/multiline_spans.rs:33:47
40+
|
41+
LL | ... {Self:+}")]
42+
| ^^
43+
|
44+
= help: no format specifier are supported in this position
45+
46+
error: invalid format specifier
47+
--> $DIR/multiline_spans.rs:38:64
48+
|
49+
LL | ... multiline string {Self:X}")]
50+
| ^^
51+
|
52+
= help: no format specifier are supported in this position
53+
54+
error: invalid format specifier
55+
--> $DIR/multiline_spans.rs:43:27
4156
|
42-
LL | #[diagnostic::on_unimplemented(message = "here is a big \
43-
| __________________________________________^
44-
LL | | multiline string \
45-
LL | | {Self:+}")]
46-
| |__________________________________________________^
57+
LL | multiline string {Self:#}")]
58+
| ^^
4759
|
4860
= help: no format specifier are supported in this position
4961

5062
error: invalid format specifier
51-
--> $DIR/multiline_spans.rs:41:42
63+
--> $DIR/multiline_spans.rs:53:27
5264
|
53-
LL | #[diagnostic::on_unimplemented(message = "here is a big \
54-
| __________________________________________^
55-
LL | | multiline string {Self:#}")]
56-
| |______________________________^
65+
LL | multiline string {Self:?}")]
66+
| ^^
5767
|
5868
= help: no format specifier are supported in this position
5969

60-
error: aborting due to 6 previous errors
70+
error: aborting due to 8 previous errors
6171

tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ trait ImportantTrait2 {}
1212
#[diagnostic::on_unimplemented(message = "Test {1:}")]
1313
//~^WARN positional format arguments are not allowed here
1414
//~|WARN positional format arguments are not allowed here
15+
//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
16+
//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
1517
trait ImportantTrait3 {}
1618

1719
#[diagnostic::on_unimplemented(message = "Test {Self:123}")]
@@ -20,15 +22,22 @@ trait ImportantTrait3 {}
2022
trait ImportantTrait4 {}
2123

2224
#[diagnostic::on_unimplemented(message = "Test {Self:!}")]
23-
//~^WARN expected `}`, found `!`
24-
//~|WARN expected `}`, found `!`
25+
//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
26+
//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
2527
trait ImportantTrait5 {}
2628

29+
#[diagnostic::on_unimplemented(message = "Test {Self:}")]
30+
//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
31+
//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
32+
trait ImportantTrait6 {}
33+
34+
2735
fn check_1(_: impl ImportantTrait1) {}
2836
fn check_2(_: impl ImportantTrait2) {}
2937
fn check_3(_: impl ImportantTrait3) {}
3038
fn check_4(_: impl ImportantTrait4) {}
3139
fn check_5(_: impl ImportantTrait5) {}
40+
fn check_6(_: impl ImportantTrait6) {}
3241

3342
fn main() {
3443
check_1(());
@@ -40,5 +49,7 @@ fn main() {
4049
check_4(());
4150
//~^ERROR Test ()
4251
check_5(());
43-
//~^ERROR Test {Self:!}
52+
//~^ERROR Test ()
53+
check_6(());
54+
//~^ERROR Test ()
4455
}

0 commit comments

Comments
 (0)