2025-02-03 06:44:41 +03:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2022-05-01 20:58:24 +03:00
|
|
|
use rustc_ast::HasTokens;
|
2020-07-27 14:04:54 +02:00
|
|
|
use rustc_ast::ptr::P;
|
2022-05-01 20:58:24 +03:00
|
|
|
use rustc_ast::token::Nonterminal::*;
|
|
|
|
use rustc_ast::token::NtExprKind::*;
|
2024-06-23 08:13:56 +10:00
|
|
|
use rustc_ast::token::NtPatKind::*;
|
2024-04-17 09:37:00 +10:00
|
|
|
use rustc_ast::token::{
|
|
|
|
self, Delimiter, InvisibleOrigin, MetaVarKind, Nonterminal, NonterminalKind, Token,
|
|
|
|
};
|
2020-07-27 14:04:54 +02:00
|
|
|
use rustc_ast_pretty::pprust;
|
|
|
|
use rustc_errors::PResult;
|
2024-12-13 10:29:23 +11:00
|
|
|
use rustc_span::{Ident, kw};
|
2020-07-27 14:04:54 +02:00
|
|
|
|
2022-10-14 23:16:25 +02:00
|
|
|
use crate::errors::UnexpectedNonterminal;
|
2022-01-12 20:43:24 +00:00
|
|
|
use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
|
2023-08-03 16:43:05 +10:00
|
|
|
use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle};
|
2020-07-27 14:04:54 +02:00
|
|
|
|
|
|
|
impl<'a> Parser<'a> {
|
|
|
|
/// Checks whether a non-terminal may begin with a particular token.
|
|
|
|
///
|
2022-04-20 14:13:49 +10:00
|
|
|
/// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with
|
|
|
|
/// that token. Be conservative (return true) if not sure. Inlined because it has a single call
|
|
|
|
/// site.
|
|
|
|
#[inline]
|
2020-12-28 16:57:13 -06:00
|
|
|
pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
|
2020-07-27 14:04:54 +02:00
|
|
|
/// Checks whether the non-terminal may contain a single (non-keyword) identifier.
|
2024-04-17 09:37:00 +10:00
|
|
|
fn may_be_ident(kind: MetaVarKind) -> bool {
|
|
|
|
match kind {
|
|
|
|
MetaVarKind::Stmt
|
|
|
|
| MetaVarKind::Pat(_)
|
|
|
|
| MetaVarKind::Expr { .. }
|
2025-02-21 16:47:07 +11:00
|
|
|
| MetaVarKind::Ty { .. }
|
2024-04-17 09:37:00 +10:00
|
|
|
| MetaVarKind::Literal // `true`, `false`
|
2024-04-18 16:22:02 +10:00
|
|
|
| MetaVarKind::Meta { .. }
|
2024-04-17 09:37:00 +10:00
|
|
|
| MetaVarKind::Path => true,
|
|
|
|
|
|
|
|
MetaVarKind::Item
|
|
|
|
| MetaVarKind::Block
|
|
|
|
| MetaVarKind::Vis => false,
|
|
|
|
|
|
|
|
MetaVarKind::Ident
|
|
|
|
| MetaVarKind::Lifetime
|
|
|
|
| MetaVarKind::TT => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Old variant of `may_be_ident`. Being phased out.
|
|
|
|
fn nt_may_be_ident(nt: &Nonterminal) -> bool {
|
2023-08-11 09:25:16 +10:00
|
|
|
match nt {
|
|
|
|
NtStmt(_)
|
|
|
|
| NtExpr(_)
|
|
|
|
| NtLiteral(_) // `true`, `false`
|
|
|
|
| NtPath(_) => true,
|
|
|
|
|
2024-04-17 12:17:09 +10:00
|
|
|
NtItem(_) | NtBlock(_) => false,
|
2023-08-11 09:25:16 +10:00
|
|
|
}
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
match kind {
|
2024-05-31 16:47:30 +00:00
|
|
|
// `expr_2021` and earlier
|
2024-06-23 08:13:56 +10:00
|
|
|
NonterminalKind::Expr(Expr2021 { .. }) => {
|
2020-07-27 14:04:54 +02:00
|
|
|
token.can_begin_expr()
|
|
|
|
// This exception is here for backwards compatibility.
|
|
|
|
&& !token.is_keyword(kw::Let)
|
2020-12-17 13:51:20 -08:00
|
|
|
// This exception is here for backwards compatibility.
|
|
|
|
&& !token.is_keyword(kw::Const)
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
2024-05-31 16:47:30 +00:00
|
|
|
// Current edition expressions
|
2024-06-23 08:13:56 +10:00
|
|
|
NonterminalKind::Expr(Expr) => {
|
2024-07-31 15:37:55 -04:00
|
|
|
// In Edition 2024, `_` is considered an expression, so we
|
2024-05-31 16:47:30 +00:00
|
|
|
// need to allow it here because `token.can_begin_expr()` does
|
|
|
|
// not consider `_` to be an expression.
|
|
|
|
//
|
|
|
|
// Because `can_begin_expr` is used elsewhere, we need to reduce
|
|
|
|
// the scope of where the `_` is considered an expression to
|
|
|
|
// just macro parsing code.
|
|
|
|
(token.can_begin_expr() || token.is_keyword(kw::Underscore))
|
2024-04-16 20:47:10 +02:00
|
|
|
// This exception is here for backwards compatibility.
|
|
|
|
&& !token.is_keyword(kw::Let)
|
|
|
|
}
|
2020-07-27 14:04:54 +02:00
|
|
|
NonterminalKind::Ty => token.can_begin_type(),
|
|
|
|
NonterminalKind::Ident => get_macro_ident(token).is_some(),
|
|
|
|
NonterminalKind::Literal => token.can_begin_literal_maybe_minus(),
|
|
|
|
NonterminalKind::Vis => match token.kind {
|
2024-04-17 12:17:09 +10:00
|
|
|
// The follow-set of :vis + "priv" keyword + interpolated/metavar-expansion.
|
2024-04-22 19:46:51 +10:00
|
|
|
token::Comma
|
|
|
|
| token::Ident(..)
|
|
|
|
| token::NtIdent(..)
|
|
|
|
| token::NtLifetime(..)
|
2024-04-17 09:37:00 +10:00
|
|
|
| token::Interpolated(_)
|
|
|
|
| token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => true,
|
2020-07-27 14:04:54 +02:00
|
|
|
_ => token.can_begin_type(),
|
|
|
|
},
|
2022-11-22 09:42:01 +00:00
|
|
|
NonterminalKind::Block => match &token.kind {
|
2022-04-26 15:40:14 +03:00
|
|
|
token::OpenDelim(Delimiter::Brace) => true,
|
2024-04-22 19:46:51 +10:00
|
|
|
token::NtLifetime(..) => true,
|
2024-04-22 16:29:27 +10:00
|
|
|
token::Interpolated(nt) => match &**nt {
|
2024-04-22 19:46:51 +10:00
|
|
|
NtBlock(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true,
|
2024-04-18 16:22:02 +10:00
|
|
|
NtItem(_) | NtPath(_) => false,
|
2023-08-11 09:25:16 +10:00
|
|
|
},
|
2024-04-17 09:37:00 +10:00
|
|
|
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k {
|
|
|
|
MetaVarKind::Block
|
|
|
|
| MetaVarKind::Stmt
|
|
|
|
| MetaVarKind::Expr { .. }
|
|
|
|
| MetaVarKind::Literal => true,
|
|
|
|
MetaVarKind::Item
|
|
|
|
| MetaVarKind::Pat(_)
|
2025-02-21 16:47:07 +11:00
|
|
|
| MetaVarKind::Ty { .. }
|
2024-04-18 16:22:02 +10:00
|
|
|
| MetaVarKind::Meta { .. }
|
2024-04-17 09:37:00 +10:00
|
|
|
| MetaVarKind::Path
|
|
|
|
| MetaVarKind::Vis => false,
|
|
|
|
MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => {
|
|
|
|
unreachable!()
|
|
|
|
}
|
|
|
|
},
|
2020-07-27 14:04:54 +02:00
|
|
|
_ => false,
|
|
|
|
},
|
2022-11-22 09:42:01 +00:00
|
|
|
NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
|
2024-04-22 19:46:51 +10:00
|
|
|
token::PathSep | token::Ident(..) | token::NtIdent(..) => true,
|
2024-04-17 09:37:00 +10:00
|
|
|
token::Interpolated(nt) => nt_may_be_ident(nt),
|
|
|
|
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => {
|
|
|
|
may_be_ident(*kind)
|
|
|
|
}
|
2020-07-27 14:04:54 +02:00
|
|
|
_ => false,
|
|
|
|
},
|
2024-01-22 02:29:21 +00:00
|
|
|
NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
|
2022-11-22 09:42:01 +00:00
|
|
|
NonterminalKind::Lifetime => match &token.kind {
|
2024-09-05 05:43:55 -04:00
|
|
|
token::Lifetime(..) | token::NtLifetime(..) => true,
|
2020-07-27 14:04:54 +02:00
|
|
|
_ => false,
|
|
|
|
},
|
2020-10-26 21:02:48 -04:00
|
|
|
NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
|
|
|
|
!matches!(token.kind, token::CloseDelim(_))
|
|
|
|
}
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 14:08:59 +10:00
|
|
|
/// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call
|
|
|
|
/// site.
|
|
|
|
#[inline]
|
2024-04-22 16:29:27 +10:00
|
|
|
pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> {
|
2020-07-27 14:04:54 +02:00
|
|
|
// A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
|
|
|
|
// which requires having captured tokens available. Since we cannot determine
|
|
|
|
// in advance whether or not a proc-macro will be (transitively) invoked,
|
|
|
|
// we always capture tokens for any `Nonterminal` which needs them.
|
2021-05-06 16:21:40 +03:00
|
|
|
let mut nt = match kind {
|
2022-03-25 12:39:12 +11:00
|
|
|
// Note that TT is treated differently to all the others.
|
2023-08-03 16:43:05 +10:00
|
|
|
NonterminalKind::TT => return Ok(ParseNtResult::Tt(self.parse_token_tree())),
|
2021-01-18 16:47:37 -05:00
|
|
|
NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
|
2023-08-11 08:39:20 +10:00
|
|
|
Some(item) => NtItem(item),
|
2021-01-13 16:28:57 -05:00
|
|
|
None => {
|
2023-12-18 14:00:17 +11:00
|
|
|
return Err(self
|
|
|
|
.dcx()
|
|
|
|
.create_err(UnexpectedNonterminal::Item(self.token.span)));
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
|
|
|
},
|
2020-08-21 17:52:52 -04:00
|
|
|
NonterminalKind::Block => {
|
2021-02-13 12:42:43 -05:00
|
|
|
// While a block *expression* may have attributes (e.g. `#[my_attr] { ... }`),
|
2021-01-22 13:28:08 -05:00
|
|
|
// the ':block' matcher does not support them
|
2023-08-11 08:39:20 +10:00
|
|
|
NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?)
|
2020-08-21 17:52:52 -04:00
|
|
|
}
|
2021-01-18 16:47:37 -05:00
|
|
|
NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
|
2023-08-11 08:39:20 +10:00
|
|
|
Some(s) => NtStmt(P(s)),
|
2021-01-13 16:28:57 -05:00
|
|
|
None => {
|
2023-12-18 14:00:17 +11:00
|
|
|
return Err(self
|
|
|
|
.dcx()
|
|
|
|
.create_err(UnexpectedNonterminal::Statement(self.token.span)));
|
2020-09-10 16:59:30 -04:00
|
|
|
}
|
2021-01-13 16:28:57 -05:00
|
|
|
},
|
2024-06-23 08:13:56 +10:00
|
|
|
NonterminalKind::Pat(pat_kind) => {
|
Remove `NtPat`.
The one notable test change is `tests/ui/macros/trace_faulty_macros.rs`.
This commit removes the complicated `Interpolated` handling in
`expected_expression_found` that results in a longer error message. But
I think the new, shorter message is actually an improvement.
The original complaint was in #71039, when the error message started
with "error: expected expression, found `1 + 1`". That was confusing
because `1 + 1` is an expression. Other than that, the reporter said
"the whole error message is not too bad if you ignore the first line".
Subsequently, extra complexity and wording was added to the error
message. But I don't think the extra wording actually helps all that
much. In particular, it still says of the `1+1` that "this is expected
to be expression". This repeats the problem from the original complaint!
This commit removes the extra complexity, reverting to a simpler error
message. This is primarily because the traversal is a pain without
`Interpolated` tokens. Nonetheless, I think the error message is
*improved*. It now starts with "expected expression, found `pat`
metavariable", which is much clearer and the real problem. It also
doesn't say anything specific about `1+1`, which is good, because the
`1+1` isn't really relevant to the error -- it's the `$e:pat` that's
important.
2024-04-18 12:44:11 +10:00
|
|
|
return Ok(ParseNtResult::Pat(
|
|
|
|
self.collect_tokens_no_attrs(|this| match pat_kind {
|
|
|
|
PatParam { .. } => this.parse_pat_no_top_alt(None, None),
|
|
|
|
PatWithOr => this.parse_pat_no_top_guard(
|
|
|
|
None,
|
|
|
|
RecoverComma::No,
|
|
|
|
RecoverColon::No,
|
|
|
|
CommaRecoveryMode::EitherTupleOrPipe,
|
|
|
|
),
|
|
|
|
})?,
|
|
|
|
pat_kind,
|
|
|
|
));
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
2024-06-23 08:13:56 +10:00
|
|
|
NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?),
|
2020-08-21 18:28:47 -04:00
|
|
|
NonterminalKind::Literal => {
|
2021-01-22 13:28:08 -05:00
|
|
|
// The `:literal` matcher does not support attributes
|
|
|
|
NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?)
|
|
|
|
}
|
2023-08-11 08:39:20 +10:00
|
|
|
NonterminalKind::Ty => {
|
2024-04-17 13:17:44 +10:00
|
|
|
return Ok(ParseNtResult::Ty(
|
|
|
|
self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?,
|
|
|
|
));
|
2023-10-13 08:58:33 +00:00
|
|
|
}
|
2020-07-27 14:04:54 +02:00
|
|
|
// this could be handled like a token, since it is one
|
|
|
|
NonterminalKind::Ident => {
|
2024-04-22 19:46:51 +10:00
|
|
|
return if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
|
|
|
|
self.bump();
|
|
|
|
Ok(ParseNtResult::Ident(ident, is_raw))
|
|
|
|
} else {
|
|
|
|
Err(self.dcx().create_err(UnexpectedNonterminal::Ident {
|
|
|
|
span: self.token.span,
|
|
|
|
token: self.token.clone(),
|
|
|
|
}))
|
|
|
|
};
|
2023-10-13 08:58:33 +00:00
|
|
|
}
|
2022-11-22 09:42:01 +00:00
|
|
|
NonterminalKind::Path => {
|
2023-08-03 08:56:31 +00:00
|
|
|
NtPath(P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?))
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
2024-04-18 16:22:02 +10:00
|
|
|
NonterminalKind::Meta => {
|
|
|
|
return Ok(ParseNtResult::Meta(P(self.parse_attr_item(ForceCollect::Yes)?)));
|
|
|
|
}
|
2023-08-11 08:39:20 +10:00
|
|
|
NonterminalKind::Vis => {
|
2024-04-17 12:17:09 +10:00
|
|
|
return Ok(ParseNtResult::Vis(P(self.collect_tokens_no_attrs(|this| {
|
|
|
|
this.parse_visibility(FollowedByType::Yes)
|
|
|
|
})?)));
|
2023-10-13 08:58:33 +00:00
|
|
|
}
|
2020-07-27 14:04:54 +02:00
|
|
|
NonterminalKind::Lifetime => {
|
2024-06-20 16:36:35 -04:00
|
|
|
// We want to keep `'keyword` parsing, just like `keyword` is still
|
|
|
|
// an ident for nonterminal purposes.
|
2024-09-05 05:43:55 -04:00
|
|
|
return if let Some((ident, is_raw)) = self.token.lifetime() {
|
2024-06-20 16:36:35 -04:00
|
|
|
self.bump();
|
2024-09-05 05:43:55 -04:00
|
|
|
Ok(ParseNtResult::Lifetime(ident, is_raw))
|
2020-07-27 14:04:54 +02:00
|
|
|
} else {
|
2024-04-22 19:46:51 +10:00
|
|
|
Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
|
2022-10-14 23:16:25 +02:00
|
|
|
span: self.token.span,
|
|
|
|
token: self.token.clone(),
|
2024-04-22 19:46:51 +10:00
|
|
|
}))
|
|
|
|
};
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
2021-05-06 16:21:40 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// If tokens are supported at all, they should be collected.
|
|
|
|
if matches!(nt.tokens_mut(), Some(None)) {
|
|
|
|
panic!(
|
|
|
|
"Missing tokens for nt {:?} at {:?}: {:?}",
|
|
|
|
nt,
|
2023-07-31 14:55:47 +00:00
|
|
|
nt.use_span(),
|
2021-05-06 16:21:40 +03:00
|
|
|
pprust::nonterminal_to_string(&nt)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-02-03 06:44:41 +03:00
|
|
|
Ok(ParseNtResult::Nt(Arc::new(nt)))
|
2020-07-27 14:04:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The token is an identifier, but not `_`.
|
|
|
|
/// We prohibit passing `_` to macros expecting `ident` for now.
|
2024-02-13 23:28:27 +00:00
|
|
|
fn get_macro_ident(token: &Token) -> Option<(Ident, token::IdentIsRaw)> {
|
2020-07-27 14:04:54 +02:00
|
|
|
token.ident().filter(|(ident, _)| ident.name != kw::Underscore)
|
|
|
|
}
|