2 use rustc_ast
::token
::{self, Nonterminal, NonterminalKind, Token}
;
3 use rustc_ast_pretty
::pprust
;
4 use rustc_errors
::PResult
;
5 use rustc_span
::symbol
::{kw, Ident}
;
7 use crate::parser
::pat
::{GateOr, RecoverComma}
;
8 use crate::parser
::{FollowedByType, ForceCollect, Parser, PathStyle}
;
11 /// Checks whether a non-terminal may begin with a particular token.
13 /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
14 /// token. Be conservative (return true) if not sure.
15 pub fn nonterminal_may_begin_with(kind
: NonterminalKind
, token
: &Token
) -> bool
{
16 /// Checks whether the non-terminal may contain a single (non-keyword) identifier.
17 fn may_be_ident(nt
: &token
::Nonterminal
) -> bool
{
19 token
::NtItem(_
) | token
::NtBlock(_
) | token
::NtVis(_
) | token
::NtLifetime(_
) => {
27 NonterminalKind
::Expr
=> {
28 token
.can_begin_expr()
29 // This exception is here for backwards compatibility.
30 && !token
.is_keyword(kw
::Let
)
31 // This exception is here for backwards compatibility.
32 && !token
.is_keyword(kw
::Const
)
34 NonterminalKind
::Ty
=> token
.can_begin_type(),
35 NonterminalKind
::Ident
=> get_macro_ident(token
).is_some(),
36 NonterminalKind
::Literal
=> token
.can_begin_literal_maybe_minus(),
37 NonterminalKind
::Vis
=> match token
.kind
{
38 // The follow-set of :vis + "priv" keyword + interpolated
39 token
::Comma
| token
::Ident(..) | token
::Interpolated(..) => true,
40 _
=> token
.can_begin_type(),
42 NonterminalKind
::Block
=> match token
.kind
{
43 token
::OpenDelim(token
::Brace
) => true,
44 token
::Interpolated(ref nt
) => !matches
!(
56 NonterminalKind
::Path
| NonterminalKind
::Meta
=> match token
.kind
{
57 token
::ModSep
| token
::Ident(..) => true,
58 token
::Interpolated(ref nt
) => match **nt
{
59 token
::NtPath(_
) | token
::NtMeta(_
) => true,
60 _
=> may_be_ident(&nt
),
64 NonterminalKind
::Pat2018 { .. }
| NonterminalKind
::Pat2021 { .. }
=> match token
.kind
{
65 token
::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
66 token
::OpenDelim(token
::Paren
) | // tuple pattern
67 token
::OpenDelim(token
::Bracket
) | // slice pattern
68 token
::BinOp(token
::And
) | // reference
69 token
::BinOp(token
::Minus
) | // negative literal
70 token
::AndAnd
| // double reference
71 token
::Literal(..) | // literal
72 token
::DotDot
| // range pattern (future compat)
73 token
::DotDotDot
| // range pattern (future compat)
74 token
::ModSep
| // path
75 token
::Lt
| // path (UFCS constant)
76 token
::BinOp(token
::Shl
) => true, // path (double UFCS)
77 // leading vert `|` or-pattern
78 token
::BinOp(token
::Or
) => matches
!(kind
, NonterminalKind
::Pat2021 {..}
),
79 token
::Interpolated(ref nt
) => may_be_ident(nt
),
82 NonterminalKind
::Lifetime
=> match token
.kind
{
83 token
::Lifetime(_
) => true,
84 token
::Interpolated(ref nt
) => {
85 matches
!(**nt
, token
::NtLifetime(_
) | token
::NtTT(_
))
89 NonterminalKind
::TT
| NonterminalKind
::Item
| NonterminalKind
::Stmt
=> {
90 !matches
!(token
.kind
, token
::CloseDelim(_
))
95 /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`).
96 pub fn parse_nonterminal(&mut self, kind
: NonterminalKind
) -> PResult
<'a
, Nonterminal
> {
97 // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
98 // needs to have them force-captured here.
99 // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
100 // which requires having captured tokens available. Since we cannot determine
101 // in advance whether or not a proc-macro will be (transitively) invoked,
102 // we always capture tokens for any `Nonterminal` which needs them.
104 NonterminalKind
::Item
=> match self.parse_item(ForceCollect
::Yes
)?
{
105 Some(item
) => token
::NtItem(item
),
107 return Err(self.struct_span_err(self.token
.span
, "expected an item keyword"));
110 NonterminalKind
::Block
=> {
111 // While a block *expression* may have attributes (e.g. `#[my_attr] { ... }`),
112 // the ':block' matcher does not support them
113 token
::NtBlock(self.collect_tokens_no_attrs(|this
| this
.parse_block())?
)
115 NonterminalKind
::Stmt
=> match self.parse_stmt(ForceCollect
::Yes
)?
{
116 Some(s
) => token
::NtStmt(s
),
118 return Err(self.struct_span_err(self.token
.span
, "expected a statement"));
121 NonterminalKind
::Pat2018 { .. }
| NonterminalKind
::Pat2021 { .. }
=> {
122 token
::NtPat(self.collect_tokens_no_attrs(|this
| match kind
{
123 NonterminalKind
::Pat2018 { .. }
=> this
.parse_pat_no_top_alt(None
),
124 NonterminalKind
::Pat2021 { .. }
=> {
125 this
.parse_pat_allow_top_alt(None
, GateOr
::Yes
, RecoverComma
::No
)
131 // If there are attributes present, then `parse_expr` will end up collecting tokens,
132 // turning the outer `collect_tokens_no_attrs` into a no-op due to the already present
133 // tokens. If there are *not* attributes present, then the outer
134 // `collect_tokens_no_attrs` will ensure that we will end up collecting tokens for the
137 // This is less efficient than it could be, since the outer `collect_tokens_no_attrs`
138 // still needs to snapshot the `TokenCursor` before calling `parse_expr`, even when
139 // `parse_expr` will end up collecting tokens. Ideally, this would work more like
140 // `parse_item`, and take in a `ForceCollect` parameter. However, this would require
141 // adding a `ForceCollect` parameter in a bunch of places in expression parsing
142 // for little gain. If the perf impact from this turns out to be noticeable, we should
143 // revisit this apporach.
144 NonterminalKind
::Expr
=> {
145 token
::NtExpr(self.collect_tokens_no_attrs(|this
| this
.parse_expr())?
)
147 NonterminalKind
::Literal
=> {
148 // The `:literal` matcher does not support attributes
150 self.collect_tokens_no_attrs(|this
| this
.parse_literal_maybe_minus())?
,
154 NonterminalKind
::Ty
=> {
155 token
::NtTy(self.collect_tokens_no_attrs(|this
| this
.parse_ty())?
)
157 // this could be handled like a token, since it is one
158 NonterminalKind
::Ident
=> {
159 if let Some((ident
, is_raw
)) = get_macro_ident(&self.token
) {
161 token
::NtIdent(ident
, is_raw
)
163 let token_str
= pprust
::token_to_string(&self.token
);
164 let msg
= &format
!("expected ident, found {}", &token_str
);
165 return Err(self.struct_span_err(self.token
.span
, msg
));
168 NonterminalKind
::Path
=> token
::NtPath(
169 self.collect_tokens_no_attrs(|this
| this
.parse_path(PathStyle
::Type
))?
,
171 NonterminalKind
::Meta
=> {
172 token
::NtMeta(P(self.collect_tokens_no_attrs(|this
| this
.parse_attr_item(false))?
))
174 NonterminalKind
::TT
=> token
::NtTT(self.parse_token_tree()),
175 NonterminalKind
::Vis
=> token
::NtVis(
176 self.collect_tokens_no_attrs(|this
| this
.parse_visibility(FollowedByType
::Yes
))?
,
178 NonterminalKind
::Lifetime
=> {
179 if self.check_lifetime() {
180 token
::NtLifetime(self.expect_lifetime().ident
)
182 let token_str
= pprust
::token_to_string(&self.token
);
183 let msg
= &format
!("expected a lifetime, found `{}`", &token_str
);
184 return Err(self.struct_span_err(self.token
.span
, msg
));
191 /// The token is an identifier, but not `_`.
192 /// We prohibit passing `_` to macros expecting `ident` for now.
193 fn get_macro_ident(token
: &Token
) -> Option
<(Ident
, bool
)> {
194 token
.ident().filter(|(ident
, _
)| ident
.name
!= kw
::Underscore
)