From 9857de218f10cfbe750d4c8165d960ae0da63cf4 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 9 Nov 2022 22:40:51 +0900 Subject: [PATCH] shift no characters when using raw string literals remove `find_skips` remove unnecessary variables --- compiler/rustc_parse_format/src/lib.rs | 150 +++++++++--------- src/test/ui/fmt/format-raw-string-error.rs | 3 + .../ui/fmt/format-raw-string-error.stderr | 10 ++ src/test/ui/fmt/issue-104142.rs | 6 + src/test/ui/fmt/issue-104142.stderr | 10 ++ 5 files changed, 103 insertions(+), 76 deletions(-) create mode 100644 src/test/ui/fmt/format-raw-string-error.rs create mode 100644 src/test/ui/fmt/format-raw-string-error.stderr create mode 100644 src/test/ui/fmt/issue-104142.rs create mode 100644 src/test/ui/fmt/issue-104142.stderr diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 54bf4d1d6b738..0113eb4e3d102 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -818,96 +818,94 @@ fn find_skips_from_snippet( _ => return (vec![], false), }; - fn find_skips(snippet: &str, is_raw: bool) -> Vec { - let mut s = snippet.char_indices(); - let mut skips = vec![]; - while let Some((pos, c)) = s.next() { - match (c, s.clone().next()) { - // skip whitespace and empty lines ending in '\\' - ('\\', Some((next_pos, '\n'))) if !is_raw => { - skips.push(pos); - skips.push(next_pos); - let _ = s.next(); + if str_style.is_some() { + return (vec![], true); + } - while let Some((pos, c)) = s.clone().next() { - if matches!(c, ' ' | '\n' | '\t') { - skips.push(pos); - let _ = s.next(); - } else { - break; - } - } - } - ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => { - 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; - } + let snippet = &snippet[1..snippet.len() - 1]; + + let mut s = snippet.char_indices(); + let mut skips = vec![]; + while let Some((pos, c)) = s.next() { + match (c, s.clone().next()) { + // skip whitespace and empty lines ending in '\\' + ('\\', Some((next_pos, '\n'))) => { + skips.push(pos); + skips.push(next_pos); + let _ = s.next(); + + while let Some((pos, c)) = s.clone().next() { + if matches!(c, ' ' | '\n' | '\t') { + skips.push(pos); + let _ = s.next(); + } else { + break; } } - ('\\', Some((_, 'u'))) if !is_raw => { + } + ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => { + skips.push(next_pos); + let _ = s.next(); + } + ('\\', Some((_, 'x'))) => { + for _ in 0..3 { + // consume `\xAB` literal if let Some((pos, _)) = s.next() { skips.push(pos); + } else { + break; } - if let Some((next_pos, next_c)) = s.next() { - if next_c == '{' { - // consume up to 6 hexanumeric chars - let digits_len = - s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count(); - - let len_utf8 = s - .as_str() - .get(..digits_len) - .and_then(|digits| u32::from_str_radix(digits, 16).ok()) - .and_then(char::from_u32) - .map_or(1, char::len_utf8); - - // Skip the digits, for chars that encode to more than 1 utf-8 byte - // exclude as many digits as it is greater than 1 byte - // - // So for a 3 byte character, exclude 2 digits - let required_skips = - digits_len.saturating_sub(len_utf8.saturating_sub(1)); - - // skip '{' and '}' also - for pos in (next_pos..).take(required_skips + 2) { - skips.push(pos) - } + } + } + ('\\', Some((_, 'u'))) => { + if let Some((pos, _)) = s.next() { + skips.push(pos); + } + if let Some((next_pos, next_c)) = s.next() { + if next_c == '{' { + // consume up to 6 hexanumeric chars + let digits_len = + s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count(); + + let len_utf8 = s + .as_str() + .get(..digits_len) + .and_then(|digits| u32::from_str_radix(digits, 16).ok()) + .and_then(char::from_u32) + .map_or(1, char::len_utf8); + + // Skip the digits, for chars that encode to more than 1 utf-8 byte + // exclude as many digits as it is greater than 1 byte + // + // So for a 3 byte character, exclude 2 digits + let required_skips = digits_len.saturating_sub(len_utf8.saturating_sub(1)); + + // skip '{' and '}' also + for pos in (next_pos..).take(required_skips + 2) { + skips.push(pos) + } - s.nth(digits_len); - } 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; + s.nth(digits_len); + } 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; } } } - _ => {} } + _ => {} } - skips } - - let r_start = str_style.map_or(0, |r| r + 1); - let r_end = str_style.unwrap_or(0); - let s = &snippet[r_start + 1..snippet.len() - r_end - 1]; - (find_skips(s, str_style.is_some()), true) + (skips, true) } #[cfg(test)] diff --git a/src/test/ui/fmt/format-raw-string-error.rs b/src/test/ui/fmt/format-raw-string-error.rs new file mode 100644 index 0000000000000..9f0bc01a749cf --- /dev/null +++ b/src/test/ui/fmt/format-raw-string-error.rs @@ -0,0 +1,3 @@ +fn main() { + println!(r#"\'\'\'\'\'\'\'\'\'\'\'\'\'\'}"#); //~ ERROR invalid format string: unmatched `}` found +} diff --git a/src/test/ui/fmt/format-raw-string-error.stderr b/src/test/ui/fmt/format-raw-string-error.stderr new file mode 100644 index 0000000000000..8d61950d8c2ad --- /dev/null +++ b/src/test/ui/fmt/format-raw-string-error.stderr @@ -0,0 +1,10 @@ +error: invalid format string: unmatched `}` found + --> $DIR/format-raw-string-error.rs:2:45 + | +LL | println!(r#"\'\'\'\'\'\'\'\'\'\'\'\'\'\'}"#); + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + +error: aborting due to previous error + diff --git a/src/test/ui/fmt/issue-104142.rs b/src/test/ui/fmt/issue-104142.rs new file mode 100644 index 0000000000000..8d7283a719780 --- /dev/null +++ b/src/test/ui/fmt/issue-104142.rs @@ -0,0 +1,6 @@ +fn main() { + println!( + r#" + \"\'}、"# //~ ERROR invalid format string: unmatched `}` found + ); +} diff --git a/src/test/ui/fmt/issue-104142.stderr b/src/test/ui/fmt/issue-104142.stderr new file mode 100644 index 0000000000000..d41644faa2827 --- /dev/null +++ b/src/test/ui/fmt/issue-104142.stderr @@ -0,0 +1,10 @@ +error: invalid format string: unmatched `}` found + --> $DIR/issue-104142.rs:4:9 + | +LL | \"\'}、"# + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + +error: aborting due to previous error +