Skip to content

fix(semantic): reject the malformed macro rule declarations that silently do nothing - #10304

Open
orizi wants to merge 1 commit into
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5from
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9
Open

orizi wants to merge 1 commit into
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5from
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9

Conversation

@orizi

@orizi orizi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

A pattern capturing into $defsite / $callsite, a ? repetition taking a
separator and an empty $() body are all accepted today and then contribute
nothing, and a rule whose pattern has a parse error is dropped silently, so
every call written for it fails with a bare "no matching rule".

orizi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

}
SemanticDiagnosticKind::MacroPlaceholderNamedAfterResolverModifier(name) => {
format!(
"`${}` is a resolver modifier, so a macro placeholder may not be named after \

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the explaination seems like too much.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened both messages to the bare statement.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened to the bare statement.

"Macro repetition block is empty, so it matches nothing.".into()
}
SemanticDiagnosticKind::MacroRuleWithUnparsablePattern => {
"This macro rule's pattern could not be parsed, so the rule can never match a call."

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the explaination seems like too much.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened to the bare statement.

@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes macro declaration and rule matching behavior; invalid rules now error at definition time and no longer participate in matching, which may surface new errors in existing code that relied on silent dead rules.

Overview
Adds declaration-time validation for user-defined inline macro rules so several malformed patterns no longer fail silently at call sites with only “no matching rule”.

Macro declaration analysis now walks pattern repetitions and subtrees to reject empty $() bodies (E2207), ? repetitions with separators in both pattern and expansion (E2206), and placeholders named exactly $defsite / $callsite (E2205). Rules whose pattern still has parse errors emit E2208 and are excluded from matching instead of being dropped quietly. Expansion placeholder checks are skipped when the pattern has modifier-named or duplicate placeholders so secondary driver errors are not reported.

New diagnostics E2205–E2208 are wired in diagnostic.rs; inline_macros tests cover these cases and adjust the infinite-loop guard test now that ($()* is rejected at declaration time.

Reviewed by Cursor Bugbot for commit 8372386. Bugbot is set up for automated code reviews on this repo. Configure here.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from a53a9fc to e2eb1e2 Compare August 5, 2026 10:54
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch 2 times, most recently from c1f115d to 967a7ef Compare August 5, 2026 11:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from e2eb1e2 to 09a334a Compare August 5, 2026 11:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 1807ca6 to e6ca70b Compare August 6, 2026 11:33
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch 2 times, most recently from c860e0c to 4e8aa3e Compare August 12, 2026 20:54

@eytan-starkware eytan-starkware left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eytan-starkware+AGNT made 7 comments.
Reviewable status: 0 of 3 files reviewed, 6 unresolved discussions (waiting on eytan-starkware, orizi, and TomerStarkware).


a discussion (no related file):
Note: the comments below are from an automatic orizi-review run (Claude agents reviewing in Ori's style, findings adversarially verified before posting). Treat with the usual bot skepticism.


crates/cairo-lang-semantic/src/expr/test_data/inline_macros line 3282 at r3 (raw file):

//! > ==========================================================================

//! > Test a pattern capturing into the name `$defsite`.

every new case here is a top-level pattern element. check_pattern_elements recurses into repetitions and subtrees and none of that recursion is pinned - add ($($defsite:ident)*), ([$defsite:ident]) and a ?-with-separator nested inside an outer repetition.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 196 at r3 (raw file):

/// `Err` if any was reported, for the caller to mark the rule with - a defective element makes the
/// rule's meaning unclear, so it must not expand.
fn check_pattern_elements<'db>(

third full traversal of the pattern with the same four match arms - PatternPlaceholders::collect right below already visits every Param/Repetition/Subtree and already computes the placeholder name (so check_placeholder_name re-interns it). fold the name check into collect - it has both the name and the ptr - and this shrinks to the two repetition checks.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 259 at r3 (raw file):

    diagnostics: &mut SemanticDiagnostics<'db>,
) -> Maybe<()> {
    let ast::OptionTerminalComma::TerminalComma(separator) = repetition.separator(db) else {

let-chain:

    if let ast::OptionTerminalComma::TerminalComma(separator) = repetition.separator(db)
        && matches!(repetition.operator(db), ast::MacroRepetitionOperator::ZeroOrOne(_))
    {
        return Err(diagnostics.report(
            separator.stable_ptr(db).untyped(),
            SemanticDiagnosticKind::MacroRepetitionSeparatorWithZeroOrOne,
        ));
    }
    Ok(())

crates/cairo-lang-semantic/src/items/macro_declaration.rs line 408 at r3 (raw file):

        // which a reused name leaves ambiguous - checking it then reports the ambiguity as
        // a defect of the expansion, which it is not.
        if placeholders.reused_names.is_empty() {

this was rule_err.is_ok(), which for an unparsed pattern was always true, so the expansion was checked. now it's reused_names.is_empty(), which for an unparsed pattern depends on whether recovery happened to leave two nameless placeholders - i.e. a coin flip on whether a dropped rule's expansion gets diagnostics.

make it explicit: hoist the !pattern_is_parsed report + continue to the top of the loop, right after let pattern = rule_syntax.lhs(db);. then the if pattern_is_parsed wrapper, the nameless-placeholders comment above it and this whole question go away.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 426 at r3 (raw file):

            // it. It is reported so that a call written for it points at the parse error rather
            // than failing with a bare "no matching rule".
            diagnostics.report(

the comment says this is so "a call written for it points at the parse error rather than failing with a bare 'no matching rule'" - but it doesn't. look at the pair golden: E1008 from the parser on the same rule, then this E2208 on an overlapping span, then the same E2158 on the call, unchanged. three diagnostics for one typo, and the confusing one is still there.

fix the comment, or make it actually replace the E2158.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 613 at r3 (raw file):

        if let Some(repetition) = ast::MacroRepetition::cast(db, node) {
            self.rule_err =

expand_repetition never looks at the operator - in an expansion ? is just *. so $(...),? on the rhs today expands over all the driver's groups and emits the separator between them, correctly. this call turns working code into an error, and it isn't "silently does nothing" as the PR title claims. the doc's "allows at most one group" rationale only holds for the pattern.

either make the expansion honor ? (fail when the driver has more than one group) and then reject the separator, or keep the check pattern-only and drop this call.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from 4e8aa3e to bb682b9 Compare August 16, 2026 11:37
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 2dd6ba4 to 5ca0569 Compare August 16, 2026 11:37
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from bb682b9 to fb771d3 Compare August 17, 2026 18:03
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 5ca0569 to 9753276 Compare August 17, 2026 18:03

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi+AGNT made 6 comments and resolved 5 discussions.
Reviewable status: 0 of 3 files reviewed, 1 unresolved discussion (waiting on eytan-starkware+AGNT and TomerStarkware).


crates/cairo-lang-semantic/src/expr/test_data/inline_macros line 3282 at r3 (raw file):

Previously, eytan-starkware+AGNT (Agent AGNT for eytan-starkware) wrote…

every new case here is a top-level pattern element. check_pattern_elements recurses into repetitions and subtrees and none of that recursion is pinned - add ($($defsite:ident)*), ([$defsite:ident]) and a ?-with-separator nested inside an outer repetition.

Added all three: ($($defsite:ident)*), ([$defsite:ident]), and a ,? nested inside an outer repetition.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 196 at r3 (raw file):

Previously, eytan-starkware+AGNT (Agent AGNT for eytan-starkware) wrote…

third full traversal of the pattern with the same four match arms - PatternPlaceholders::collect right below already visits every Param/Repetition/Subtree and already computes the placeholder name (so check_placeholder_name re-interns it). fold the name check into collect - it has both the name and the ptr - and this shrinks to the two repetition checks.

Folded into PatternPlaceholders::collect; check_pattern_elements shrank to the repetition checks.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 259 at r3 (raw file):

Previously, eytan-starkware+AGNT (Agent AGNT for eytan-starkware) wrote…

let-chain:

    if let ast::OptionTerminalComma::TerminalComma(separator) = repetition.separator(db)
        && matches!(repetition.operator(db), ast::MacroRepetitionOperator::ZeroOrOne(_))
    {
        return Err(diagnostics.report(
            separator.stable_ptr(db).untyped(),
            SemanticDiagnosticKind::MacroRepetitionSeparatorWithZeroOrOne,
        ));
    }
    Ok(())

Done.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 408 at r3 (raw file):

Previously, eytan-starkware+AGNT (Agent AGNT for eytan-starkware) wrote…

this was rule_err.is_ok(), which for an unparsed pattern was always true, so the expansion was checked. now it's reused_names.is_empty(), which for an unparsed pattern depends on whether recovery happened to leave two nameless placeholders - i.e. a coin flip on whether a dropped rule's expansion gets diagnostics.

make it explicit: hoist the !pattern_is_parsed report + continue to the top of the loop, right after let pattern = rule_syntax.lhs(db);. then the if pattern_is_parsed wrapper, the nameless-placeholders comment above it and this whole question go away.

Done - report + continue at the top of the loop; the wrapper, the nameless-placeholder comment and the coin flip are gone.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 426 at r3 (raw file):

Previously, eytan-starkware+AGNT (Agent AGNT for eytan-starkware) wrote…

the comment says this is so "a call written for it points at the parse error rather than failing with a bare 'no matching rule'" - but it doesn't. look at the pair golden: E1008 from the parser on the same rule, then this E2208 on an overlapping span, then the same E2158 on the call, unchanged. three diagnostics for one typo, and the confusing one is still there.

fix the comment, or make it actually replace the E2158.

Fixed the comment - it claims only the no-shadowing effect now.


crates/cairo-lang-semantic/src/items/macro_declaration.rs line 613 at r3 (raw file):

Previously, eytan-starkware+AGNT (Agent AGNT for eytan-starkware) wrote…

expand_repetition never looks at the operator - in an expansion ? is just *. so $(...),? on the rhs today expands over all the driver's groups and emits the separator between them, correctly. this call turns working code into an error, and it isn't "silently does nothing" as the PR title claims. the doc's "allows at most one group" rationale only holds for the pattern.

either make the expansion honor ? (fail when the driver has more than one group) and then reject the separator, or keep the check pattern-only and drop this call.

Half-taken: the check stays because rustc rejects a separator on a ? expansion block with the same error (the golden quotes it), so dropping it would open a parity gap. What was wrong was the rationale - the doc now says a separator would simply never be emitted there, and the ? promise itself becomes enforced at expansion in the E2210 PR up the stack.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 9753276 to 952557f Compare August 23, 2026 08:43
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch 2 times, most recently from 9056373 to c1b78a0 Compare August 23, 2026 10:31
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 952557f to 365d74c Compare August 23, 2026 10:31
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 365d74c to a3ae693 Compare August 25, 2026 12:30
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from c1b78a0 to b1d0456 Compare August 25, 2026 12:30
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from a3ae693 to d20f422 Compare August 26, 2026 19:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from b1d0456 to d168b6a Compare August 26, 2026 19:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from d20f422 to 9bb15db Compare August 27, 2026 04:32
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch 2 times, most recently from a40e519 to e358fcd Compare August 27, 2026 13:33
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch 2 times, most recently from 8495163 to 827bc64 Compare August 28, 2026 10:07
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch 2 times, most recently from 6e5b156 to d9bfd11 Compare August 28, 2026 12:58
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 32b563d to dc0804b Compare September 14, 2026 04:51
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from d9bfd11 to 236c562 Compare September 14, 2026 04:51
…ntly do nothing

A pattern capturing into `$defsite` / `$callsite`, a `?` repetition taking a
separator and an empty `$()` body are all accepted today and then contribute
nothing, and a rule whose pattern has a parse error is dropped silently, so
every call written for it fails with a bare "no matching rule".
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from 236c562 to 8372386 Compare September 14, 2026 06:56
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from dc0804b to 74aada1 Compare September 14, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants