Skip to content

Commit f39039e

Browse files
authored
Auto merge of #36527 - nnethercote:last_token_kind, r=jseyfried
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%.
2 parents 0b03ba1 + 8075d54 commit f39039e

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
}
@@ -925,26 +928,22 @@ impl<'a> Parser<'a> {
925928

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

933-
if self.token == token::Eof {
934-
self.last_token_eof = true;
935-
}
936-
937936
self.last_span = self.span;
938-
// Stash token for error recovery (sometimes; clone is not necessarily cheap).
939-
self.last_token = if self.token.is_ident() ||
940-
self.token.is_path() ||
941-
self.token.is_doc_comment() ||
942-
self.token == token::Comma {
943-
Some(Box::new(self.token.clone()))
944-
} else {
945-
None
937+
938+
// Record last token kind for possible error recovery.
939+
self.last_token_kind = match self.token {
940+
token::DocComment(..) => LastTokenKind::DocComment,
941+
token::Comma => LastTokenKind::Comma,
942+
token::Interpolated(..) => LastTokenKind::Interpolated,
943+
token::Eof => LastTokenKind::Eof,
944+
_ => LastTokenKind::Other,
946945
};
947-
self.last_token_interpolated = self.token.is_interpolated();
946+
948947
let next = if self.buffer_start == self.buffer_end {
949948
self.reader.real_token()
950949
} else {
@@ -981,11 +980,10 @@ impl<'a> Parser<'a> {
981980
lo: BytePos,
982981
hi: BytePos) {
983982
self.last_span = mk_sp(self.span.lo, lo);
984-
// It would be incorrect to just stash current token, but fortunately
985-
// for tokens currently using `bump_with`, last_token will be of no
986-
// use anyway.
987-
self.last_token = None;
988-
self.last_token_interpolated = false;
983+
// It would be incorrect to record the kind of the current token, but
984+
// fortunately for tokens currently using `bump_with`, the
985+
// last_token_kind will be of no use anyway.
986+
self.last_token_kind = LastTokenKind::Other;
989987
self.span = mk_sp(lo, hi);
990988
self.token = next;
991989
self.expected_tokens.clear();
@@ -2950,7 +2948,7 @@ impl<'a> Parser<'a> {
29502948
self.expected_tokens.push(TokenType::Operator);
29512949
while let Some(op) = AssocOp::from_token(&self.token) {
29522950

2953-
let lhs_span = if self.last_token_interpolated {
2951+
let lhs_span = if self.last_token_kind == LastTokenKind::Interpolated {
29542952
self.last_span
29552953
} else {
29562954
lhs.span
@@ -4012,13 +4010,13 @@ impl<'a> Parser<'a> {
40124010
None => {
40134011
let unused_attrs = |attrs: &[_], s: &mut Self| {
40144012
if attrs.len() > 0 {
4015-
let last_token = s.last_token.clone().map(|t| *t);
4016-
match last_token {
4017-
Some(token::DocComment(_)) => s.span_err_help(s.last_span,
4013+
if s.last_token_kind == LastTokenKind::DocComment {
4014+
s.span_err_help(s.last_span,
40184015
"found a documentation comment that doesn't document anything",
40194016
"doc comments must come before what they document, maybe a \
4020-
comment was intended with `//`?"),
4021-
_ => s.span_err(s.span, "expected statement after outer attribute"),
4017+
comment was intended with `//`?");
4018+
} else {
4019+
s.span_err(s.span, "expected statement after outer attribute");
40224020
}
40234021
}
40244022
};
@@ -4308,9 +4306,7 @@ impl<'a> Parser<'a> {
43084306

43094307
let missing_comma = !lifetimes.is_empty() &&
43104308
!self.token.is_like_gt() &&
4311-
self.last_token
4312-
.as_ref().map_or(true,
4313-
|x| &**x != &token::Comma);
4309+
self.last_token_kind != LastTokenKind::Comma;
43144310

43154311
if missing_comma {
43164312

0 commit comments

Comments
 (0)