rustc_parse/parser/
nonterminal.rs1use rustc_ast::token::NtExprKind::*;
2use rustc_ast::token::NtPatKind::*;
3use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, NonterminalKind, Token};
4use rustc_ast_pretty::pprust;
5use rustc_errors::PResult;
6use rustc_span::{Ident, kw};
7
8use crate::errors::UnexpectedNonterminal;
9use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
10use crate::parser::{
11 AllowConstBlockItems, FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle,
12};
13
14impl<'a> Parser<'a> {
15 #[inline]
21 pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
22 fn may_be_ident(kind: MetaVarKind) -> bool {
24 match kind {
25 MetaVarKind::Stmt
26 | MetaVarKind::Pat(_)
27 | MetaVarKind::Expr { .. }
28 | MetaVarKind::Ty { .. }
29 | MetaVarKind::Literal | MetaVarKind::Meta { .. }
31 | MetaVarKind::Path => true,
32
33 MetaVarKind::Item
34 | MetaVarKind::Block
35 | MetaVarKind::Vis
36 | MetaVarKind::Guard => false,
37
38 MetaVarKind::Ident
39 | MetaVarKind::Lifetime
40 | MetaVarKind::TT => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
41 }
42 }
43
44 match kind {
45 NonterminalKind::Expr(Expr2021 { .. }) => {
47 token.can_begin_expr()
48 && !token.is_keyword(kw::Let)
50 && !token.is_keyword(kw::Const)
52 }
53 NonterminalKind::Expr(Expr) => {
55 (token.can_begin_expr() || token.is_keyword(kw::Underscore))
63 && !token.is_keyword(kw::Let)
65 }
66 NonterminalKind::Ty => token.can_begin_type(),
67 NonterminalKind::Ident => get_macro_ident(token).is_some(),
68 NonterminalKind::Literal => token.can_begin_literal_maybe_minus(),
69 NonterminalKind::Vis => match token.kind {
70 token::Comma
72 | token::Ident(..)
73 | token::NtIdent(..)
74 | token::NtLifetime(..)
75 | token::OpenInvisible(InvisibleOrigin::MetaVar(_)) => true,
76 _ => token.can_begin_type(),
77 },
78 NonterminalKind::Block => match &token.kind {
79 token::OpenBrace => true,
80 token::NtLifetime(..) => true,
81 token::OpenInvisible(InvisibleOrigin::MetaVar(k)) => match k {
82 MetaVarKind::Block
83 | MetaVarKind::Stmt
84 | MetaVarKind::Expr { .. }
85 | MetaVarKind::Literal => true,
86 MetaVarKind::Item
87 | MetaVarKind::Pat(_)
88 | MetaVarKind::Ty { .. }
89 | MetaVarKind::Meta { .. }
90 | MetaVarKind::Path
91 | MetaVarKind::Vis
92 | MetaVarKind::Guard => false,
93 MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => {
94 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
95 }
96 },
97 _ => false,
98 },
99 NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
100 token::PathSep | token::Ident(..) | token::NtIdent(..) => true,
101 token::OpenInvisible(InvisibleOrigin::MetaVar(kind)) => may_be_ident(*kind),
102 _ => false,
103 },
104 NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
105 NonterminalKind::Lifetime => match &token.kind {
106 token::Lifetime(..) | token::NtLifetime(..) => true,
107 _ => false,
108 },
109 NonterminalKind::Guard => match token.kind {
110 token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Guard)) => true,
111 _ => token.is_keyword(kw::If),
112 },
113 NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
114 token.kind.close_delim().is_none()
115 }
116 }
117 }
118
119 #[inline]
122 pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> {
123 match kind {
128 NonterminalKind::TT => Ok(ParseNtResult::Tt(self.parse_token_tree())),
130 NonterminalKind::Item => match self
131 .parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?
132 {
133 Some(item) => Ok(ParseNtResult::Item(item)),
134 None => Err(self.dcx().create_err(UnexpectedNonterminal::Item(self.token.span))),
135 },
136 NonterminalKind::Block => {
137 Ok(ParseNtResult::Block(self.collect_tokens_no_attrs(|this| this.parse_block())?))
140 }
141 NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
142 Some(stmt) => Ok(ParseNtResult::Stmt(Box::new(stmt))),
143 None => {
144 Err(self.dcx().create_err(UnexpectedNonterminal::Statement(self.token.span)))
145 }
146 },
147 NonterminalKind::Pat(pat_kind) => Ok(ParseNtResult::Pat(
148 self.collect_tokens_no_attrs(|this| match pat_kind {
149 PatParam { .. } => this.parse_pat_no_top_alt(None, None),
150 PatWithOr => this.parse_pat_no_top_guard(
151 None,
152 RecoverComma::No,
153 RecoverColon::No,
154 CommaRecoveryMode::EitherTupleOrPipe,
155 ),
156 })
157 .map(Box::new)?,
158 pat_kind,
159 )),
160 NonterminalKind::Expr(expr_kind) => {
161 Ok(ParseNtResult::Expr(self.parse_expr_force_collect()?, expr_kind))
162 }
163 NonterminalKind::Literal => {
164 Ok(ParseNtResult::Literal(
166 self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?,
167 ))
168 }
169 NonterminalKind::Ty => Ok(ParseNtResult::Ty(
170 self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?,
171 )),
172 NonterminalKind::Ident => {
174 if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
175 self.bump();
176 Ok(ParseNtResult::Ident(ident, is_raw))
177 } else {
178 Err(self.dcx().create_err(UnexpectedNonterminal::Ident {
179 span: self.token.span,
180 token: pprust::token_to_string(&self.token),
181 }))
182 }
183 }
184 NonterminalKind::Path => Ok(ParseNtResult::Path(Box::new(
185 self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?,
186 ))),
187 NonterminalKind::Meta => {
188 Ok(ParseNtResult::Meta(Box::new(self.parse_attr_item(ForceCollect::Yes)?)))
189 }
190 NonterminalKind::Vis => Ok(ParseNtResult::Vis(Box::new(
191 self.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?,
192 ))),
193 NonterminalKind::Lifetime => {
194 if let Some((ident, is_raw)) = self.token.lifetime() {
197 self.bump();
198 Ok(ParseNtResult::Lifetime(ident, is_raw))
199 } else {
200 Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
201 span: self.token.span,
202 token: pprust::token_to_string(&self.token),
203 }))
204 }
205 }
206 NonterminalKind::Guard => {
207 Ok(ParseNtResult::Guard(self.expect_match_arm_guard(ForceCollect::Yes)?))
208 }
209 }
210 }
211}
212
213fn get_macro_ident(token: &Token) -> Option<(Ident, token::IdentIsRaw)> {
216 token.ident().filter(|(ident, _)| ident.name != kw::Underscore)
217}