diff options
| author | Lukas Wirth <me@lukaswirth.dev> | 2025-07-07 18:42:12 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-07 18:42:12 +0000 |
| commit | 0209f9e5f87a943b1461e883ff4b101cee6e2b1a (patch) | |
| tree | e664347f1242f9dcacfdd56772b0e0c859a2a08b | |
| parent | 7fbbdb62955e13ec3825fe9fe954ed4b3747200f (diff) | |
| parent | f74e9b0ebfacf79e318077cf959f734a24a38e93 (diff) | |
| download | rust-0209f9e5f87a943b1461e883ff4b101cee6e2b1a.tar.gz rust-0209f9e5f87a943b1461e883ff4b101cee6e2b1a.zip | |
Merge pull request #20180 from ChayimFriedman2/parser-stuck
fix: Always bump in the parser in `err_and_bump()`
4 files changed, 50 insertions, 8 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 1c69b37f164..5e95b061399 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -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, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index d5874f829ba..70c38d4d7c7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -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); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 8ed0fc6729f..c31ebc42a86 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -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!['}']) { diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 36a363afe93..ca02d9fdfde 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -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. |
