Skip to content

Commit 8d7164b

Browse files
authored
feat(orizi): Add no-progress guard to macro repetition matching. (#9845)
1 parent 0f4666d commit 8d7164b

2 files changed

Lines changed: 73 additions & 7 deletions

File tree

crates/cairo-lang-semantic/src/expr/test_data/inline_macros

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,3 +2360,63 @@ error[E2156]: Inline macro `not::a::path::format` not found.
23602360
--> lib.cairo:2:5
23612361
not::a::path::format!("");
23622362
^^^^^^^^^^^^^^^^^^^^^^^^^
2363+
2364+
//! > ==========================================================================
2365+
2366+
//! > Test macro with empty inner repetition body (would loop infinitely without no-progress guard).
2367+
2368+
//! > test_runner_name
2369+
test_function_diagnostics(expect_diagnostics: false)
2370+
2371+
//! > crate_settings
2372+
edition = "2024_07"
2373+
[experimental_features]
2374+
negative_impls = false
2375+
associated_item_constraints = false
2376+
coupons = false
2377+
user_defined_inline_macros = true
2378+
2379+
//! > function_code
2380+
fn foo() {
2381+
test_macro!();
2382+
}
2383+
2384+
//! > function_name
2385+
foo
2386+
2387+
//! > module_code
2388+
macro test_macro {
2389+
($()*) => { 1 };
2390+
}
2391+
2392+
//! > expected_diagnostics
2393+
2394+
//! > ==========================================================================
2395+
2396+
//! > Test macro with repetition over empty subtrees (would fail to match without the subtree progress fix).
2397+
2398+
//! > test_runner_name
2399+
test_function_diagnostics(expect_diagnostics: false)
2400+
2401+
//! > crate_settings
2402+
edition = "2024_07"
2403+
[experimental_features]
2404+
negative_impls = false
2405+
associated_item_constraints = false
2406+
coupons = false
2407+
user_defined_inline_macros = true
2408+
2409+
//! > function_code
2410+
fn foo() {
2411+
test_macro!(() ());
2412+
}
2413+
2414+
//! > function_name
2415+
foo
2416+
2417+
//! > module_code
2418+
macro test_macro {
2419+
($(())*) => { 1 };
2420+
}
2421+
2422+
//! > expected_diagnostics

crates/cairo-lang-semantic/src/items/macro_declaration.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ pub fn is_macro_rule_match<'db>(
267267
/// Helper function for [expand_macro_rule].
268268
/// Traverses the macro expansion and replaces the placeholders with the provided values,
269269
/// while collecting the result in `res_buffer`.
270+
/// Returns `Some(true)` if the match succeeded and some input was consumed,
271+
/// `Some(false)` if the match succeeded but no input was consumed (empty match),
272+
/// and `None` if the match failed.
270273
fn is_macro_rule_match_ex<'db>(
271274
db: &'db dyn Database,
272275
matcher_elements: ast::MacroElements<'db>,
@@ -275,10 +278,12 @@ fn is_macro_rule_match_ex<'db>(
275278
>,
276279
ctx: &mut MatcherContext<'db>,
277280
consume_all_input: bool,
278-
) -> Option<()> {
281+
) -> Option<bool> {
282+
let mut advanced = false;
279283
for matcher_element in matcher_elements.elements(db) {
280284
match matcher_element {
281285
ast::MacroElement::Token(matcher_token) => {
286+
advanced = true;
282287
let input_token = input_iter.next()?;
283288
match input_token {
284289
ast::TokenTree::Token(token_tree_leaf) => {
@@ -296,6 +301,7 @@ fn is_macro_rule_match_ex<'db>(
296301
}
297302
}
298303
ast::MacroElement::Param(param) => {
304+
advanced = true;
299305
let placeholder_kind: PlaceholderKind =
300306
if let ast::OptionParamKind::ParamKind(param_kind) = param.kind(db) {
301307
param_kind.kind(db).into()
@@ -370,6 +376,7 @@ fn is_macro_rule_match_ex<'db>(
370376
}
371377
}
372378
ast::MacroElement::Subtree(matcher_subtree) => {
379+
advanced = true;
373380
let input_token = input_iter.next()?;
374381
if let ast::TokenTree::Subtree(input_subtree) = input_token {
375382
let inner_elements = get_macro_elements(db, matcher_subtree.subtree(db));
@@ -404,17 +411,16 @@ fn is_macro_rule_match_ex<'db>(
404411
loop {
405412
let mut inner_ctx = ctx.clone();
406413
let mut temp_iter = input_iter.clone();
407-
if is_macro_rule_match_ex(
414+
let Some(true) = is_macro_rule_match_ex(
408415
db,
409416
elements.clone(),
410417
&mut temp_iter,
411418
&mut inner_ctx,
412419
false,
413-
)
414-
.is_none()
415-
{
420+
) else {
416421
break;
417-
}
422+
};
423+
advanced = true;
418424
*ctx = inner_ctx;
419425
*input_iter = temp_iter;
420426
match_count += 1;
@@ -449,7 +455,7 @@ fn is_macro_rule_match_ex<'db>(
449455
if consume_all_input && input_iter.next().is_some() {
450456
return None;
451457
}
452-
Some(())
458+
Some(advanced)
453459
}
454460

455461
fn validate_repetition_operator_constraints(ctx: &MatcherContext<'_>) -> bool {

0 commit comments

Comments
 (0)