1
Fork 0

Remove NtItem and NtStmt.

This involves replacing `nt_pretty_printing_compatibility_hack` with
`stream_pretty_printing_compatibility_hack`.

The handling of statements in `transcribe` is slightly different to
other nonterminal kinds, due to the lack of `from_ast` implementation
for empty statements.

Notable test changes:
- `tests/ui/proc-macro/expand-to-derive.rs`: the diff looks large but
  the only difference is the insertion of a single invisible-delimited
  group around a metavar.
This commit is contained in:
Nicholas Nethercote 2024-04-18 14:53:52 +10:00
parent 98a48781fe
commit 141719f68a
18 changed files with 198 additions and 145 deletions

View file

@ -5,7 +5,7 @@ use std::ops::Bound;
use ast::Label;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
use rustc_ast::util::classify::{self, TrailingBrace};
use rustc_ast::{
AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
@ -33,8 +33,8 @@ impl<'a> Parser<'a> {
/// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
/// whether or not we have attributes.
// Public for rustfmt usage.
pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| {
pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
Ok(self.parse_stmt_without_recovery(false, force_collect, false).unwrap_or_else(|e| {
e.emit();
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
None
@ -42,23 +42,27 @@ impl<'a> Parser<'a> {
}
/// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
/// whether or not we have attributes.
// Public for `cfg_eval` macro expansion.
/// whether or not we have attributes. If `force_full_expr` is true, parses the stmt without
/// using `Restriction::STMT_EXPR`. Public for `cfg_eval` macro expansion.
pub fn parse_stmt_without_recovery(
&mut self,
capture_semi: bool,
force_collect: ForceCollect,
force_full_expr: bool,
) -> PResult<'a, Option<Stmt>> {
let pre_attr_pos = self.collect_pos();
let attrs = self.parse_outer_attributes()?;
let lo = self.token.span;
maybe_whole!(self, NtStmt, |stmt| {
if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
this.parse_stmt_without_recovery(false, ForceCollect::Yes, false)
}) {
let mut stmt = stmt.expect("an actual statement");
stmt.visit_attrs(|stmt_attrs| {
attrs.prepend_to_nt_inner(stmt_attrs);
});
Some(stmt.into_inner())
});
return Ok(Some(stmt));
}
if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
self.bump();
@ -147,12 +151,14 @@ impl<'a> Parser<'a> {
} else if self.token != token::CloseDelim(Delimiter::Brace) {
// Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case
// above.
let restrictions =
if force_full_expr { Restrictions::empty() } else { Restrictions::STMT_EXPR };
let e = self.collect_tokens(
Some(pre_attr_pos),
AttrWrapper::empty(),
force_collect,
|this, _empty_attrs| {
let (expr, _) = this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?;
let (expr, _) = this.parse_expr_res(restrictions, attrs)?;
Ok((expr, Trailing::No, UsePreAttrPos::Yes))
},
)?;
@ -229,11 +235,15 @@ impl<'a> Parser<'a> {
let mac = P(MacCall { path, args });
let kind = if (style == MacStmtStyle::Braces
&& self.token != token::Dot
&& self.token != token::Question)
|| self.token == token::Semi
|| self.token == token::Eof
{
&& !matches!(self.token.kind, token::Dot | token::Question))
|| matches!(
self.token.kind,
token::Semi
| token::Eof
| token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
MetaVarKind::Stmt
)))
) {
StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
} else {
// Since none of the above applied, this is an expression statement macro.
@ -501,7 +511,7 @@ impl<'a> Parser<'a> {
// bar;
//
// which is valid in other languages, but not Rust.
match self.parse_stmt_without_recovery(false, ForceCollect::No) {
match self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
// If the next token is an open brace, e.g., we have:
//
// if expr other_expr {
@ -810,10 +820,24 @@ impl<'a> Parser<'a> {
&mut self,
recover: AttemptLocalParseRecovery,
) -> PResult<'a, Option<Stmt>> {
// Skip looking for a trailing semicolon when we have an interpolated statement.
maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner()));
// Skip looking for a trailing semicolon when we have a metavar seq.
if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
// Why pass `true` for `force_full_expr`? Statement expressions are less expressive
// than "full" expressions, due to the `STMT_EXPR` restriction, and sometimes need
// parentheses. E.g. the "full" expression `match paren_around_match {} | true` when
// used in statement context must be written `(match paren_around_match {} | true)`.
// However, if the expression we are parsing in this statement context was pasted by a
// declarative macro, it may have come from a "full" expression context, and lack
// these parentheses. So we lift the `STMT_EXPR` restriction to ensure the statement
// will reparse successfully.
this.parse_stmt_without_recovery(false, ForceCollect::No, true)
}) {
let stmt = stmt.expect("an actual statement");
return Ok(Some(stmt));
}
let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else {
let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?
else {
return Ok(None);
};