Skip to content

Commit 444ac1a

Browse files
authored
Rollup merge of #113774 - compiler-errors:fill-expr-bracket, r=eholk
Improve error message when closing bracket interpreted as formatting fill character Fixes #112732 by explaining why it's erroring in the way it is.
2 parents a47b7b0 + e021191 commit 444ac1a

8 files changed

+97
-75
lines changed

compiler/rustc_parse_format/src/lib.rs

+45-58
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ pub struct Argument<'a> {
109109
pub struct FormatSpec<'a> {
110110
/// Optionally specified character to fill alignment with.
111111
pub fill: Option<char>,
112+
/// Span of the optionally specified fill character.
113+
pub fill_span: Option<InnerSpan>,
112114
/// Optionally specified alignment.
113115
pub align: Alignment,
114116
/// The `+` or `-` flag.
@@ -264,7 +266,7 @@ impl<'a> Iterator for Parser<'a> {
264266
Some(String(self.string(pos + 1)))
265267
} else {
266268
let arg = self.argument(lbrace_end);
267-
if let Some(rbrace_pos) = self.must_consume('}') {
269+
if let Some(rbrace_pos) = self.consume_closing_brace(&arg) {
268270
if self.is_source_literal {
269271
let lbrace_byte_pos = self.to_span_index(pos);
270272
let rbrace_byte_pos = self.to_span_index(rbrace_pos);
@@ -450,69 +452,51 @@ impl<'a> Parser<'a> {
450452

451453
/// Forces consumption of the specified character. If the character is not
452454
/// found, an error is emitted.
453-
fn must_consume(&mut self, c: char) -> Option<usize> {
455+
fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option<usize> {
454456
self.ws();
455457

456-
if let Some(&(pos, maybe)) = self.cur.peek() {
457-
if c == maybe {
458+
let pos;
459+
let description;
460+
461+
if let Some(&(peek_pos, maybe)) = self.cur.peek() {
462+
if maybe == '}' {
458463
self.cur.next();
459-
Some(pos)
460-
} else {
461-
let pos = self.to_span_index(pos);
462-
let description = format!("expected `'}}'`, found `{maybe:?}`");
463-
let label = "expected `}`".to_owned();
464-
let (note, secondary_label) = if c == '}' {
465-
(
466-
Some(
467-
"if you intended to print `{`, you can escape it using `{{`".to_owned(),
468-
),
469-
self.last_opening_brace
470-
.map(|sp| ("because of this opening brace".to_owned(), sp)),
471-
)
472-
} else {
473-
(None, None)
474-
};
475-
self.errors.push(ParseError {
476-
description,
477-
note,
478-
label,
479-
span: pos.to(pos),
480-
secondary_label,
481-
should_be_replaced_with_positional_argument: false,
482-
});
483-
None
464+
return Some(peek_pos);
484465
}
466+
467+
pos = peek_pos;
468+
description = format!("expected `'}}'`, found `{maybe:?}`");
485469
} else {
486-
let description = format!("expected `{c:?}` but string was terminated");
470+
description = "expected `'}'` but string was terminated".to_owned();
487471
// point at closing `"`
488-
let pos = self.input.len() - if self.append_newline { 1 } else { 0 };
489-
let pos = self.to_span_index(pos);
490-
if c == '}' {
491-
let label = format!("expected `{c:?}`");
492-
let (note, secondary_label) = if c == '}' {
493-
(
494-
Some(
495-
"if you intended to print `{`, you can escape it using `{{`".to_owned(),
496-
),
497-
self.last_opening_brace
498-
.map(|sp| ("because of this opening brace".to_owned(), sp)),
499-
)
500-
} else {
501-
(None, None)
502-
};
503-
self.errors.push(ParseError {
504-
description,
505-
note,
506-
label,
507-
span: pos.to(pos),
508-
secondary_label,
509-
should_be_replaced_with_positional_argument: false,
510-
});
511-
} else {
512-
self.err(description, format!("expected `{c:?}`"), pos.to(pos));
513-
}
514-
None
472+
pos = self.input.len() - if self.append_newline { 1 } else { 0 };
515473
}
474+
475+
let pos = self.to_span_index(pos);
476+
477+
let label = "expected `'}'`".to_owned();
478+
let (note, secondary_label) = if arg.format.fill == Some('}') {
479+
(
480+
Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()),
481+
arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)),
482+
)
483+
} else {
484+
(
485+
Some("if you intended to print `{`, you can escape it using `{{`".to_owned()),
486+
self.last_opening_brace.map(|sp| ("because of this opening brace".to_owned(), sp)),
487+
)
488+
};
489+
490+
self.errors.push(ParseError {
491+
description,
492+
note,
493+
label,
494+
span: pos.to(pos),
495+
secondary_label,
496+
should_be_replaced_with_positional_argument: false,
497+
});
498+
499+
None
516500
}
517501

518502
/// Consumes all whitespace characters until the first non-whitespace character
@@ -608,6 +592,7 @@ impl<'a> Parser<'a> {
608592
fn format(&mut self) -> FormatSpec<'a> {
609593
let mut spec = FormatSpec {
610594
fill: None,
595+
fill_span: None,
611596
align: AlignUnknown,
612597
sign: None,
613598
alternate: false,
@@ -625,9 +610,10 @@ impl<'a> Parser<'a> {
625610
}
626611

627612
// fill character
628-
if let Some(&(_, c)) = self.cur.peek() {
613+
if let Some(&(idx, c)) = self.cur.peek() {
629614
if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) {
630615
spec.fill = Some(c);
616+
spec.fill_span = Some(self.span(idx, idx + 1));
631617
self.cur.next();
632618
}
633619
}
@@ -722,6 +708,7 @@ impl<'a> Parser<'a> {
722708
fn inline_asm(&mut self) -> FormatSpec<'a> {
723709
let mut spec = FormatSpec {
724710
fill: None,
711+
fill_span: None,
725712
align: AlignUnknown,
726713
sign: None,
727714
alternate: false,

compiler/rustc_parse_format/src/tests.rs

+15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ fn same(fmt: &'static str, p: &[Piece<'static>]) {
99
fn fmtdflt() -> FormatSpec<'static> {
1010
return FormatSpec {
1111
fill: None,
12+
fill_span: None,
1213
align: AlignUnknown,
1314
sign: None,
1415
alternate: false,
@@ -128,6 +129,7 @@ fn format_type() {
128129
position_span: InnerSpan { start: 2, end: 3 },
129130
format: FormatSpec {
130131
fill: None,
132+
fill_span: None,
131133
align: AlignUnknown,
132134
sign: None,
133135
alternate: false,
@@ -152,6 +154,7 @@ fn format_align_fill() {
152154
position_span: InnerSpan { start: 2, end: 3 },
153155
format: FormatSpec {
154156
fill: None,
157+
fill_span: None,
155158
align: AlignRight,
156159
sign: None,
157160
alternate: false,
@@ -173,6 +176,7 @@ fn format_align_fill() {
173176
position_span: InnerSpan { start: 2, end: 3 },
174177
format: FormatSpec {
175178
fill: Some('0'),
179+
fill_span: Some(InnerSpan::new(4, 5)),
176180
align: AlignLeft,
177181
sign: None,
178182
alternate: false,
@@ -194,6 +198,7 @@ fn format_align_fill() {
194198
position_span: InnerSpan { start: 2, end: 3 },
195199
format: FormatSpec {
196200
fill: Some('*'),
201+
fill_span: Some(InnerSpan::new(4, 5)),
197202
align: AlignLeft,
198203
sign: None,
199204
alternate: false,
@@ -218,6 +223,7 @@ fn format_counts() {
218223
position_span: InnerSpan { start: 2, end: 2 },
219224
format: FormatSpec {
220225
fill: None,
226+
fill_span: None,
221227
align: AlignUnknown,
222228
sign: None,
223229
alternate: false,
@@ -239,6 +245,7 @@ fn format_counts() {
239245
position_span: InnerSpan { start: 2, end: 2 },
240246
format: FormatSpec {
241247
fill: None,
248+
fill_span: None,
242249
align: AlignUnknown,
243250
sign: None,
244251
alternate: false,
@@ -260,6 +267,7 @@ fn format_counts() {
260267
position_span: InnerSpan { start: 2, end: 3 },
261268
format: FormatSpec {
262269
fill: None,
270+
fill_span: None,
263271
align: AlignUnknown,
264272
sign: None,
265273
alternate: false,
@@ -281,6 +289,7 @@ fn format_counts() {
281289
position_span: InnerSpan { start: 2, end: 2 },
282290
format: FormatSpec {
283291
fill: None,
292+
fill_span: None,
284293
align: AlignUnknown,
285294
sign: None,
286295
alternate: false,
@@ -302,6 +311,7 @@ fn format_counts() {
302311
position_span: InnerSpan { start: 2, end: 2 },
303312
format: FormatSpec {
304313
fill: None,
314+
fill_span: None,
305315
align: AlignUnknown,
306316
sign: None,
307317
alternate: false,
@@ -323,6 +333,7 @@ fn format_counts() {
323333
position_span: InnerSpan { start: 2, end: 2 },
324334
format: FormatSpec {
325335
fill: None,
336+
fill_span: None,
326337
align: AlignUnknown,
327338
sign: None,
328339
alternate: false,
@@ -344,6 +355,7 @@ fn format_counts() {
344355
position_span: InnerSpan { start: 2, end: 2 },
345356
format: FormatSpec {
346357
fill: None,
358+
fill_span: None,
347359
align: AlignUnknown,
348360
sign: None,
349361
alternate: false,
@@ -368,6 +380,7 @@ fn format_flags() {
368380
position_span: InnerSpan { start: 2, end: 2 },
369381
format: FormatSpec {
370382
fill: None,
383+
fill_span: None,
371384
align: AlignUnknown,
372385
sign: Some(Sign::Minus),
373386
alternate: false,
@@ -389,6 +402,7 @@ fn format_flags() {
389402
position_span: InnerSpan { start: 2, end: 2 },
390403
format: FormatSpec {
391404
fill: None,
405+
fill_span: None,
392406
align: AlignUnknown,
393407
sign: Some(Sign::Plus),
394408
alternate: true,
@@ -415,6 +429,7 @@ fn format_mixture() {
415429
position_span: InnerSpan { start: 7, end: 8 },
416430
format: FormatSpec {
417431
fill: None,
432+
fill_span: None,
418433
align: AlignUnknown,
419434
sign: None,
420435
alternate: false,

tests/ui/fmt/closing-brace-as-fill.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// issue: 112732
2+
3+
// `}` is typoed since it is interpreted as a fill character rather than a closing bracket
4+
5+
fn main() {
6+
println!("Hello, world! {0:}<3", 2);
7+
//~^ ERROR invalid format string: expected `'}'` but string was terminated
8+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: invalid format string: expected `'}'` but string was terminated
2+
--> $DIR/closing-brace-as-fill.rs:6:35
3+
|
4+
LL | println!("Hello, world! {0:}<3", 2);
5+
| - ^ expected `'}'` in format string
6+
| |
7+
| this is not interpreted as a formatting closing brace
8+
|
9+
= note: the character `'}'` is interpreted as a fill character because of the `:` that precedes it
10+
11+
error: aborting due to previous error
12+

0 commit comments

Comments
 (0)