Skip to content

fix(semantic): reject a trailing macro repetition separator like rustc - #10313

Open
orizi wants to merge 1 commit into
graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansionfrom
graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13
Open

fix(semantic): reject a trailing macro repetition separator like rustc#10313
orizi wants to merge 1 commit into
graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansionfrom
graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13

Conversation

@orizi

@orizi orizi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

A repetition's separator only ever stands between two consecutive groups,
so the matcher now consumes it together with the group that follows it,
rather than after the group that precedes it. A separator that no group
follows is no longer absorbed: it is left in the input for whatever comes
after the repetition to match, and if nothing does, the rule does not
match.

This aligns Cairo with rustc's macro_rules!, which rejects m!(1, 2,)
against ($($x:expr),*) with "unexpected end of macro invocation". It is
a behavior break in both directions, since it can also flip a call from
one rule to another: against a macro whose first rule is
($($x:expr),*) and whose second is ($($x:expr,)*), m!(1, 2,) used
to stop at rule 1 and now falls through to rule 2 - which is what rustc
picks too.

Trailing-separator tolerance is now spelled explicitly, as in rustc:
either $($x:expr,)*, putting the separator in the group body, or
$($x:expr),* $(,)?. The four corelib call sites that relied on the
absorbed separator are moved onto a macro that asks for it that way.

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

orizi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from fe3b861 to 82bf327 Compare August 4, 2026 08:41
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 25948c8 to 0c024e9 Compare August 4, 2026 08:41
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from 82bf327 to 7002ac1 Compare August 4, 2026 08:56
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 0c024e9 to ec48061 Compare August 4, 2026 08:56
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from 7002ac1 to 96e7d61 Compare August 5, 2026 10:54
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from ec48061 to 0be1ab1 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

Medium Risk
This is a deliberate behavior break in user-defined inline macro matching in the semantic layer; macros that relied on absorbed trailing separators or implicit rule choice may fail or pick a different rule at compile time.

Overview
Inline macro repetition matching now treats separators as between groups only: from the second group onward the matcher consumes the separator before the next group, and it no longer eats a separator after the last group. Trailing commas or semicolons are left in the input, so rules like ($($x:ident),*) fail on m!(a, b,) instead of absorbing the extra token.

That matches rustc’s macro_rules! and can change which rule wins when one pattern uses a repetition separator and another bakes it into each group (e.g. m!(1, 2,) now matches ($($x:expr,)*) rather than ($($x:expr),*)). Optional trailing separators are spelled explicitly, e.g. $($x:expr),* $(,)? or ($($x:expr,)*).

Tests and corelib macro examples were updated: repetition matcher tests no longer expect trailing separators on matcher_star!; a dedicated matcher_trailing_comma! macro documents the rustc-style pattern.

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

@orizi
orizi changed the base branch from graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion to graphite-base/10313 August 5, 2026 11:58
@orizi
orizi force-pushed the graphite-base/10313 branch from 96e7d61 to a9ecb49 Compare August 5, 2026 15:15
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 0be1ab1 to 36bb10c Compare August 5, 2026 15:15
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 36bb10c to 7905f7c Compare August 6, 2026 11:33
@orizi
orizi force-pushed the graphite-base/10313 branch from a9ecb49 to 5300e32 Compare August 6, 2026 11:33
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 7905f7c to 52d9322 Compare August 12, 2026 20:54
@orizi
orizi changed the base branch from graphite-base/10313 to graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion August 12, 2026 20:55
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from 2748956 to 53e7e0f Compare August 16, 2026 10:04
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 52d9322 to 07e10f2 Compare August 16, 2026 10:04

