Unnamed repository; edit this file 'description' to name the repository.
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.
Chayim Refael Friedman 10 months ago
parent 3816d0a · commit 87cddda
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs42
-rw-r--r--crates/hir-expand/src/builtin/quote.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs7
-rw-r--r--crates/parser/src/parser.rs7
4 files changed, 50 insertions, 8 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 1c69b37f16..5e95b06139 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/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/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs
index d5874f829b..70c38d4d7c 100644
--- a/crates/hir-expand/src/builtin/quote.rs
+++ b/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/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 8ed0fc6729..c31ebc42a8 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/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/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 36a363afe9..ca02d9fdfd 100644
--- a/crates/parser/src/parser.rs
+++ b/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.