Skip to content

Commit ec5610f

Browse files
authored
Rollup merge of #72047 - Julian-Wollersberger:literal_error_reporting_cleanup, r=petrochenkov
Literal error reporting cleanup While doing some performance work, I noticed some code duplication in `librustc_parser/lexer/mod.rs`, so I cleaned it up. This PR is probably best reviewed commit by commit. I'm not sure what the API stability practices for `librustc_lexer` are. Four public methods in `unescape.rs` can be removed, but two are used by clippy, so I left them in for now. I could open a PR for Rust-Analyzer when this one lands. But how do I open a PR for clippy? (Git submodules are frustrating to work with)
2 parents aecab5e + 43ae785 commit ec5610f

File tree

6 files changed

+132
-214
lines changed

6 files changed

+132
-214
lines changed

src/librustc_ast/util/literal.rs

+42-33
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use crate::tokenstream::TokenTree;
66

77
use rustc_data_structures::sync::Lrc;
88
use rustc_lexer::unescape::{unescape_byte, unescape_char};
9-
use rustc_lexer::unescape::{unescape_byte_str, unescape_str};
10-
use rustc_lexer::unescape::{unescape_raw_byte_str, unescape_raw_str};
9+
use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode};
1110
use rustc_span::symbol::{kw, sym, Symbol};
1211
use rustc_span::Span;
1312

