1
Fork 0

Only match a fragment specifier the if it starts with certain tokens.

Fixes #24189.
Fixes #26444.
Fixes #27832.
Fixes #34030.
Fixes #35650.
Fixes #39964.
Fixes the 4th comment in #40569.
Fixes the issue blocking #40984.
This commit is contained in:
kennytm 2017-06-26 22:23:59 +08:00
parent b80b659d67
commit 600800480a
No known key found for this signature in database
GPG key ID: FEF6C8051D0E013C
4 changed files with 317 additions and 6 deletions

View file

@ -386,12 +386,11 @@ fn inner_parse_loop(sess: &ParseSess,
return Error(span, "missing fragment specifier".to_string());
}
}
TokenTree::MetaVarDecl(..) => {
TokenTree::MetaVarDecl(_, _, id) => {
// Built-in nonterminals never start with these tokens,
// so we can eliminate them from consideration.
match *token {
token::CloseDelim(_) => {},
_ => bb_eis.push(ei),
if may_begin_with(&*id.name.as_str(), token) {
bb_eis.push(ei);
}
}
seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..)) => {
@ -493,6 +492,73 @@ pub fn parse(sess: &ParseSess,
}
}
/// Checks whether a non-terminal may begin with a particular token.
///
/// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
/// token. Be conservative (return true) if not sure.
fn may_begin_with(name: &str, token: &Token) -> bool {
/// Checks whether the non-terminal may contain a single (non-keyword) identifier.
fn may_be_ident(nt: &token::Nonterminal) -> bool {
match *nt {
token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) => false,
_ => true,
}
}
match name {
"expr" => token.can_begin_expr(),
"ty" => token.can_begin_type(),
"ident" => token.is_ident(),
"vis" => match *token { // The follow-set of :vis + "priv" keyword + interpolated
Token::Comma | Token::Ident(_) | Token::Interpolated(_) => true,
_ => token.can_begin_type(),
},
"block" => match *token {
Token::OpenDelim(token::Brace) => true,
Token::Interpolated(ref nt) => match nt.0 {
token::NtItem(_) |
token::NtPat(_) |
token::NtTy(_) |
token::NtIdent(_) |
token::NtMeta(_) |
token::NtPath(_) |
token::NtVis(_) => false, // none of these may start with '{'.
_ => true,
},
_ => false,
},
"path" | "meta" => match *token {
Token::ModSep | Token::Ident(_) => true,
Token::Interpolated(ref nt) => match nt.0 {
token::NtPath(_) | token::NtMeta(_) => true,
_ => may_be_ident(&nt.0),
},
_ => false,
},
"pat" => match *token {
Token::Ident(_) | // box, ref, mut, and other identifiers (can stricten)
Token::OpenDelim(token::Paren) | // tuple pattern
Token::OpenDelim(token::Bracket) | // slice pattern
Token::BinOp(token::And) | // reference
Token::BinOp(token::Minus) | // negative literal
Token::AndAnd | // double reference
Token::Literal(..) | // literal
Token::DotDot | // range pattern (future compat)
Token::DotDotDot | // range pattern (future compat)
Token::ModSep | // path
Token::Lt | // path (UFCS constant)
Token::BinOp(token::Shl) | // path (double UFCS)
Token::Underscore => true, // placeholder
Token::Interpolated(ref nt) => may_be_ident(&nt.0),
_ => false,
},
_ => match *token {
token::CloseDelim(_) => false,
_ => true,
},
}
}
fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
if name == "tt" {
return token::NtTT(p.parse_token_tree());