Merge pull request #20180 from ChayimFriedman2/parser-stuck
Some checks are pending
metrics / build_metrics (push) Waiting to run
metrics / other_metrics (diesel-1.4.8) (push) Blocked by required conditions
metrics / other_metrics (hyper-0.14.18) (push) Blocked by required conditions
metrics / other_metrics (ripgrep-13.0.0) (push) Blocked by required conditions
metrics / other_metrics (self) (push) Blocked by required conditions
metrics / other_metrics (webrender-2022) (push) Blocked by required conditions
metrics / generate_final_metrics (push) Blocked by required conditions
rustdoc / rustdoc (push) Waiting to run

fix: Always bump in the parser in `err_and_bump()`
This commit is contained in:
Lukas Wirth 2025-07-07 18:42:12 +00:00 committed by GitHub
commit 778e08df16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 50 additions and 8 deletions

View file

@ -20,13 +20,14 @@ use base_db::RootQueryDb;
use expect_test::Expect;
use hir_expand::{
AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
builtin::quote::quote,
db::ExpandDatabase,
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
span_map::SpanMapRef,
};
use intern::Symbol;
use intern::{Symbol, sym};
use itertools::Itertools;
use span::{Edition, Span};
use span::{Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext};
use stdx::{format_to, format_to_acc};
use syntax::{
AstNode, AstPtr,
@ -34,7 +35,9 @@ use syntax::{
SyntaxNode, T,
ast::{self, edit::IndentLevel},
};
use syntax_bridge::token_tree_to_syntax_node;
use test_fixture::WithFixture;
use tt::{TextRange, TextSize};
use crate::{
AdtId, Lookup, ModuleDefId,
@ -386,3 +389,38 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
other.type_id() == TypeId::of::<Self>()
}
}
#[test]
fn regression_20171() {
// This really isn't the appropriate place to put this test, but it's convenient with access to `quote!`.
let span = Span {
range: TextRange::empty(TextSize::new(0)),
anchor: SpanAnchor {
file_id: span::EditionedFileId::current_edition(span::FileId::from_raw(0)),
ast_id: ROOT_ERASED_FILE_AST_ID,
},
ctx: SyntaxContext::root(Edition::CURRENT),
};
let close_brace = tt::Punct { char: '}', spacing: tt::Spacing::Alone, span };
let dotdot1 = tt::Punct { char: '.', spacing: tt::Spacing::Joint, span };
let dotdot2 = tt::Punct { char: '.', spacing: tt::Spacing::Alone, span };
let dollar_crate = sym::dollar_crate;
let tt = quote! {
span => {
if !((matches!(
drive_parser(&mut parser, data, false),
Err(TarParserError::CorruptField {
field: CorruptFieldContext::PaxKvLength,
error: GeneralParseError::ParseInt(ParseIntError { #dotdot1 #dotdot2 })
})
#close_brace ))) {
#dollar_crate::panic::panic_2021!();
}}
};
token_tree_to_syntax_node(
&tt,
syntax_bridge::TopEntryPoint::MacroStmts,
&mut |_| Edition::CURRENT,
Edition::CURRENT,
);
}

View file

@ -129,7 +129,7 @@ macro_rules! quote {
}
}
}
pub(super) use quote;
pub use quote;
pub trait ToTokenTree {
fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder);

View file

@ -411,11 +411,10 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
dir_spec.abandon(p);
op.abandon(p);
op_n.abandon(p);
p.err_and_bump("expected asm operand");
// improves error recovery and handles err_and_bump recovering from `{` which gets
// the parser stuck here
// improves error recovery
if p.at(T!['{']) {
p.error("expected asm operand");
// test_err bad_asm_expr
// fn foo() {
// builtin#asm(
@ -423,6 +422,8 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
// );
// }
expr(p);
} else {
p.err_and_bump("expected asm operand");
}
if p.at(T!['}']) {

View file

@ -29,7 +29,7 @@ pub(crate) struct Parser<'t> {
edition: Edition,
}
const PARSER_STEP_LIMIT: usize = 15_000_000;
const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15_000_000 };
impl<'t> Parser<'t> {
pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
@ -254,7 +254,10 @@ impl<'t> Parser<'t> {
/// Create an error node and consume the next token.
pub(crate) fn err_and_bump(&mut self, message: &str) {
self.err_recover(message, TokenSet::EMPTY);
let m = self.start();
self.error(message);
self.bump_any();
m.complete(self, ERROR);
}
/// Create an error node and consume the next token unless it is in the recovery set.