Skip to content

Commit 8075d54

Browse files
committed
Optimize the parser's last token handling.
The parser currently makes a heap copy of the last token in four cases: identifiers, paths, doc comments, and commas. The identifier and interpolation cases are unused, and for doc comments and commas we only need to record their presence, not their value. This commit consolidates the last token handling and avoids the unnecessary copies by replacing `last_token`, `last_token_eof`, and `last_token_interpolated` with a new field `last_token_kind`. This simplifies the parser slightly and speeds up parsing on some files by 3--4%.
1 parent f81f496 commit 8075d54

File tree

1 file changed

+39
-43
lines changed

1 file changed

+39
-43
lines changed

src/libsyntax/parse/parser.rs

+39-43
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
237237
lhs
238238
}
239239

240+
#[derive(PartialEq)]
241+
enum LastTokenKind {
242+
DocComment,
243+
Comma,
244+
Interpolated,
245+
Eof,
246+
Other,
247+
}
248+
240249
/* ident is handled by common.rs */
241250

242251
pub struct Parser<'a> {
@@ -248,10 +257,8 @@ pub struct Parser<'a> {
248257
/// the span of the prior token:
249258
pub last_span: Span,
250259
pub cfg: CrateConfig,
251-
/// the previous token or None (only stashed sometimes).
252-
pub last_token: Option<Box<token::Token>>,
253-
last_token_interpolated: bool,
254-
last_token_eof: bool,
260+
/// the previous token kind
261+
last_token_kind: LastTokenKind,
255262
pub buffer: [TokenAndSpan; 4],
256263
pub buffer_start: isize,
257264
pub buffer_end: isize,
@@ -362,9 +369,7 @@ impl<'a> Parser<'a> {
362369
token: tok0.tok,
363370
span: span,
364371
last_span: span,
365-
last_token: None,
366-
last_token_interpolated: false,
367-
last_token_eof: false,
372+
last_token_kind: LastTokenKind::Other,
368373
buffer: [
369374
placeholder.clone(),
370375
placeholder.clone(),
@@ -500,7 +505,7 @@ impl<'a> Parser<'a> {
500505
expr: PResult<'a, P<Expr>>)
501506
-> PResult<'a, (Span, P<Expr>)> {
502507
expr.map(|e| {
503-
if self.last_token_interpolated {
508+
if self.last_token_kind == LastTokenKind::Interpolated {
504509
(self.last_span, e)
505510
} else {
506511
(e.span, e)
@@ -520,21 +525,19 @@ impl<'a> Parser<'a> {
520525
self.bug("ident interpolation not converted to real token");
521526
}
522527
_ => {
523-
let last_token = self.last_token.clone().map(|t| *t);
524-
Err(match last_token {
525-
Some(token::DocComment(_)) => self.span_fatal_help(self.last_span,
528+
Err(if self.last_token_kind == LastTokenKind::DocComment {
529+
self.span_fatal_help(self.last_span,
526530
"found a documentation comment that doesn't document anything",
527531
"doc comments must come before what they document, maybe a comment was \
528-
intended with `//`?"),
529-
_ => {
532+
intended with `//`?")
533+
} else {
530534
let mut err = self.fatal(&format!("expected identifier, found `{}`",
531535
self.this_token_to_string()));
532536
if self.token == token::Underscore {
533537
err.note("`_` is a wildcard pattern, not an identifier");
534538
}
535539
err
536-
}
537-
})
540+
})
538541
}
539542
}
540543
}
@@ -923,26 +926,22 @@ impl<'a> Parser<'a> {
923926

924927
/// Advance the parser by one token
925928
pub fn bump(&mut self) {
926-
if self.last_token_eof {
929+
if self.last_token_kind == LastTokenKind::Eof {
927930
// Bumping after EOF is a bad sign, usually an infinite loop.
928931
self.bug("attempted to bump the parser past EOF (may be stuck in a loop)");
929932
}
930933

931-
if self.token == token::Eof {
932-
self.last_token_eof = true;
933-
}
934-
935934
self.last_span = self.span;
936-
// Stash token for error recovery (sometimes; clone is not necessarily cheap).
937-
self.last_token = if self.token.is_ident() ||
938-
self.token.is_path() ||
939-
self.token.is_doc_comment() ||
940-
self.token == token::Comma {
941-
Some(Box::new(self.token.clone()))
942-
} else {
943-
None
935+
936+
// Record last token kind for possible error recovery.
937+
self.last_token_kind = match self.token {
938+
token::DocComment(..) => LastTokenKind::DocComment,
939+
token::Comma => LastTokenKind::Comma,
940+
token::Interpolated(..) => LastTokenKind::Interpolated,
941+
token::Eof => LastTokenKind::Eof,
942+
_ => LastTokenKind::Other,
944943
};
945-
self.last_token_interpolated = self.token.is_interpolated();
944+
946945
let next = if self.buffer_start == self.buffer_end {
947946
self.reader.real_token()
948947
} else {
@@ -979,11 +978,10 @@ impl<'a> Parser<'a> {
979978
lo: BytePos,
980979
hi: BytePos) {
981980
self.last_span = mk_sp(self.span.lo, lo);
982-
// It would be incorrect to just stash current token, but fortunately
983-
// for tokens currently using `bump_with`, last_token will be of no
984-
// use anyway.
985-
self.last_token = None;
986-
self.last_token_interpolated = false;
981+
// It would be incorrect to record the kind of the current token, but
982+
// fortunately for tokens currently using `bump_with`, the
983+
// last_token_kind will be of no use anyway.
984+
self.last_token_kind = LastTokenKind::Other;
987985
self.span = mk_sp(lo, hi);
988986
self.token = next;
989987
self.expected_tokens.clear();
@@ -2974,7 +2972,7 @@ impl<'a> Parser<'a> {
29742972
self.expected_tokens.push(TokenType::Operator);
29752973
while let Some(op) = AssocOp::from_token(&self.token) {
29762974

2977-
let lhs_span = if self.last_token_interpolated {
2975+
let lhs_span = if self.last_token_kind == LastTokenKind::Interpolated {
29782976
self.last_span
29792977
} else {
29802978
lhs.span
@@ -4036,13 +4034,13 @@ impl<'a> Parser<'a> {
40364034
None => {
40374035
let unused_attrs = |attrs: &[_], s: &mut Self| {
40384036
if attrs.len() > 0 {
4039-
let last_token = s.last_token.clone().map(|t| *t);
4040-
match last_token {
4041-
Some(token::DocComment(_)) => s.span_err_help(s.last_span,
4037+
if s.last_token_kind == LastTokenKind::DocComment {
4038+
s.span_err_help(s.last_span,
40424039
"found a documentation comment that doesn't document anything",
40434040
"doc comments must come before what they document, maybe a \
4044-
comment was intended with `//`?"),
4045-
_ => s.span_err(s.span, "expected statement after outer attribute"),
4041+
comment was intended with `//`?");
4042+
} else {
4043+
s.span_err(s.span, "expected statement after outer attribute");
40464044
}
40474045
}
40484046
};
@@ -4332,9 +4330,7 @@ impl<'a> Parser<'a> {
43324330

43334331
let missing_comma = !lifetimes.is_empty() &&
43344332
!self.token.is_like_gt() &&
4335-
self.last_token
4336-
.as_ref().map_or(true,
4337-
|x| &**x != &token::Comma);
4333+
self.last_token_kind != LastTokenKind::Comma;
43384334

43394335
if missing_comma {
43404336

0 commit comments

Comments
 (0)