about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <me@lukaswirth.dev>2025-07-07 18:42:12 +0000
committerGitHub <noreply@github.com>2025-07-07 18:42:12 +0000
commit0209f9e5f87a943b1461e883ff4b101cee6e2b1a (patch)
treee664347f1242f9dcacfdd56772b0e0c859a2a08b
parent7fbbdb62955e13ec3825fe9fe954ed4b3747200f (diff)
parentf74e9b0ebfacf79e318077cf959f734a24a38e93 (diff)
downloadrust-0209f9e5f87a943b1461e883ff4b101cee6e2b1a.tar.gz
rust-0209f9e5f87a943b1461e883ff4b101cee6e2b1a.zip
Merge pull request #20180 from ChayimFriedman2/parser-stuck
fix: Always bump in the parser in `err_and_bump()`
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs7
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.