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

fix(semantic): reject the malformed macro rule declarations that silently do nothing#10304
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

@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 registration behavior; macros that previously compiled may now error at definition, but scope is limited to user-defined inline macros in semantic analysis.

Overview
User-defined inline macro rules are now validated at declaration time so broken patterns no longer fail silently at call sites with only “no matching rule.”

New diagnostics (E2205–E2208): pattern placeholders named $defsite or $callsite (captures that can never be read in expansions); ? repetitions with a separator (in both pattern and expansion); literally empty $() pattern bodies; and rules whose pattern did not parse (explicit E2208 while the rule is dropped, instead of only a call-site mismatch).

Declaration logic in macro_declaration.rs walks pattern repetitions/subtrees via check_pattern_elements, records modifier-named params in PatternPlaceholders::modifier_named, and applies the same separator check during expansion traversal.

Reviewed by Cursor Bugbot for commit fb771d3. 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 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 2 comments and resolved 2 discussions.
Reviewable status: 0 of 3 files reviewed, all discussions resolved.

}
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.

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.

Shortened to the bare statement.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch from 09a334a to 9719407 Compare August 5, 2026 15:15
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from 967a7ef to 37217a1 Compare August 5, 2026 15:15
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4a-small-decl-checks-f6-f8-f9 branch from 37217a1 to 60ea5f0 Compare August 5, 2026 16:58
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr3-subtree-delimiters-f5 branch 2 times, most recently 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
…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 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
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