@@ -59,45 +58,53 @@ impl LitKind {
5958
// new symbol because the string in the LitKind is different to the
6059
// string in the token.
6160
let s = symbol.as_str();
62-
let symbol = if s.contains(&['\\', '\r'][..]) {
63-
let mut buf = String::with_capacity(s.len());
64-
let mut error = Ok(());
65-
unescape_str(&s, &mut |_, unescaped_char| match unescaped_char {
66-
Ok(c) => buf.push(c),
67-
Err(_) => error = Err(LitError::LexerError),
68-
});
69-
error?;
70-
Symbol::intern(&buf)
71-
} else {
72-
symbol
73-
};
61+
let symbol =
62+
if s.contains(&['\\', '\r'][..]) {
63+
let mut buf = String::with_capacity(s.len());
64+
let mut error = Ok(());
65+
unescape_literal(&s, Mode::Str, &mut |_, unescaped_char| {
66+
match unescaped_char {
67+
Ok(c) => buf.push(c),
68+
Err(_) => error = Err(LitError::LexerError),
69+
}
70+
});
71+
error?;
72+
Symbol::intern(&buf)
73+
} else {
74+
symbol
75+
};
7476
LitKind::Str(symbol, ast::StrStyle::Cooked)
7577
}
7678
token::StrRaw(n) => {
7779
// Ditto.
7880
let s = symbol.as_str();
79-
let symbol = if s.contains('\r') {
80-
let mut buf = String::with_capacity(s.len());
81-
let mut error = Ok(());
82-
unescape_raw_str(&s, &mut |_, unescaped_char| match unescaped_char {
83-
Ok(c) => buf.push(c),
84-
Err(_) => error = Err(LitError::LexerError),
85-
});
86-
error?;
87-
buf.shrink_to_fit();
88-
Symbol::intern(&buf)
89-
} else {
90-
symbol
91-
};
81+
let symbol =
82+
if s.contains('\r') {
83+
let mut buf = String::with_capacity(s.len());
84+
let mut error = Ok(());
85+
unescape_literal(&s, Mode::RawStr, &mut |_, unescaped_char| {
86+
match unescaped_char {
87+
Ok(c) => buf.push(c),
88+
Err(_) => error = Err(LitError::LexerError),
89+
}
90+
});
91+
error?;
92+
buf.shrink_to_fit();
93+
Symbol::intern(&buf)
94+
} else {
95+
symbol
96+
};
9297
LitKind::Str(symbol, ast::StrStyle::Raw(n))
9398
}
9499
token::ByteStr => {
95100
let s = symbol.as_str();
96101
let mut buf = Vec::with_capacity(s.len());
97102
let mut error = Ok(());
98-
unescape_byte_str(&s, &mut |_, unescaped_byte| match unescaped_byte {
99-
Ok(c) => buf.push(c),
100-
Err(_) => error = Err(LitError::LexerError),
103+
unescape_byte_literal(&s, Mode::ByteStr, &mut |_, unescaped_byte| {
104+
match unescaped_byte {
105+
Ok(c) => buf.push(c),
106+
Err(_) => error = Err(LitError::LexerError),
107+
}
101108
});
102109
error?;
103110
buf.shrink_to_fit();
@@ -108,9 +115,11 @@ impl LitKind {
108115
let bytes = if s.contains('\r') {
109116
let mut buf = Vec::with_capacity(s.len());
110117
let mut error = Ok(());
111-
unescape_raw_byte_str(&s, &mut |_, unescaped_byte| match unescaped_byte {
112-
Ok(c) => buf.push(c),
113-
Err(_) => error = Err(LitError::LexerError),
118+
unescape_byte_literal(&s, Mode::RawByteStr, &mut |_, unescaped_byte| {
119+
match unescaped_byte {
120+
Ok(c) => buf.push(c),
121+
Err(_) => error = Err(LitError::LexerError),
122+
}
114123
});
115124
error?;
116125
buf.shrink_to_fit();

src/librustc_lexer/src/lib.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
//! Low-level Rust lexer.
22
//!
3+
//! The idea with `librustc_lexer` is to make a reusable library,
4+
//! by separating out pure lexing and rustc-specific concerns, like spans,
5+
//! error reporting an interning. So, rustc_lexer operates directly on `&str`,
6+
//! produces simple tokens which are a pair of type-tag and a bit of original text,
7+
//! and does not report errors, instead storing them as flags on the token.
8+
//!
39
//! Tokens produced by this lexer are not yet ready for parsing the Rust syntax,
410
//! for that see `librustc_parse::lexer`, which converts this basic token stream
511
//! into wide tokens used by actual parser.
@@ -719,6 +725,9 @@ impl Cursor<'_> {
719725

720726
// Check that amount of closing '#' symbols
721727
// is equal to the amount of opening ones.
728+
// Note that this will not consume extra trailing `#` characters:
729+
// `r###"abcde"####` is lexed as a `LexedRawString { n_hashes: 3 }`
730+
// followed by a `#` token.
722731
let mut hashes_left = n_start_hashes;
723732
let is_closing_hash = |c| {
724733
if c == '#' && hashes_left != 0 {
@@ -739,8 +748,8 @@ impl Cursor<'_> {
739748
possible_terminator_offset: None,
740749
};
741750
} else if n_end_hashes > max_hashes {
742-
// Keep track of possible terminators to give a hint about where there might be
743-
// a missing terminator
751+
// Keep track of possible terminators to give a hint about
752+
// where there might be a missing terminator
744753
possible_terminator_offset =
745754
Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len);
746755
max_hashes = n_end_hashes;

src/librustc_lexer/src/unescape.rs

+41-51
Original file line numberDiff line numberDiff line change
@@ -58,69 +58,57 @@ pub enum EscapeError {
5858
NonAsciiCharInByteString,
5959
}
6060

61-
/// Takes a contents of a char literal (without quotes), and returns an
62-
/// unescaped char or an error
63-
pub fn unescape_char(literal_text: &str) -> Result<char, (usize, EscapeError)> {
64-
let mut chars = literal_text.chars();
65-
unescape_char_or_byte(&mut chars, Mode::Char)
66-
.map_err(|err| (literal_text.len() - chars.as_str().len(), err))
67-
}
68-
69-
/// Takes a contents of a byte literal (without quotes), and returns an
70-
/// unescaped byte or an error.
71-
pub fn unescape_byte(literal_text: &str) -> Result<u8, (usize, EscapeError)> {
72-
let mut chars = literal_text.chars();
73-
unescape_char_or_byte(&mut chars, Mode::Byte)
74-
.map(byte_from_char)
75-
.map_err(|err| (literal_text.len() - chars.as_str().len(), err))
76-
}
77-
78-
/// Takes a contents of a string literal (without quotes) and produces a
61+
/// Takes a contents of a literal (without quotes) and produces a
7962
/// sequence of escaped characters or errors.
8063
/// Values are returned through invoking of the provided callback.
81-
pub fn unescape_str<F>(literal_text: &str, callback: &mut F)
64+
pub fn unescape_literal<F>(literal_text: &str, mode: Mode, callback: &mut F)
8265
where
8366
F: FnMut(Range<usize>, Result<char, EscapeError>),
8467
{
85-
unescape_str_or_byte_str(literal_text, Mode::Str, callback)
68+
match mode {
69+
Mode::Char | Mode::Byte => {
70+
let mut chars = literal_text.chars();
71+
let result = unescape_char_or_byte(&mut chars, mode);
72+
// The Chars iterator moved forward.
73+
callback(0..(literal_text.len() - chars.as_str().len()), result);
74+
}
75+
Mode::Str | Mode::ByteStr => unescape_str_or_byte_str(literal_text, mode, callback),
76+
// NOTE: Raw strings do not perform any explicit character escaping, here we
77+
// only translate CRLF to LF and produce errors on bare CR.
78+
Mode::RawStr | Mode::RawByteStr => {
79+
unescape_raw_str_or_byte_str(literal_text, mode, callback)
80+
}
81+
}
8682
}
8783

88-
/// Takes a contents of a byte string literal (without quotes) and produces a
89-
/// sequence of bytes or errors.
84+
/// Takes a contents of a byte, byte string or raw byte string (without quotes)
85+
/// and produces a sequence of bytes or errors.
9086
/// Values are returned through invoking of the provided callback.
91-
pub fn unescape_byte_str<F>(literal_text: &str, callback: &mut F)
87+
pub fn unescape_byte_literal<F>(literal_text: &str, mode: Mode, callback: &mut F)
9288
where
9389
F: FnMut(Range<usize>, Result<u8, EscapeError>),
9490
{
95-
unescape_str_or_byte_str(literal_text, Mode::ByteStr, &mut |range, char| {
96-
callback(range, char.map(byte_from_char))
91+
assert!(mode.is_bytes());
92+
unescape_literal(literal_text, mode, &mut |range, result| {
93+
callback(range, result.map(byte_from_char));
9794
})
9895
}
9996

100-
/// Takes a contents of a raw string literal (without quotes) and produces a
101-
/// sequence of characters or errors.
102-
/// Values are returned through invoking of the provided callback.
103-
/// NOTE: Raw strings do not perform any explicit character escaping, here we
104-
/// only translate CRLF to LF and produce errors on bare CR.
105-
pub fn unescape_raw_str<F>(literal_text: &str, callback: &mut F)
106-
where
107-
F: FnMut(Range<usize>, Result<char, EscapeError>),
108-
{
109-
unescape_raw_str_or_byte_str(literal_text, Mode::Str, callback)
97+
/// Takes a contents of a char literal (without quotes), and returns an
98+
/// unescaped char or an error
99+
pub fn unescape_char(literal_text: &str) -> Result<char, (usize, EscapeError)> {
100+
let mut chars = literal_text.chars();
101+
unescape_char_or_byte(&mut chars, Mode::Char)
102+
.map_err(|err| (literal_text.len() - chars.as_str().len(), err))
110103
}
111104

112-
/// Takes a contents of a raw byte string literal (without quotes) and produces a
113-
/// sequence of bytes or errors.
114-
/// Values are returned through invoking of the provided callback.
115-
/// NOTE: Raw strings do not perform any explicit character escaping, here we
116-
/// only translate CRLF to LF and produce errors on bare CR.
117-
pub fn unescape_raw_byte_str<F>(literal_text: &str, callback: &mut F)
118-
where
119-
F: FnMut(Range<usize>, Result<u8, EscapeError>),
120-
{
121-
unescape_raw_str_or_byte_str(literal_text, Mode::ByteStr, &mut |range, char| {
122-
callback(range, char.map(byte_from_char))
123-
})
105+
/// Takes a contents of a byte literal (without quotes), and returns an
106+
/// unescaped byte or an error.
107+
pub fn unescape_byte(literal_text: &str) -> Result<u8, (usize, EscapeError)> {
108+
let mut chars = literal_text.chars();
109+
unescape_char_or_byte(&mut chars, Mode::Byte)
110+
.map(byte_from_char)
111+
.map_err(|err| (literal_text.len() - chars.as_str().len(), err))
124112
}
125113

126114
/// What kind of literal do we parse.
@@ -130,13 +118,15 @@ pub enum Mode {
130118
Str,
131119
Byte,
132120
ByteStr,
121+
RawStr,
122+
RawByteStr,
133123
}
134124

135125
impl Mode {
136126
pub fn in_single_quotes(self) -> bool {
137127
match self {
138128
Mode::Char | Mode::Byte => true,
139-
Mode::Str | Mode::ByteStr => false,
129+
Mode::Str | Mode::ByteStr | Mode::RawStr | Mode::RawByteStr => false,
140130
}
141131
}
142132

@@ -146,8 +136,8 @@ impl Mode {
146136

147137
pub fn is_bytes(self) -> bool {
148138
match self {
149-
Mode::Byte | Mode::ByteStr => true,
150-
Mode::Char | Mode::Str => false,
139+
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
140+
Mode::Char | Mode::Str | Mode::RawStr => false,
151141
}
152142
}
153143
}
@@ -345,7 +335,7 @@ where
345335

346336
fn byte_from_char(c: char) -> u8 {
347337
let res = c as u32;
348-
assert!(res <= u8::max_value() as u32, "guaranteed because of Mode::Byte(Str)");
338+
assert!(res <= u8::max_value() as u32, "guaranteed because of Mode::ByteStr");
349339
res as u8
350340
}
351341

src/librustc_lexer/src/unescape/tests.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fn test_unescape_char_good() {
102102
fn test_unescape_str_good() {
103103
fn check(literal_text: &str, expected: &str) {
104104
let mut buf = Ok(String::with_capacity(literal_text.len()));
105-
unescape_str(literal_text, &mut |range, c| {
105+
unescape_literal(literal_text, Mode::Str, &mut |range, c| {
106106
if let Ok(b) = &mut buf {
107107
match c {
108108
Ok(c) => b.push(c),
@@ -222,7 +222,7 @@ fn test_unescape_byte_good() {
222222
fn test_unescape_byte_str_good() {
223223
fn check(literal_text: &str, expected: &[u8]) {
224224
let mut buf = Ok(Vec::with_capacity(literal_text.len()));
225-
unescape_byte_str(literal_text, &mut |range, c| {
225+
unescape_byte_literal(literal_text, Mode::ByteStr, &mut |range, c| {
226226
if let Ok(b) = &mut buf {
227227
match c {
228228
Ok(c) => b.push(c),
@@ -246,7 +246,7 @@ fn test_unescape_byte_str_good() {
246246
fn test_unescape_raw_str() {
247247
fn check(literal: &str, expected: &[(Range<usize>, Result<char, EscapeError>)]) {
248248
let mut unescaped = Vec::with_capacity(literal.len());
249-
unescape_raw_str(literal, &mut |range, res| unescaped.push((range, res)));
249+
unescape_literal(literal, Mode::RawStr, &mut |range, res| unescaped.push((range, res)));
250250
assert_eq!(unescaped, expected);
251251
}
252252

@@ -258,7 +258,9 @@ fn test_unescape_raw_str() {
258258
fn test_unescape_raw_byte_str() {
259259
fn check(literal: &str, expected: &[(Range<usize>, Result<u8, EscapeError>)]) {
260260
let mut unescaped = Vec::with_capacity(literal.len());
261-
unescape_raw_byte_str(literal, &mut |range, res| unescaped.push((range, res)));
261+
unescape_byte_literal(literal, Mode::RawByteStr, &mut |range, res| {
262+
unescaped.push((range, res))
263+
});
262264
assert_eq!(unescaped, expected);
263265
}
264266

0 commit comments

Comments
 (0)