Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 307d1af

Browse files
committedJun 13, 2024
Remove superfluous escaping from byte, byte str, and c str literals
1 parent 9adf702 commit 307d1af

File tree

2 files changed

+94
-14
lines changed

2 files changed

+94
-14
lines changed
 

‎proc_macro/src/escape.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#[derive(Copy, Clone)]
2+
pub(crate) struct EscapeOptions {
3+
/// Produce \'.
4+
pub escape_single_quote: bool,
5+
/// Produce \".
6+
pub escape_double_quote: bool,
7+
/// Produce \x escapes for non-ASCII, and use \x rather than \u for ASCII
8+
/// control characters.
9+
pub escape_nonascii: bool,
10+
}
11+
12+
pub(crate) fn escape_bytes(bytes: &[u8], opt: EscapeOptions) -> String {
13+
let mut repr = String::new();
14+
15+
if opt.escape_nonascii {
16+
for &byte in bytes {
17+
escape_single_byte(byte, opt, &mut repr);
18+
}
19+
} else {
20+
let mut chunks = bytes.utf8_chunks();
21+
while let Some(chunk) = chunks.next() {
22+
for ch in chunk.valid().chars() {
23+
escape_single_char(ch, opt, &mut repr);
24+
}
25+
for &byte in chunk.invalid() {
26+
escape_single_byte(byte, opt, &mut repr);
27+
}
28+
}
29+
}
30+
31+
repr
32+
}
33+
34+
fn escape_single_byte(byte: u8, opt: EscapeOptions, repr: &mut String) {
35+
if byte == b'\0' {
36+
repr.push_str("\\0");
37+
} else if (byte == b'\'' && !opt.escape_single_quote)
38+
|| (byte == b'"' && !opt.escape_double_quote)
39+
{
40+
repr.push(byte as char);
41+
} else {
42+
// Escapes \t, \r, \n, \\, \', \", and uses \x## for non-ASCII and
43+
// for ASCII control characters.
44+
repr.extend(byte.escape_ascii().map(char::from));
45+
}
46+
}
47+
48+
fn escape_single_char(ch: char, opt: EscapeOptions, repr: &mut String) {
49+
if (ch == '\'' && !opt.escape_single_quote) || (ch == '"' && !opt.escape_double_quote) {
50+
repr.push(ch);
51+
} else {
52+
// Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for
53+
// non-printable characters and for Grapheme_Extend characters, which
54+
// includes things like U+0300 "Combining Grave Accent".
55+
repr.extend(ch.escape_debug());
56+
}
57+
}

‎proc_macro/src/lib.rs

+37-14
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@
4343
pub mod bridge;
4444

4545
mod diagnostic;
46+
mod escape;
4647

4748
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
4849
pub use diagnostic::{Diagnostic, Level, MultiSpan};
4950

51+
use crate::escape::{escape_bytes, EscapeOptions};
5052
use std::ffi::CStr;
5153
use std::ops::{Range, RangeBounds};
5254
use std::path::PathBuf;
@@ -1356,40 +1358,61 @@ impl Literal {
13561358
/// String literal.
13571359
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
13581360
pub fn string(string: &str) -> Literal {
1359-
let quoted = format!("{:?}", string);
1360-
assert!(quoted.starts_with('"') && quoted.ends_with('"'));
1361-
let symbol = &quoted[1..quoted.len() - 1];
1362-
Literal::new(bridge::LitKind::Str, symbol, None)
1361+
let escape = EscapeOptions {
1362+
escape_single_quote: false,
1363+
escape_double_quote: true,
1364+
escape_nonascii: false,
1365+
};
1366+
let repr = escape_bytes(string.as_bytes(), escape);
1367+
Literal::new(bridge::LitKind::Str, &repr, None)
13631368
}
13641369

13651370
/// Character literal.
13661371
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
13671372
pub fn character(ch: char) -> Literal {
1368-
let quoted = format!("{:?}", ch);
1369-
assert!(quoted.starts_with('\'') && quoted.ends_with('\''));
1370-
let symbol = &quoted[1..quoted.len() - 1];
1371-
Literal::new(bridge::LitKind::Char, symbol, None)
1373+
let escape = EscapeOptions {
1374+
escape_single_quote: true,
1375+
escape_double_quote: false,
1376+
escape_nonascii: false,
1377+
};
1378+
let repr = escape_bytes(ch.encode_utf8(&mut [0u8; 4]).as_bytes(), escape);
1379+
Literal::new(bridge::LitKind::Char, &repr, None)
13721380
}
13731381

13741382
/// Byte character literal.
13751383
#[stable(feature = "proc_macro_byte_character", since = "1.79.0")]
13761384
pub fn byte_character(byte: u8) -> Literal {
1377-
let string = [byte].escape_ascii().to_string();
1378-
Literal::new(bridge::LitKind::Byte, &string, None)
1385+
let escape = EscapeOptions {
1386+
escape_single_quote: true,
1387+
escape_double_quote: false,
1388+
escape_nonascii: true,
1389+
};
1390+
let repr = escape_bytes(&[byte], escape);
1391+
Literal::new(bridge::LitKind::Byte, &repr, None)
13791392
}
13801393

13811394
/// Byte string literal.
13821395
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
13831396
pub fn byte_string(bytes: &[u8]) -> Literal {
1384-
let string = bytes.escape_ascii().to_string();
1385-
Literal::new(bridge::LitKind::ByteStr, &string, None)
1397+
let escape = EscapeOptions {
1398+
escape_single_quote: false,
1399+
escape_double_quote: true,
1400+
escape_nonascii: true,
1401+
};
1402+
let repr = escape_bytes(bytes, escape);
1403+
Literal::new(bridge::LitKind::ByteStr, &repr, None)
13861404
}
13871405

13881406
/// C string literal.
13891407
#[stable(feature = "proc_macro_c_str_literals", since = "1.79.0")]
13901408
pub fn c_string(string: &CStr) -> Literal {
1391-
let string = string.to_bytes().escape_ascii().to_string();
1392-
Literal::new(bridge::LitKind::CStr, &string, None)
1409+
let escape = EscapeOptions {
1410+
escape_single_quote: false,
1411+
escape_double_quote: true,
1412+
escape_nonascii: false,
1413+
};
1414+
let repr = escape_bytes(string.to_bytes(), escape);
1415+
Literal::new(bridge::LitKind::CStr, &repr, None)
13931416
}
13941417

13951418
/// Returns the span encompassing this literal.

0 commit comments

Comments
 (0)
Failed to load comments.