Skip to content

test(semantic): pin the macro matcher behaviors that already agree with rustc - #10306

Open
orizi wants to merge 1 commit into
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7from
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins
Open

orizi wants to merge 1 commit into
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7from
graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins

Conversation

@orizi

@orizi orizi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Tests only, pure append, zero production hunks and zero churn in either golden file.

Thirteen goldens pinning behaviors where Cairo's user-defined inline macros already match
rustc macro_rules!, so later work on the matcher and the expander cannot silently regress
them. Twelve in expr/expansion_test_data/inline_macros (expansion text) and one in
expr/test_data/inline_macros (a rejection, so a diagnostic).

Newly pinned, with the rustc 1.96.0 (ac68faa20 2026-05-25) output each expectation was
cross-checked against. Every rustc program transcribes through stringify!, so the
transcriber's own output is observed, and every one includes an invocation because rustc's
matcher and transcriber errors are lazy.

golden rustc program rustc output
bracketed call of a paren rule ($($x:expr),*) => { stringify!(($($x,)*)) }, m![1, 2] (1, 2,)
braced call of a paren rule same, m!{1, 2} (1, 2,)
empty bracketed call of a paren rule same, m![] ()
later rule after + matched nothing ($([$($x:ident),+]),*) => { 1 }; ($([$($x:ident),*]),*) => { 2 };, m!([], [a, b]) 2
repeating rule ahead of an empty rule ($($x:ident),+) => { 1 }; () => { 2 };, m!(a, b) 1
empty call taking the empty rule same, m!() 2
expansion operator differing from the pattern's ($($x:expr),*) => { stringify!((0, $($x,)?)) }, m!(1, 2) (0, 1, 2,)
the same over no captures at all same, m!() (0,)
placeholder broadcast into a deeper block ($(($k:expr, [$($v:expr),*])),*) => { stringify!(($($(($k * $v),)*)*)) }, m!((10, [1, 2]), (20, [3, 4])) ((10 * 1), (10 * 2), (20 * 3), (20 * 4),), value (10, 20, 60, 80)
the same with a non-final group of no captures same, m!((10, []), (20, [3, 4])) ((20 * 3), (20 * 4),), value (60, 80)
doubly nested block flattened into one sum ($([$($x:expr),*]),*) => { stringify!($($($x +)*)* 0) }, m!([1, 2], [3, 4]) 1 + 2 + 3 + 4 + 0, value 10
the same with a non-final group matching nothing same, m!([], [3, 4]) 3 + 4 + 0, value 7
_ not matched by an ident placeholder ($($x:ident),*) => { 7 }, m!(a, _, b) error: no rules expected reserved identifier '_' with note: while trying to match meta-variable $x:ident

