Skip to content

Commit d1c9775

Browse files
committed
Auto merge of #13568 - noritada:fix/len-of-byte-string-with-escaped-newlines, r=Veykril
Fix the length displayed for byte string literals with escaped newlines This is a fix for the problem I reported earlier: "the length of byte strings containing escaped newlines is displayed two bytes longer when the first escaped character is a newline". I would appreciate it if you could review the fix. Many thanks. Closes #13567
2 parents 8a633fe + 2340d70 commit d1c9775

File tree

1 file changed

+40
-6
lines changed

1 file changed

+40
-6
lines changed

crates/syntax/src/ast/token_ext.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,17 +209,19 @@ impl ast::String {
209209
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
210210

211211
let mut buf = String::new();
212-
let mut text_iter = text.chars();
212+
let mut prev_end = 0;
213213
let mut has_error = false;
214214
unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match (
215215
unescaped_char,
216216
buf.capacity() == 0,
217217
) {
218218
(Ok(c), false) => buf.push(c),
219-
(Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
219+
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
220+
prev_end = char_range.end
221+
}
220222
(Ok(c), true) => {
221223
buf.reserve_exact(text.len());
222-
buf.push_str(&text[..char_range.start]);
224+
buf.push_str(&text[..prev_end]);
223225
buf.push(c);
224226
}
225227
(Err(_), _) => has_error = true,
@@ -252,17 +254,19 @@ impl ast::ByteString {
252254
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
253255

254256
let mut buf: Vec<u8> = Vec::new();
255-
let mut text_iter = text.chars();
257+
let mut prev_end = 0;
256258
let mut has_error = false;
257259
unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match (
258260
unescaped_char,
259261
buf.capacity() == 0,
260262
) {
261263
(Ok(c), false) => buf.push(c as u8),
262-
(Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
264+
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
265+
prev_end = char_range.end
266+
}
263267
(Ok(c), true) => {
264268
buf.reserve_exact(text.len());
265-
buf.extend_from_slice(text[..char_range.start].as_bytes());
269+
buf.extend_from_slice(text[..prev_end].as_bytes());
266270
buf.push(c as u8);
267271
}
268272
(Err(_), _) => has_error = true,
@@ -445,6 +449,36 @@ mod tests {
445449
check_string_value(r"\foobar", None);
446450
check_string_value(r"\nfoobar", "\nfoobar");
447451
check_string_value(r"C:\\Windows\\System32\\", "C:\\Windows\\System32\\");
452+
check_string_value(r"\x61bcde", "abcde");
453+
check_string_value(
454+
r"a\
455+
bcde", "abcde",
456+
);
457+
}
458+
459+
fn check_byte_string_value<'a, const N: usize>(
460+
lit: &str,
461+
expected: impl Into<Option<&'a [u8; N]>>,
462+
) {
463+
assert_eq!(
464+
ast::ByteString { syntax: make::tokens::literal(&format!("b\"{}\"", lit)) }
465+
.value()
466+
.as_deref(),
467+
expected.into().map(|value| &value[..])
468+
);
469+
}
470+
471+
#[test]
472+
fn test_byte_string_escape() {
473+
check_byte_string_value(r"foobar", b"foobar");
474+
check_byte_string_value(r"\foobar", None::<&[u8; 0]>);
475+
check_byte_string_value(r"\nfoobar", b"\nfoobar");
476+
check_byte_string_value(r"C:\\Windows\\System32\\", b"C:\\Windows\\System32\\");
477+
check_byte_string_value(r"\x61bcde", b"abcde");
478+
check_byte_string_value(
479+
r"a\
480+
bcde", b"abcde",
481+
);
448482
}
449483

450484
#[test]

0 commit comments

Comments
 (0)