diff options
| author | Chayim Refael Friedman <chayimfr@gmail.com> | 2025-07-06 03:21:43 +0300 |
|---|---|---|
| committer | Chayim Refael Friedman <chayimfr@gmail.com> | 2025-07-06 03:21:43 +0300 |
| commit | f74e9b0ebfacf79e318077cf959f734a24a38e93 (patch) | |
| tree | 2bfd4e888748db4e91bae38b5f6d1a906a960b47 | |
| parent | 4b506ca0e1821e71fd8a6c7434dc02b5bd432a34 (diff) | |
| download | rust-f74e9b0ebfacf79e318077cf959f734a24a38e93.tar.gz rust-f74e9b0ebfacf79e318077cf959f734a24a38e93.zip | |
Always bump in the parser in `err_and_bump()`
Even when at curly braces, otherwise the parser can get stuck. This has happened in the past in #18625, but it was just worked around instead of handling the root of the problem. Now this happened again in #20171. IMO we can't let `err_and_bump()` not bump, that's too confusing and invites errors. We can (as I did) workaround the worse recovery instead.
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. |