Cairo's blessed expansion text is the same token sequence as rustc's in every row; it
differs only in the spacing around a spliced placeholder (1+2+3+4+0, ((10* 1),...),
which is the known trivia loss around $x and not a difference in what was spliced. Every
value the text evaluates to is the value rustc computes.

Test shape, per the plan's rules: every repetition level in every new case holds two
captures; the offending or empty element is the non-final one wherever the case has one
([] first in the fallthrough, sum and bcast cases, _ second in the _ case); each
behavior has an explicit zero-match or empty-group case; and the _ case calls the macro
once with a matching argument list and once with _, so its single E2158 shows the
rejection is the _ rather than the repetition.

Because these pin existing behavior there is no fix to revert, so each expected block was
shown load-bearing by corrupting it and observing the failure. All thirteen fail as an
expanded_code / expected_diagnostics mismatch on exactly their own case - never a panic
and never a diagnostics-expectation error.

Two throwaway production perturbations, reverted and not part of this commit, additionally
show the pins are sensitive to the behavior they name rather than to arbitrary text:

  • Making is_macro_rule_match return None for a braced or bracketed call reddens exactly
    the three outermost-delimiter cases and nothing else in the file.
  • Capping an expansion repetition at one group when its operator is ? reddens exactly the
    differing-operator case and nothing else in the file.

Not pinned, with reasons:

  • Two cross-check rows are not expressible on this base. A transcriber separator that
    differs from the pattern's needs a non-comma separator, which the grammar does not have
    (($($x:ident),*) => { ($($x);*) } is an E1031 parse error), and rustc's metavariable
    expressions are still unstable there, so Cairo's rejection of ${count(x)} is not
    agreement with a rustc behavior that exists.
  • Nine rows already had a golden and were only re-verified on this base.
  • No E2199 sibling-zip case is added; that behavior needs its own design decision.

$($($x +)*)* 0 is pinned as the accepting expansion it is in rustc, not as an error.

Gates: cargo test --profile=ci-dev -p cairo-lang-semantic (106 passed),
./scripts/rust_fmt.sh --check, ./scripts/clippy.sh --profile=ci-dev,
./scripts/validate_error_codes.sh,
cargo run --profile=ci-dev --bin cairo-test -- tests/bug_samples --starknet (68 passed),
and cargo run --profile=ci-dev --bin cairo-test -- corelib/ (737 passed) - the last
because no other gate compiles corelib/src/test/.

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

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from e4aab9d to a7e94c5 Compare August 4, 2026 08:41
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 5f7d17c to 9b8111a Compare August 4, 2026 08:41
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from a7e94c5 to cc43a8c Compare August 5, 2026 10:54
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 9b8111a to 46bd0ab Compare August 5, 2026 10:54
@orizi
orizi marked this pull request as ready for review August 5, 2026 11:10
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Test-only golden file updates with no production code changes; risk is limited to CI/test maintenance if expectations drift.

Overview
Adds regression goldens only for user-defined inline macros where Cairo already matches rustc macro_rules!, so future matcher/expander work cannot silently change behavior.

In expr/expansion_test_data/inline_macros, seven new test_expand_expr cases pin expansion output for: call delimiters ((), [], {}) independent of the rule’s outer parens; rule fallthrough when + matches nothing in a non-final group; + vs empty () rules; expansion repetitions whose ?/+/* operator is ignored relative to the pattern; deep placeholder broadcast into nested $() blocks; and doubly nested expansion blocks flattened into one sum (including empty non-final groups).

In expr/test_data/inline_macros, one new diagnostic case asserts m!(a, _, b) fails with E2158 because _ is not captured by $x:ident, alongside a matching m!(a, b) control call.

Reviewed by Cursor Bugbot for commit 17e644e. 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.pr5-regression-pins branch from cc43a8c to 9f845a8 Compare August 5, 2026 11:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 46bd0ab to 929912f Compare August 5, 2026 11:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from 9f845a8 to a757f08 Compare August 5, 2026 15:15

@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 6 comments.
Reviewable status: 0 of 2 files reviewed, 5 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/expansion_test_data/inline_macros line 279 at r1 (raw file):

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

//! > Test calls of a parenthesized rule written with every call delimiter (valid).

name says every call delimiter, only bracketed and braced are called. add the paren call or drop "every":

let parenthesized = outer_delim!(1, 2);
let bracketed = outer_delim![1, 2];

crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 332 at r1 (raw file):

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

//! > Test a `+` rule matching at least one capture, next to an empty rule.

this is fallthrough above, one nesting level up - same behavior, a rule whose + matched nothing falls through to the next rule. the only thing it adds is a call that does match the + rule, which fallthrough should be carrying itself. drop this case and add the positive call there:

let matched = fallthrough!([a, b], [c, d]);
let fell_through = fallthrough!([], [a, b]);

(also the only new case in the file with no comment.)


crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 392 at r1 (raw file):

// broadcast to every capture of the `$v` group it encloses.
#[feature("user_defined_inline_macros")]
macro bcast {

bcast - the file spells it out as broadcast a few cases up, and here the variable is broadcast. deep_broadcast for the macro.


crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 417 at r1 (raw file):

// sum that the trailing `0` terminates.
#[feature("user_defined_inline_macros")]
macro sum {

sum a few cases below sums, on the same pattern as sums, flatten and nested - four macros in this file differing only in the expansion body, two of them one letter apart. rename at least, and the only thing this really adds over flatten is the trailing 0 and the empty non-final group - consider adding flatten!([], [3, 4]) to flatten instead of a fourth macro on that pattern.


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


//! > cairo_code
// `_` is not an identifier, so an `ident` placeholder does not match it. The first call, which the

every other case in this cluster records the rustc comparison in the comment itself (rustc's macro_rules! rejects the equivalent pattern with ...). this one doesn't, so the cross-check survives only in the commit message. add it:

// `_` is not an identifier, so an `ident` placeholder does not match it. The first call, which the
// rule does match, shows the rejection is the `_` and not the repetition itself; in the second call
// the offending element is not the first.
// rustc's `macro_rules!` rejects the equivalent call with
// `error: no rules expected reserved identifier '_'`.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 7cc0ce6 to fd19862 Compare August 16, 2026 11:37
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch 3 times, most recently from db92211 to 7796c97 Compare August 17, 2026 18:40
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 65c3f43 to 304290b Compare August 17, 2026 18:40

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7796c97. Configure here.

Comment thread crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros Outdated
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from 7796c97 to 4c77c15 Compare August 17, 2026 19:07

@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 5 comments and resolved 4 discussions.
Reviewable status: 0 of 2 files reviewed, 1 unresolved discussion (waiting on eytan-starkware+AGNT and TomerStarkware).


crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 279 at r1 (raw file):

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

name says every call delimiter, only bracketed and braced are called. add the paren call or drop "every":

let parenthesized = outer_delim!(1, 2);
let bracketed = outer_delim![1, 2];

Added the paren call.


crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 332 at r1 (raw file):

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

this is fallthrough above, one nesting level up - same behavior, a rule whose + matched nothing falls through to the next rule. the only thing it adds is a call that does match the + rule, which fallthrough should be carrying itself. drop this case and add the positive call there:

let matched = fallthrough!([a, b], [c, d]);
let fell_through = fallthrough!([], [a, b]);

(also the only new case in the file with no comment.)

Done - the deeper block is dropped and fallthrough carries matched/fell_through instances.


crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 392 at r1 (raw file):

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

bcast - the file spells it out as broadcast a few cases up, and here the variable is broadcast. deep_broadcast for the macro.

Renamed to deep_broadcast.


crates/cairo-lang-semantic/src/expr/expansion_test_data/inline_macros line 417 at r1 (raw file):

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

sum a few cases below sums, on the same pattern as sums, flatten and nested - four macros in this file differing only in the expansion body, two of them one letter apart. rename at least, and the only thing this really adds over flatten is the trailing 0 and the empty non-final group - consider adding flatten!([], [3, 4]) to flatten instead of a fourth macro on that pattern.

Half-taken: renamed to nested_sum, but the fold into flatten does not hold - flatten!([], [3, 4]) expands to (,3,4), a parse error; the trailing 0 termination is exactly what lets a non-final group be empty, and the comment now says so.


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

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

every other case in this cluster records the rustc comparison in the comment itself (rustc's macro_rules! rejects the equivalent pattern with ...). this one doesn't, so the cross-check survives only in the commit message. add it:

// `_` is not an identifier, so an `ident` placeholder does not match it. The first call, which the
// rule does match, shows the rejection is the `_` and not the repetition itself; in the second call
// the offending element is not the first.
// rustc's `macro_rules!` rejects the equivalent call with
// `error: no rules expected reserved identifier '_'`.

Added the rustc comparison line.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 304290b to df4f07e Compare August 23, 2026 08:43
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch 2 times, most recently from a55a4a6 to 3aaa1e4 Compare August 23, 2026 10:31
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from df4f07e to 96f2495 Compare August 23, 2026 10:31
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 96f2495 to e1b8458 Compare August 25, 2026 12:30
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from 3aaa1e4 to b578992 Compare August 25, 2026 12:30
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from e1b8458 to 817e9e3 Compare August 26, 2026 19:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from b578992 to 7abbd8e Compare August 26, 2026 19:59
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 817e9e3 to e2134ff Compare August 27, 2026 04:32
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch 2 times, most recently from 3f196cb to fc3616d Compare August 27, 2026 13:33
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from e2134ff to 8fd1650 Compare August 27, 2026 13:33
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from fc3616d to f7538ae Compare August 28, 2026 10:07
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 8fd1650 to b657e7f Compare August 28, 2026 10:07
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from f7538ae to 9b28d59 Compare August 28, 2026 12:58
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 4bc47fa to 42e0536 Compare September 14, 2026 04:51
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from 9b28d59 to 0e43809 Compare September 14, 2026 04:51
…th rustc

Tests only, pure append, zero production hunks and zero churn in either golden file.

Thirteen goldens pinning behaviors where Cairo's user-defined inline macros already match
rustc `macro_rules!`, so later work on the matcher and the expander cannot silently regress
them. Twelve in `expr/expansion_test_data/inline_macros` (expansion text) and one in
`expr/test_data/inline_macros` (a rejection, so a diagnostic).

Newly pinned, with the rustc 1.96.0 (ac68faa20 2026-05-25) output each expectation was
cross-checked against. Every rustc program transcribes through `stringify!`, so the
transcriber's own output is observed, and every one includes an invocation because rustc's
matcher and transcriber errors are lazy.

| golden | rustc program | rustc output |
| --- | --- | --- |
| bracketed call of a paren rule | `($($x:expr),*) => { stringify!(($($x,)*)) }`, `m![1, 2]` | `(1, 2,)` |
| braced call of a paren rule | same, `m!{1, 2}` | `(1, 2,)` |
| empty bracketed call of a paren rule | same, `m![]` | `()` |
| later rule after `+` matched nothing | `($([$($x:ident),+]),*) => { 1 }; ($([$($x:ident),*]),*) => { 2 };`, `m!([], [a, b])` | `2` |
| repeating rule ahead of an empty rule | `($($x:ident),+) => { 1 }; () => { 2 };`, `m!(a, b)` | `1` |
| empty call taking the empty rule | same, `m!()` | `2` |
| expansion operator differing from the pattern's | `($($x:expr),*) => { stringify!((0, $($x,)?)) }`, `m!(1, 2)` | `(0, 1, 2,)` |
| the same over no captures at all | same, `m!()` | `(0,)` |
| placeholder broadcast into a deeper block | `($(($k:expr, [$($v:expr),*])),*) => { stringify!(($($(($k * $v),)*)*)) }`, `m!((10, [1, 2]), (20, [3, 4]))` | `((10 * 1), (10 * 2), (20 * 3), (20 * 4),)`, value `(10, 20, 60, 80)` |
| the same with a non-final group of no captures | same, `m!((10, []), (20, [3, 4]))` | `((20 * 3), (20 * 4),)`, value `(60, 80)` |
| doubly nested block flattened into one sum | `($([$($x:expr),*]),*) => { stringify!($($($x +)*)* 0) }`, `m!([1, 2], [3, 4])` | `1 + 2 + 3 + 4 + 0`, value `10` |
| the same with a non-final group matching nothing | same, `m!([], [3, 4])` | `3 + 4 + 0`, value `7` |
| `_` not matched by an ident placeholder | `($($x:ident),*) => { 7 }`, `m!(a, _, b)` | `error: no rules expected reserved identifier '_'` with `note: while trying to match meta-variable $x:ident` |

Cairo's blessed expansion text is the same token sequence as rustc's in every row; it
differs only in the spacing around a spliced placeholder (`1+2+3+4+0`, `((10* 1),...`),
which is the known trivia loss around `$x` and not a difference in what was spliced. Every
value the text evaluates to is the value rustc computes.

Test shape, per the plan's rules: every repetition level in every new case holds two
captures; the offending or empty element is the non-final one wherever the case has one
(`[]` first in the fallthrough, `sum` and `bcast` cases, `_` second in the `_` case); each
behavior has an explicit zero-match or empty-group case; and the `_` case calls the macro
once with a matching argument list and once with `_`, so its single E2158 shows the
rejection is the `_` rather than the repetition.

Because these pin existing behavior there is no fix to revert, so each expected block was
shown load-bearing by corrupting it and observing the failure. All thirteen fail as an
`expanded_code` / `expected_diagnostics` mismatch on exactly their own case - never a panic
and never a diagnostics-expectation error.

Two throwaway production perturbations, reverted and not part of this commit, additionally
show the pins are sensitive to the behavior they name rather than to arbitrary text:

* Making `is_macro_rule_match` return `None` for a braced or bracketed call reddens exactly
  the three outermost-delimiter cases and nothing else in the file.
* Capping an expansion repetition at one group when its operator is `?` reddens exactly the
  differing-operator case and nothing else in the file.

Not pinned, with reasons:

* Two cross-check rows are not expressible on this base. A transcriber separator that
  differs from the pattern's needs a non-comma separator, which the grammar does not have
  (`($($x:ident),*) => { ($($x);*) }` is an E1031 parse error), and rustc's metavariable
  expressions are still unstable there, so Cairo's rejection of `${count(x)}` is not
  agreement with a rustc behavior that exists.
* Nine rows already had a golden and were only re-verified on this base.
* No E2199 sibling-zip case is added; that behavior needs its own design decision.

`$($($x +)*)* 0` is pinned as the accepting expansion it is in rustc, not as an error.

Gates: `cargo test --profile=ci-dev -p cairo-lang-semantic` (106 passed),
`./scripts/rust_fmt.sh --check`, `./scripts/clippy.sh --profile=ci-dev`,
`./scripts/validate_error_codes.sh`,
`cargo run --profile=ci-dev --bin cairo-test -- tests/bug_samples --starknet` (68 passed),
and `cargo run --profile=ci-dev --bin cairo-test -- corelib/` (737 passed) - the last
because no other gate compiles `corelib/src/test/`.
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr4b-expr-follow-set-f7 branch from 42e0536 to e20bda1 Compare September 14, 2026 06:56
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/matcher-correctness.pr5-regression-pins branch from 0e43809 to 17e644e 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