diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index d13de0029682e..ecb34e43c590c 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -945,12 +945,36 @@ impl<'a> StringReader<'a> { self.scan_unicode_escape(delim) && !ascii_only } else { let span = self.mk_sp(start, self.pos); - self.sess.span_diagnostic - .struct_span_err(span, "incorrect unicode escape sequence") - .span_help(span, - "format of unicode escape sequences is \ - `\\u{…}`") - .emit(); + let mut suggestion = "\\u{".to_owned(); + let mut err = self.sess.span_diagnostic.struct_span_err( + span, + "incorrect unicode escape sequence", + ); + let mut i = 0; + while let (Some(ch), true) = (self.ch, i < 6) { + if ch.is_digit(16) { + suggestion.push(ch); + self.bump(); + i += 1; + } else { + break; + } + } + if i != 0 { + suggestion.push('}'); + err.span_suggestion_with_applicability( + self.mk_sp(start, self.pos), + "format of unicode escape sequences uses braces", + suggestion, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help( + span, + "format of unicode escape sequences is `\\u{...}`", + ); + } + err.emit(); false }; if ascii_only { diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index ac1402a0faaa5..c11f27f3ed589 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -808,12 +808,57 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, } ('\\', Some((next_pos, 'n'))) | ('\\', Some((next_pos, 't'))) | + ('\\', Some((next_pos, '0'))) | ('\\', Some((next_pos, '\\'))) | ('\\', Some((next_pos, '\''))) | ('\\', Some((next_pos, '\"'))) => { skips.push(*next_pos); let _ = s.next(); } + ('\\', Some((_, 'x'))) if !is_raw => { + for _ in 0..3 { // consume `\xAB` literal + if let Some((pos, _)) = s.next() { + skips.push(pos); + } else { + break; + } + } + } + ('\\', Some((_, 'u'))) if !is_raw => { + if let Some((pos, _)) = s.next() { + skips.push(pos); + } + if let Some((next_pos, next_c)) = s.next() { + if next_c == '{' { + skips.push(next_pos); + let mut i = 0; // consume up to 6 hexanumeric chars + closing `}` + while let (Some((next_pos, c)), true) = (s.next(), i < 7) { + if c.is_digit(16) { + skips.push(next_pos); + } else if c == '}' { + skips.push(next_pos); + break; + } else { + break; + } + i += 1; + } + } else if next_c.is_digit(16) { + skips.push(next_pos); + // We suggest adding `{` and `}` when appropriate, accept it here as if + // it were correct + let mut i = 0; // consume up to 6 hexanumeric chars + while let (Some((next_pos, c)), _) = (s.next(), i < 6) { + if c.is_digit(16) { + skips.push(next_pos); + } else { + break; + } + i += 1; + } + } + } + } _ if eat_ws => { // `take_while(|c| c.is_whitespace())` eat_ws = false; } diff --git a/src/test/ui/fmt/format-string-error-2.rs b/src/test/ui/fmt/format-string-error-2.rs index 3c6c15c06bb2a..5c25ae502ff6d 100644 --- a/src/test/ui/fmt/format-string-error-2.rs +++ b/src/test/ui/fmt/format-string-error-2.rs @@ -69,4 +69,19 @@ raw { \n //~^^ ERROR invalid format string println!("\t{}"); //~^ ERROR 1 positional argument in format string + + // note: `\x7B` is `{` + println!("\x7B}\u{8} {", 1); + //~^ ERROR invalid format string: expected `'}'` but string was terminated + + println!("\x7B}\u8 {", 1); + //~^ ERROR incorrect unicode escape sequence + //~| ERROR argument never used + + // note: raw strings don't escape `\xFF` and `\u{FF}` sequences + println!(r#"\x7B}\u{8} {"#, 1); + //~^ ERROR invalid format string: unmatched `}` found + + println!(r#"\x7B}\u8 {"#, 1); + //~^ ERROR invalid format string: unmatched `}` found } diff --git a/src/test/ui/fmt/format-string-error-2.stderr b/src/test/ui/fmt/format-string-error-2.stderr index face0bc0f5f68..baab8529940c5 100644 --- a/src/test/ui/fmt/format-string-error-2.stderr +++ b/src/test/ui/fmt/format-string-error-2.stderr @@ -1,3 +1,11 @@ +error: incorrect unicode escape sequence + --> $DIR/format-string-error-2.rs:77:20 + | +LL | println!("/x7B}/u8 {", 1); + | ^^- + | | + | help: format of unicode escape sequences uses braces: `/u{8}` + error: invalid format string: expected `'}'`, found `'a'` --> $DIR/format-string-error-2.rs:5:5 | @@ -139,5 +147,39 @@ error: 1 positional argument in format string, but no arguments were given LL | println!("/t{}"); | ^^ -error: aborting due to 14 previous errors +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/format-string-error-2.rs:74:27 + | +LL | println!("/x7B}/u{8} {", 1); + | -^ expected `'}'` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: argument never used + --> $DIR/format-string-error-2.rs:77:28 + | +LL | println!("/x7B}/u8 {", 1); + | ------------ ^ argument never used + | | + | formatting specifier missing + +error: invalid format string: unmatched `}` found + --> $DIR/format-string-error-2.rs:82:21 + | +LL | println!(r#"/x7B}/u{8} {"#, 1); + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + +error: invalid format string: unmatched `}` found + --> $DIR/format-string-error-2.rs:85:21 + | +LL | println!(r#"/x7B}/u8 {"#, 1); + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + +error: aborting due to 19 previous errors diff --git a/src/test/ui/parser/issue-23620-invalid-escapes.rs b/src/test/ui/parser/issue-23620-invalid-escapes.rs index 6895a926b4f0c..f8a2f1cc7e6c9 100644 --- a/src/test/ui/parser/issue-23620-invalid-escapes.rs +++ b/src/test/ui/parser/issue-23620-invalid-escapes.rs @@ -35,4 +35,7 @@ fn main() { //~^ ERROR invalid character in numeric character escape: //~^^ ERROR form of character escape may only be used with characters in the range [\x00-\x7f] //~^^^ ERROR incorrect unicode escape sequence + + let _ = "\u8f"; + //~^ ERROR incorrect unicode escape sequence } diff --git a/src/test/ui/parser/issue-23620-invalid-escapes.stderr b/src/test/ui/parser/issue-23620-invalid-escapes.stderr index b1a5764e891d8..f6e476ab0cd1d 100644 --- a/src/test/ui/parser/issue-23620-invalid-escapes.stderr +++ b/src/test/ui/parser/issue-23620-invalid-escapes.stderr @@ -16,7 +16,7 @@ error: incorrect unicode escape sequence LL | let _ = b'/u'; | ^^ | -help: format of unicode escape sequences is `/u{…}` +help: format of unicode escape sequences is `/u{...}` --> $DIR/issue-23620-invalid-escapes.rs:10:15 | LL | let _ = b'/u'; @@ -82,7 +82,7 @@ error: incorrect unicode escape sequence LL | let _ = b"/u{a4a4} /xf /u"; | ^^ | -help: format of unicode escape sequences is `/u{…}` +help: format of unicode escape sequences is `/u{...}` --> $DIR/issue-23620-invalid-escapes.rs:28:28 | LL | let _ = b"/u{a4a4} /xf /u"; @@ -112,11 +112,19 @@ error: incorrect unicode escape sequence LL | let _ = "/xf /u"; | ^^ | -help: format of unicode escape sequences is `/u{…}` +help: format of unicode escape sequences is `/u{...}` --> $DIR/issue-23620-invalid-escapes.rs:34:18 | LL | let _ = "/xf /u"; | ^^ -error: aborting due to 17 previous errors +error: incorrect unicode escape sequence + --> $DIR/issue-23620-invalid-escapes.rs:39:14 + | +LL | let _ = "/u8f"; + | ^^-- + | | + | help: format of unicode escape sequences uses braces: `/u{8f}` + +error: aborting due to 18 previous errors