rustc_parse/parser/
nonterminal.rs
1use std::sync::Arc;
2
3use rustc_ast::HasTokens;
4use rustc_ast::ptr::P;
5use rustc_ast::token::Nonterminal::*;
6use rustc_ast::token::NtExprKind::*;
7use rustc_ast::token::NtPatKind::*;
8use rustc_ast::token::{
9 self, Delimiter, InvisibleOrigin, MetaVarKind, Nonterminal, NonterminalKind, Token,
10};
11use rustc_ast_pretty::pprust;
12use rustc_errors::PResult;
13use rustc_span::{Ident, kw};
14
15use crate::errors::UnexpectedNonterminal;
16use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
17use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle};
18
19impl<'a> Parser<'a> {
20 #[inline]
26 pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
27 fn may_be_ident(kind: MetaVarKind) -> bool {
29 match kind {
30 MetaVarKind::Stmt
31 | MetaVarKind::Pat(_)
32 | MetaVarKind::Expr { .. }
33 | MetaVarKind::Ty
34 | MetaVarKind::Literal | MetaVarKind::Meta
36 | MetaVarKind::Path => true,
37
38 MetaVarKind::Item
39 | MetaVarKind::Block
40 | MetaVarKind::Vis => false,
41
42 MetaVarKind::Ident
43 | MetaVarKind::Lifetime
44 | MetaVarKind::TT => unreachable!(),
45 }
46 }
47
48 fn nt_may_be_ident(nt: &Nonterminal) -> bool {
50 match nt {
51 NtStmt(_)
52 | NtPat(_)
53 | NtExpr(_)
54 | NtTy(_)
55 | NtLiteral(_) | NtMeta(_)
57 | NtPath(_) => true,
58
59 NtItem(_)
60 | NtBlock(_)
61 | NtVis(_) => false,
62 }
63 }
64
65 match kind {
66 NonterminalKind::Expr(Expr2021 { .. }) => {
68 token.can_begin_expr()
69 && !token.is_keyword(kw::Let)
71 && !token.is_keyword(kw::Const)
73 }
74 NonterminalKind::Expr(Expr) => {
76 (token.can_begin_expr() || token.is_keyword(kw::Underscore))
84 && !token.is_keyword(kw::Let)
86 }
87 NonterminalKind::Ty => token.can_begin_type(),
88 NonterminalKind::Ident => get_macro_ident(token).is_some(),
89 NonterminalKind::Literal => token.can_begin_literal_maybe_minus(),
90 NonterminalKind::Vis => match token.kind {
91 token::Comma
93 | token::Ident(..)
94 | token::NtIdent(..)
95 | token::NtLifetime(..)
96 | token::Interpolated(_)
97 | token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => true,
98 _ => token.can_begin_type(),
99 },
100 NonterminalKind::Block => match &token.kind {
101 token::OpenDelim(Delimiter::Brace) => true,
102 token::NtLifetime(..) => true,
103 token::Interpolated(nt) => match &**nt {
104 NtBlock(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true,
105 NtItem(_) | NtPat(_) | NtTy(_) | NtMeta(_) | NtPath(_) | NtVis(_) => false,
106 },
107 token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k {
108 MetaVarKind::Block
109 | MetaVarKind::Stmt
110 | MetaVarKind::Expr { .. }
111 | MetaVarKind::Literal => true,
112 MetaVarKind::Item
113 | MetaVarKind::Pat(_)
114 | MetaVarKind::Ty
115 | MetaVarKind::Meta
116 | MetaVarKind::Path
117 | MetaVarKind::Vis => false,
118 MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => {
119 unreachable!()
120 }
121 },
122 _ => false,
123 },
124 NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
125 token::PathSep | token::Ident(..) | token::NtIdent(..) => true,
126 token::Interpolated(nt) => nt_may_be_ident(nt),
127 token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => {
128 may_be_ident(*kind)
129 }
130 _ => false,
131 },
132 NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
133 NonterminalKind::Lifetime => match &token.kind {
134 token::Lifetime(..) | token::NtLifetime(..) => true,
135 _ => false,
136 },
137 NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
138 !matches!(token.kind, token::CloseDelim(_))
139 }
140 }
141 }
142
143 #[inline]
146 pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> {
147 let mut nt = match kind {
152 NonterminalKind::TT => return Ok(ParseNtResult::Tt(self.parse_token_tree())),
154 NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
155 Some(item) => NtItem(item),
156 None => {
157 return Err(self
158 .dcx()
159 .create_err(UnexpectedNonterminal::Item(self.token.span)));
160 }
161 },
162 NonterminalKind::Block => {
163 NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?)
166 }
167 NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
168 Some(s) => NtStmt(P(s)),
169 None => {
170 return Err(self
171 .dcx()
172 .create_err(UnexpectedNonterminal::Statement(self.token.span)));
173 }
174 },
175 NonterminalKind::Pat(pat_kind) => {
176 NtPat(self.collect_tokens_no_attrs(|this| match pat_kind {
177 PatParam { .. } => this.parse_pat_no_top_alt(None, None),
178 PatWithOr => this.parse_pat_no_top_guard(
179 None,
180 RecoverComma::No,
181 RecoverColon::No,
182 CommaRecoveryMode::EitherTupleOrPipe,
183 ),
184 })?)
185 }
186 NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?),
187 NonterminalKind::Literal => {
188 NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?)
190 }
191 NonterminalKind::Ty => {
192 NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?)
193 }
194 NonterminalKind::Ident => {
196 return if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
197 self.bump();
198 Ok(ParseNtResult::Ident(ident, is_raw))
199 } else {
200 Err(self.dcx().create_err(UnexpectedNonterminal::Ident {
201 span: self.token.span,
202 token: self.token.clone(),
203 }))
204 };
205 }
206 NonterminalKind::Path => {
207 NtPath(P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?))
208 }
209 NonterminalKind::Meta => NtMeta(P(self.parse_attr_item(ForceCollect::Yes)?)),
210 NonterminalKind::Vis => {
211 NtVis(P(self
212 .collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?))
213 }
214 NonterminalKind::Lifetime => {
215 return if let Some((ident, is_raw)) = self.token.lifetime() {
218 self.bump();
219 Ok(ParseNtResult::Lifetime(ident, is_raw))
220 } else {
221 Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
222 span: self.token.span,
223 token: self.token.clone(),
224 }))
225 };
226 }
227 };
228
229 if matches!(nt.tokens_mut(), Some(None)) {
231 panic!(
232 "Missing tokens for nt {:?} at {:?}: {:?}",
233 nt,
234 nt.use_span(),
235 pprust::nonterminal_to_string(&nt)
236 );
237 }
238
239 Ok(ParseNtResult::Nt(Arc::new(nt)))
240 }
241}
242
243fn get_macro_ident(token: &Token) -> Option<(Ident, token::IdentIsRaw)> {
246 token.ident().filter(|(ident, _)| ident.name != kw::Underscore)
247}