From d827973a8d8af1fedbfac4eb5e8f78687e8d30f5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 2 Apr 2023 22:48:56 -0700 Subject: [PATCH 1/4] Add regression test of \0 escaping for issue 363 --- tests/test.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index 3f0bfd3c..4c2de3c8 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -114,6 +114,10 @@ fn literal_string() { assert_eq!(Literal::string("foo").to_string(), "\"foo\""); assert_eq!(Literal::string("\"").to_string(), "\"\\\"\""); assert_eq!(Literal::string("didn't").to_string(), "\"didn't\""); + assert_eq!( + Literal::string("a\00b\07c\08d\0e\0").to_string(), + "\"a\\00b\\07c\\08d\\0e\\0\"", + ); } #[test] @@ -147,6 +151,10 @@ fn literal_byte_string() { Literal::byte_string(b"\0\t\n\r\"\\2\x10").to_string(), "b\"\\0\\t\\n\\r\\\"\\\\2\\x10\"", ); + assert_eq!( + Literal::byte_string(b"a\00b\07c\08d\0e\0").to_string(), + "b\"a\\00b\\07c\\08d\\0e\\0\"", + ); } #[test] From 57b4db1f78944eda659ad2f1bd529fca94e4669c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 2 Apr 2023 22:54:50 -0700 Subject: [PATCH 2/4] Circumvent clippy::octal_escapes lint in generated literals --- src/fallback.rs | 29 +++++++++++++++++++++-------- tests/test.rs | 4 ++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/fallback.rs b/src/fallback.rs index 214843ae..bd5d9dad 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -958,12 +958,20 @@ impl Literal { pub fn string(t: &str) -> Literal { let mut repr = String::with_capacity(t.len() + 2); repr.push('"'); - for c in t.chars() { - if c == '\'' { + let mut chars = t.chars(); + while let Some(ch) = chars.next() { + if ch == '\0' + && chars + .as_str() + .starts_with(|next| '0' <= next && next <= '7') + { + // circumvent clippy::octal_escapes lint + repr.push_str("\\x00"); + } else if ch == '\'' { // escape_debug turns this into "\'" which is unnecessary. - repr.push(c); + repr.push(ch); } else { - repr.extend(c.escape_debug()); + repr.extend(ch.escape_debug()); } } repr.push('"'); @@ -985,16 +993,21 @@ impl Literal { pub fn byte_string(bytes: &[u8]) -> Literal { let mut escaped = "b\"".to_string(); - for b in bytes { + let mut bytes = bytes.iter(); + while let Some(&b) = bytes.next() { #[allow(clippy::match_overlapping_arm)] - match *b { - b'\0' => escaped.push_str(r"\0"), + match b { + b'\0' => escaped.push_str(match bytes.as_slice().first() { + // circumvent clippy::octal_escapes lint + Some(b'0'..=b'7') => r"\x00", + _ => r"\0", + }), b'\t' => escaped.push_str(r"\t"), b'\n' => escaped.push_str(r"\n"), b'\r' => escaped.push_str(r"\r"), b'"' => escaped.push_str("\\\""), b'\\' => escaped.push_str("\\\\"), - b'\x20'..=b'\x7E' => escaped.push(*b as char), + b'\x20'..=b'\x7E' => escaped.push(b as char), _ => { let _ = write!(escaped, "\\x{:02X}", b); } diff --git a/tests/test.rs b/tests/test.rs index 4c2de3c8..31881045 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -116,7 +116,7 @@ fn literal_string() { assert_eq!(Literal::string("didn't").to_string(), "\"didn't\""); assert_eq!( Literal::string("a\00b\07c\08d\0e\0").to_string(), - "\"a\\00b\\07c\\08d\\0e\\0\"", + "\"a\\x000b\\x007c\\08d\\0e\\0\"", ); } @@ -153,7 +153,7 @@ fn literal_byte_string() { ); assert_eq!( Literal::byte_string(b"a\00b\07c\08d\0e\0").to_string(), - "b\"a\\00b\\07c\\08d\\0e\\0\"", + "b\"a\\x000b\\x007c\\08d\\0e\\0\"", ); } From 5d3e58b9c6728636eb3b4ae1ea80d6448c155621 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 2 Apr 2023 23:06:53 -0700 Subject: [PATCH 3/4] Ignore octal_escapes clippy lint in test error: octal-looking escape in string literal --> tests/test.rs:118:25 | 118 | Literal::string("a\00b\07c\08d\0e\0").to_string(), | ^^^^^^^^^^^^^^^^^^^^ | = help: octal escapes are not supported, `\0` is always a null character = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes = note: `-D clippy::octal-escapes` implied by `-D clippy::all` help: if an octal escape was intended, use the hexadecimal representation instead | 118 | Literal::string("a\x00b\x07c\08d\0e\0").to_string(), | ~~~~~~~~~~~~~~~~~~~~~~ help: if the null character is intended, disambiguate using | 118 | Literal::string("a\x000b\x007c\08d\0e\0").to_string(), | ~~~~~~~~~~~~~~~~~~~~~~~~ error: octal-looking escape in byte string literal --> tests/test.rs:155:30 | 155 | Literal::byte_string(b"a\00b\07c\08d\0e\0").to_string(), | ^^^^^^^^^^^^^^^^^^^^^ | = help: octal escapes are not supported, `\0` is always a null byte = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes help: if an octal escape was intended, use the hexadecimal representation instead | 155 | Literal::byte_string(b"a\x00b\x07c\08d\0e\0").to_string(), | ~~~~~~~~~~~~~~~~~~~~~~~ help: if the null byte is intended, disambiguate using | 155 | Literal::byte_string(b"a\x000b\x007c\08d\0e\0").to_string(), | ~~~~~~~~~~~~~~~~~~~~~~~~~ --- tests/test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 31881045..75f69e2a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,7 +1,8 @@ #![allow( clippy::assertions_on_result_states, clippy::items_after_statements, - clippy::non_ascii_literal + clippy::non_ascii_literal, + clippy::octal_escapes )] use proc_macro2::{Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; From 6c216275bc87d8fadb4059d9912d3eef654f23c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 2 Apr 2023 23:12:20 -0700 Subject: [PATCH 4/4] Fix literal_string test on rustc older than 1.61 --- tests/test.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 75f69e2a..2bd93e0b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -115,10 +115,11 @@ fn literal_string() { assert_eq!(Literal::string("foo").to_string(), "\"foo\""); assert_eq!(Literal::string("\"").to_string(), "\"\\\"\""); assert_eq!(Literal::string("didn't").to_string(), "\"didn't\""); - assert_eq!( - Literal::string("a\00b\07c\08d\0e\0").to_string(), - "\"a\\x000b\\x007c\\08d\\0e\\0\"", - ); + + let repr = Literal::string("a\00b\07c\08d\0e\0").to_string(); + if repr != "\"a\\x000b\\x007c\\u{0}8d\\u{0}e\\u{0}\"" { + assert_eq!(repr, "\"a\\x000b\\x007c\\08d\\0e\\0\""); + } } #[test]