shift no characters when using raw string literals
remove `find_skips` remove unnecessary variables
This commit is contained in:
parent
0aaad9e757
commit
9857de218f
5 changed files with 115 additions and 88 deletions
|
@ -818,96 +818,94 @@ fn find_skips_from_snippet(
|
||||||
_ => return (vec![], false),
|
_ => return (vec![], false),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
|
if str_style.is_some() {
|
||||||
let mut s = snippet.char_indices();
|
return (vec![], true);
|
||||||
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();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
('\\', 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 == '{' {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
skips
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let r_start = str_style.map_or(0, |r| r + 1);
|
let snippet = &snippet[1..snippet.len() - 1];
|
||||||
let r_end = str_style.unwrap_or(0);
|
|
||||||
let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
|
let mut s = snippet.char_indices();
|
||||||
(find_skips(s, str_style.is_some()), true)
|
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((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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
('\\', 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(skips, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
3
src/test/ui/fmt/format-raw-string-error.rs
Normal file
3
src/test/ui/fmt/format-raw-string-error.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
println!(r#"\'\'\'\'\'\'\'\'\'\'\'\'\'\'}"#); //~ ERROR invalid format string: unmatched `}` found
|
||||||
|
}
|
10
src/test/ui/fmt/format-raw-string-error.stderr
Normal file
10
src/test/ui/fmt/format-raw-string-error.stderr
Normal file
|
@ -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
|
||||||
|
|
6
src/test/ui/fmt/issue-104142.rs
Normal file
6
src/test/ui/fmt/issue-104142.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
fn main() {
|
||||||
|
println!(
|
||||||
|
r#"
|
||||||
|
\"\'}、"# //~ ERROR invalid format string: unmatched `}` found
|
||||||
|
);
|
||||||
|
}
|
10
src/test/ui/fmt/issue-104142.stderr
Normal file
10
src/test/ui/fmt/issue-104142.stderr
Normal file
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue