diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 20f4646490a6b..c807b9ee7b296 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -491,6 +491,7 @@ impl<'input> Parser<'input> { ('<' | '^' | '>', _) => self.suggest_format_align(c), (',', _) => self.suggest_unsupported_python_numeric_grouping(), ('=', '}') => self.suggest_rust_debug_printing_macro(), + ('+', _) => self.suggest_format_missing_colon_for_sign(), _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } @@ -939,6 +940,23 @@ impl<'input> Parser<'input> { } } + fn suggest_format_missing_colon_for_sign(&mut self) { + if let Some((range, _)) = self.consume_pos('+') { + self.errors.insert( + 0, + ParseError { + description: "the `+` sign flag must appear after `:` in a format string" + .to_owned(), + note: Some("`+` comes after `:`, try `{:+}` instead of `{+}`".to_owned()), + label: "expected `:` before `+` sign flag".to_owned(), + span: range, + secondary_label: None, + suggestion: Suggestion::None, + }, + ); + } + } + fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) { // If the argument is not an identifier, it is not a field access. if !arg.is_identifier() { diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index a6c7e1890abd1..c5e94ad7acd46 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -595,3 +595,13 @@ fn diagnostic_format_mod() { assert_eq!(parser.line_spans, &[]); assert!(parser.errors.is_empty()); } +#[test] +fn format_plus_sign_missing_colon_error() { + // `{+}` should produce an error suggesting `:` before `+` + let mut p = Parser::new("{+}", None, None, false, ParseMode::Format); + let _ = p.by_ref().collect::>>(); + assert!(!p.errors.is_empty()); + assert!(p.errors[0].description.contains("`+` sign flag must appear after `:`")); + assert!(p.errors[0].label.contains("expected `:` before `+` sign flag")); + assert_eq!(p.errors[0].span, 2..3); +} diff --git a/tests/ui/fmt/format-string-wrong-order.rs b/tests/ui/fmt/format-string-wrong-order.rs index 4d4e04ecd042d..bda19fe9bebce 100644 --- a/tests/ui/fmt/format-string-wrong-order.rs +++ b/tests/ui/fmt/format-string-wrong-order.rs @@ -20,4 +20,8 @@ fn main() { //~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}` println!("{0:#X>18}", 12345); //~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}` + format!("{+:}"); + //~^ ERROR invalid format string: the `+` sign flag must appear after `:` in a format string + format!("{bar+:}"); + //~^ ERROR invalid format string: the `+` sign flag must appear after `:` in a format string } diff --git a/tests/ui/fmt/format-string-wrong-order.stderr b/tests/ui/fmt/format-string-wrong-order.stderr index 441ae6d2e505f..eaaac2b9d8566 100644 --- a/tests/ui/fmt/format-string-wrong-order.stderr +++ b/tests/ui/fmt/format-string-wrong-order.stderr @@ -74,5 +74,21 @@ error: invalid format string: expected alignment specifier after `:` in format s LL | println!("{0:#X>18}", 12345); | ^ expected `>` to occur after `:` in format string -error: aborting due to 10 previous errors +error: invalid format string: the `+` sign flag must appear after `:` in a format string + --> $DIR/format-string-wrong-order.rs:23:15 + | +LL | format!("{+:}"); + | ^ expected `:` before `+` sign flag in format string + | + = note: `+` comes after `:`, try `{:+}` instead of `{+}` + +error: invalid format string: the `+` sign flag must appear after `:` in a format string + --> $DIR/format-string-wrong-order.rs:25:18 + | +LL | format!("{bar+:}"); + | ^ expected `:` before `+` sign flag in format string + | + = note: `+` comes after `:`, try `{:+}` instead of `{+}` + +error: aborting due to 12 previous errors