@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 4 comments.
Reviewable status: 0 of 4 files reviewed, 3 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/items/macro_declaration.rs line 1000 at r2 (raw file):

                let mut match_count = 0;
                loop {
                    let mut inner_ctx = ctx.clone();

the check belongs above the ctx.clone(), and temp_iter is thrown away on break so peek() buys nothing over next(). as written every repetition pays one extra full MatcherContext clone on the terminating iteration.

                    let mut temp_iter = input_iter.clone();
                    // A separator only ever stands *between* two groups, so from the second group
                    // on it has to be consumed before the group - and only together with it. A
                    // separator that no group follows is not part of the repetition, and is left
                    // in the input for whatever comes after it to match, exactly as rustc's
                    // `macro_rules!` does.
                    if match_count > 0
                        && let Some(expected_sep) = &expected_separator
                    {
                        let Some(ast::TokenTree::Token(token_leaf)) = temp_iter.next() else {
                            break;
                        };
                        if token_leaf.as_syntax_node().get_text_without_trivia(db) != *expected_sep
                        {
                            break;
                        }
                    }
                    let mut inner_ctx = ctx.clone();

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

                    // in the input for whatever comes after it to match, exactly as rustc's
                    // `macro_rules!` does.
                    if match_count > 0

this puts the formatter and the matcher in direct contradiction.

cairo-format routes every macro-call token tree through the arg-list rules (formatter_impl.rs:960 -> token_tree_as_wrapped_arg_list), and ArgList sets set_comma_if_broken() (node_properties.rs:643). so when a call is long enough to break over lines the formatter adds a trailing comma - see the blessed array![...] output in crates/cairo-lang-formatter/test_data/expected_results/linebreaking.cairo:117, where the input had none.

so after this change, running the formatter over

m!(some_long_arg_one, some_long_arg_two, some_long_arg_three, some_long_arg_four, some_long_arg_five);

against ($($x:expr),*) turns compiling code into No matching rule found in inline macro.

and the other direction, which this PR's own test documents (test_data/inline_macros:3010, "the formatter strips a trailing comma from a single-line macro call"), used to be a no-op and now changes the program: stripping the comma is exactly the u16 -> u8 rule flip pinned at test_data/inline_macros:4012.

the two #[cairofmt::skip]s in this PR are the symptom, not the fix. either the formatter stops touching trailing separators inside macro calls (base PR), or this lands as a footgun where formatting breaks the build.


corelib/src/test/language_features/macro_test.cairo line 215 at r2 (raw file):

    // A separator only ever stands between two groups, so a trailing one is not part of the
    // repetition and the calls above reject it - as rustc's `macro_rules!` does. A macro that wants

"the calls above reject it" - there are no calls above any more, you removed them (matcher_plus!(1,), matcher_star!(3,), ...). say it about the macros, not the calls.

@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from 53e7e0f to 60c1d28 Compare August 16, 2026 11:37
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from 07e10f2 to 3c518b8 Compare August 16, 2026 11:37
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from 60c1d28 to cb444e0 Compare August 17, 2026 18:03
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch 2 times, most recently from fac6c52 to e02e2e6 Compare August 17, 2026 18:40
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from cb444e0 to cbd3fa7 Compare August 17, 2026 18:40
A repetition's separator only ever stands between two consecutive groups,
so the matcher now consumes it together with the group that follows it,
rather than after the group that precedes it. A separator that no group
follows is no longer absorbed: it is left in the input for whatever comes
after the repetition to match, and if nothing does, the rule does not
match.

This aligns Cairo with rustc's `macro_rules!`, which rejects `m!(1, 2,)`
against `($($x:expr),*)` with "unexpected end of macro invocation". It is
a behavior break in both directions, since it can also flip a call from
one rule to another: against a macro whose first rule is
`($($x:expr),*)` and whose second is `($($x:expr,)*)`, `m!(1, 2,)` used
to stop at rule 1 and now falls through to rule 2 - which is what rustc
picks too.

Trailing-separator tolerance is now spelled explicitly, as in rustc:
either `$($x:expr,)*`, putting the separator in the group body, or
`$($x:expr),* $(,)?`. The four corelib call sites that relied on the
absorbed separator are moved onto a macro that asks for it that way.
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-trailing-f13 branch from e02e2e6 to e367efd Compare August 17, 2026 19:07
@orizi
orizi force-pushed the graph-plan/2026-08-03-macro-fixes/separator-grammar.sep-matcher-expansion branch from cbd3fa7 to 55f3321 Compare August 17, 2026 19:07
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