Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ fn expand_macro<'cx>(
// hacky, but speeds up the `html5ever` benchmark significantly. (Issue
// 68836 suggests a more comprehensive but more complex change to deal with
// this situation.)
// FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
let parser = parser_from_cx(sess, arg.clone());

// Try each arm's matchers.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2112,6 +2112,8 @@ impl<'a> Parser<'a> {
// HACK: This is needed so we can detect whether we're inside a macro,
// where regular assumptions about what tokens can follow other tokens
// don't necessarily apply.
&& self.may_recover()
// FIXME(Nilstrieb): Remove this check once `may_recover` actually stops recovery
&& self.subparser_name.is_none()
{
// It is likely that the closure body is a block but where the
Expand Down
30 changes: 28 additions & 2 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath {
};
}

#[derive(Clone, Copy)]
pub enum Recovery {
Allowed,
Forbidden,
}

#[derive(Clone)]
pub struct Parser<'a> {
pub sess: &'a ParseSess,
Expand Down Expand Up @@ -152,12 +158,15 @@ pub struct Parser<'a> {
/// This allows us to recover when the user forget to add braces around
/// multiple statements in the closure body.
pub current_closure: Option<ClosureSpans>,
/// Whether the parser is allowed to do recovery.
/// This is disabled when parsing macro arguments, see #103534
pub recovery: Recovery,
}

// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was a little unprecise, as macro matching now uses a Cow<'_, Parser> which it only clones when it actually needs nonterminals. Since the hottest macros (html5ever) don't use nonterminals, the parser size becomes less important.

// it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Parser<'_>, 328);
rustc_data_structures::static_assert_size!(Parser<'_>, 336);

/// Stores span information about a closure.
#[derive(Clone)]
Expand Down Expand Up @@ -483,6 +492,7 @@ impl<'a> Parser<'a> {
inner_attr_ranges: Default::default(),
},
current_closure: None,
recovery: Recovery::Allowed,
};

// Make parser point to the first token.
Expand All @@ -491,6 +501,22 @@ impl<'a> Parser<'a> {
parser
}

pub fn forbid_recovery(mut self) -> Self {
self.recovery = Recovery::Forbidden;
self
}

/// Whether the parser is allowed to recover from broken code.
///
/// If this returns false, recovering broken code into valid code (especially if this recovery does lookahead)
/// is not allowed. All recovery done by the parser must be gated behind this check.
///
/// Technically, this only needs to restruct eager recovery by doing lookahead at more tokens.
/// But making the distinction is very subtle, and simply forbidding all recovery is a lot simpler to uphold.
fn may_recover(&self) -> bool {
matches!(self.recovery, Recovery::Allowed)
}

pub fn unexpected<T>(&mut self) -> PResult<'a, T> {
match self.expect_one_of(&[], &[]) {
Err(e) => Err(e),
Expand Down