Skip to content

Commit 37d9ea7

Browse files
committed
Improve scan_escape.
`scan_escape` currently has a fast path (for when the first char isn't '\\') and a slow path. This commit changes `scan_escape` so it only handles the slow path, i.e. the actual escaping code. The fast path is inlined into the two call sites. This change makes the code faster, because there is no function call overhead on the fast path. (`scan_escape` is a big function and doesn't get inlined.) This change also improves readability, because it removes a bunch of mode checks on the the fast paths.
1 parent 532d3cd commit 37d9ea7

File tree

1 file changed

+22
-23
lines changed

1 file changed

+22
-23
lines changed

compiler/rustc_lexer/src/unescape.rs

+22-23
Original file line numberDiff line numberDiff line change
@@ -159,26 +159,8 @@ impl Mode {
159159
}
160160
}
161161

162-
fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
163-
if first_char != '\\' {
164-
// Previous character was not a slash, and we don't expect it to be
165-
// an escape-only character.
166-
return match first_char {
167-
'\t' | '\n' => Err(EscapeError::EscapeOnlyChar),
168-
'\r' => Err(EscapeError::BareCarriageReturn),
169-
'\'' if mode.in_single_quotes() => Err(EscapeError::EscapeOnlyChar),
170-
'"' if mode.in_double_quotes() => Err(EscapeError::EscapeOnlyChar),
171-
_ => {
172-
if mode.is_bytes() && !first_char.is_ascii() {
173-
// Byte literal can't be a non-ascii character.
174-
return Err(EscapeError::NonAsciiCharInByte);
175-
}
176-
Ok(first_char)
177-
}
178-
};
179-
}
180-
181-
// Previous character is '\\', try to unescape it.
162+
fn scan_escape(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
163+
// Previous character was '\\', unescape what follows.
182164

183165
let second_char = chars.next().ok_or(EscapeError::LoneSlash)?;
184166

@@ -270,9 +252,24 @@ fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result<ch
270252
Ok(res)
271253
}
272254

255+
#[inline]
256+
fn ascii_check(first_char: char, mode: Mode) -> Result<char, EscapeError> {
257+
if mode.is_bytes() && !first_char.is_ascii() {
258+
// Byte literal can't be a non-ascii character.
259+
Err(EscapeError::NonAsciiCharInByte)
260+
} else {
261+
Ok(first_char)
262+
}
263+
}
264+
273265
fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
274266
let first_char = chars.next().ok_or(EscapeError::ZeroChars)?;
275-
let res = scan_escape(first_char, chars, mode)?;
267+
let res = match first_char {
268+
'\\' => scan_escape(chars, mode),
269+
'\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
270+
'\r' => Err(EscapeError::BareCarriageReturn),
271+
_ => ascii_check(first_char, mode),
272+
}?;
276273
if chars.next().is_some() {
277274
return Err(EscapeError::MoreThanOneChar);
278275
}
@@ -303,12 +300,14 @@ where
303300
skip_ascii_whitespace(&mut chars, start, callback);
304301
continue;
305302
}
306-
_ => scan_escape(first_char, &mut chars, mode),
303+
_ => scan_escape(&mut chars, mode),
307304
}
308305
}
309306
'\n' => Ok('\n'),
310307
'\t' => Ok('\t'),
311-
_ => scan_escape(first_char, &mut chars, mode),
308+
'"' => Err(EscapeError::EscapeOnlyChar),
309+
'\r' => Err(EscapeError::BareCarriageReturn),
310+
_ => ascii_check(first_char, mode),
312311
};
313312
let end = initial_len - chars.as_str().len();
314313
callback(start..end, unescaped_char);

0 commit comments

Comments
 (0)