rustc_parse/parser/
nonterminal.rs

1use rustc_ast::ptr::P;
2use rustc_ast::token::NtExprKind::*;
3use rustc_ast::token::NtPatKind::*;
4use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, NonterminalKind, Token};
5use rustc_errors::PResult;
6use rustc_span::{Ident, kw};
7
8use crate::errors::UnexpectedNonterminal;
9use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
10use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle};
11
12impl<'a> Parser<'a> {
13    /// Checks whether a non-terminal may begin with a particular token.
14    ///
15    /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with
16    /// that token. Be conservative (return true) if not sure. Inlined because it has a single call
17    /// site.
18    #[inline]
19    pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
20        /// Checks whether the non-terminal may contain a single (non-keyword) identifier.
21        fn may_be_ident(kind: MetaVarKind) -> bool {
22            match kind {
23                MetaVarKind::Stmt
24                | MetaVarKind::Pat(_)
25                | MetaVarKind::Expr { .. }
26                | MetaVarKind::Ty { .. }
27                | MetaVarKind::Literal // `true`, `false`
28                | MetaVarKind::Meta { .. }
29                | MetaVarKind::Path => true,
30
31                MetaVarKind::Item
32                | MetaVarKind::Block
33                | MetaVarKind::Vis => false,
34
35                MetaVarKind::Ident
36                | MetaVarKind::Lifetime
37                | MetaVarKind::TT => unreachable!(),
38            }
39        }
40
41        match kind {
42            // `expr_2021` and earlier
43            NonterminalKind::Expr(Expr2021 { .. }) => {
44                token.can_begin_expr()
45                // This exception is here for backwards compatibility.
46                && !token.is_keyword(kw::Let)
47                // This exception is here for backwards compatibility.
48                && !token.is_keyword(kw::Const)
49            }
50            // Current edition expressions
51            NonterminalKind::Expr(Expr) => {
52                // In Edition 2024, `_` is considered an expression, so we
53                // need to allow it here because `token.can_begin_expr()` does
54                // not consider `_` to be an expression.
55                //
56                // Because `can_begin_expr` is used elsewhere, we need to reduce
57                // the scope of where the `_` is considered an expression to
58                // just macro parsing code.
59                (token.can_begin_expr() || token.is_keyword(kw::Underscore))
60                // This exception is here for backwards compatibility.
61                && !token.is_keyword(kw::Let)
62            }
63            NonterminalKind::Ty => token.can_begin_type(),
64            NonterminalKind::Ident => get_macro_ident(token).is_some(),
65            NonterminalKind::Literal => token.can_begin_literal_maybe_minus(),
66            NonterminalKind::Vis => match token.kind {
67                // The follow-set of :vis + "priv" keyword + interpolated/metavar-expansion.
68                token::Comma
69                | token::Ident(..)
70                | token::NtIdent(..)
71                | token::NtLifetime(..)
72                | token::OpenInvisible(InvisibleOrigin::MetaVar(_)) => true,
73                _ => token.can_begin_type(),
74            },
75            NonterminalKind::Block => match &token.kind {
76                token::OpenBrace => true,
77                token::NtLifetime(..) => true,
78                token::OpenInvisible(InvisibleOrigin::MetaVar(k)) => match k {
79                    MetaVarKind::Block
80                    | MetaVarKind::Stmt
81                    | MetaVarKind::Expr { .. }
82                    | MetaVarKind::Literal => true,
83                    MetaVarKind::Item
84                    | MetaVarKind::Pat(_)
85                    | MetaVarKind::Ty { .. }
86                    | MetaVarKind::Meta { .. }
87                    | MetaVarKind::Path
88                    | MetaVarKind::Vis => false,
89                    MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => {
90                        unreachable!()
91                    }
92                },
93                _ => false,
94            },
95            NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
96                token::PathSep | token::Ident(..) | token::NtIdent(..) => true,
97                token::OpenInvisible(InvisibleOrigin::MetaVar(kind)) => may_be_ident(*kind),
98                _ => false,
99            },
100            NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
101            NonterminalKind::Lifetime => match &token.kind {
102                token::Lifetime(..) | token::NtLifetime(..) => true,
103                _ => false,
104            },
105            NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
106                token.kind.close_delim().is_none()
107            }
108        }
109    }
110
111    /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call
112    /// site.
113    #[inline]
114    pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> {
115        // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
116        // which requires having captured tokens available. Since we cannot determine
117        // in advance whether or not a proc-macro will be (transitively) invoked,
118        // we always capture tokens for any nonterminal that needs them.
119        match kind {
120            // Note that TT is treated differently to all the others.
121            NonterminalKind::TT => Ok(ParseNtResult::Tt(self.parse_token_tree())),
122            NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
123                Some(item) => Ok(ParseNtResult::Item(item)),
124                None => Err(self.dcx().create_err(UnexpectedNonterminal::Item(self.token.span))),
125            },
126            NonterminalKind::Block => {
127                // While a block *expression* may have attributes (e.g. `#[my_attr] { ... }`),
128                // the ':block' matcher does not support them
129                Ok(ParseNtResult::Block(self.collect_tokens_no_attrs(|this| this.parse_block())?))
130            }
131            NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
132                Some(stmt) => Ok(ParseNtResult::Stmt(P(stmt))),
133                None => {
134                    Err(self.dcx().create_err(UnexpectedNonterminal::Statement(self.token.span)))
135                }
136            },
137            NonterminalKind::Pat(pat_kind) => Ok(ParseNtResult::Pat(
138                self.collect_tokens_no_attrs(|this| match pat_kind {
139                    PatParam { .. } => this.parse_pat_no_top_alt(None, None),
140                    PatWithOr => this.parse_pat_no_top_guard(
141                        None,
142                        RecoverComma::No,
143                        RecoverColon::No,
144                        CommaRecoveryMode::EitherTupleOrPipe,
145                    ),
146                })?,
147                pat_kind,
148            )),
149            NonterminalKind::Expr(expr_kind) => {
150                Ok(ParseNtResult::Expr(self.parse_expr_force_collect()?, expr_kind))
151            }
152            NonterminalKind::Literal => {
153                // The `:literal` matcher does not support attributes.
154                Ok(ParseNtResult::Literal(
155                    self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?,
156                ))
157            }
158            NonterminalKind::Ty => Ok(ParseNtResult::Ty(
159                self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?,
160            )),
161            // This could be handled like a token, since it is one.
162            NonterminalKind::Ident => {
163                if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
164                    self.bump();
165                    Ok(ParseNtResult::Ident(ident, is_raw))
166                } else {
167                    Err(self.dcx().create_err(UnexpectedNonterminal::Ident {
168                        span: self.token.span,
169                        token: self.token,
170                    }))
171                }
172            }
173            NonterminalKind::Path => Ok(ParseNtResult::Path(P(
174                self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?
175            ))),
176            NonterminalKind::Meta => {
177                Ok(ParseNtResult::Meta(P(self.parse_attr_item(ForceCollect::Yes)?)))
178            }
179            NonterminalKind::Vis => {
180                Ok(ParseNtResult::Vis(P(self
181                    .collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?)))
182            }
183            NonterminalKind::Lifetime => {
184                // We want to keep `'keyword` parsing, just like `keyword` is still
185                // an ident for nonterminal purposes.
186                if let Some((ident, is_raw)) = self.token.lifetime() {
187                    self.bump();
188                    Ok(ParseNtResult::Lifetime(ident, is_raw))
189                } else {
190                    Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
191                        span: self.token.span,
192                        token: self.token,
193                    }))
194                }
195            }
196        }
197    }
198}
199
200/// The token is an identifier, but not `_`.
201/// We prohibit passing `_` to macros expecting `ident` for now.
202fn get_macro_ident(token: &Token) -> Option<(Ident, token::IdentIsRaw)> {
203    token.ident().filter(|(ident, _)| ident.name != kw::Underscore)
204}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy