Sitelet https://github.com/starkware-libs/cairo/pull/10227
Skip to content

fix(parser): stop block recovery at a following control-flow statement - #10227

Merged
orizi merged 1 commit into
mainfrom
orizi/07-21-fix_parser_stop_block_recovery_at_a_following_control-flow_statement
Jul 21, 2026
Merged

orizi merged 1 commit into
mainfrom
orizi/07-21-fix_parser_stop_block_recovery_at_a_following_control-flow_statement

Conversation

@orizi

@orizi orizi commented Jul 21, 2026 •

Copy link
Copy Markdown
Collaborator

TL;DR

Improved parser error recovery for broken if statement headers to prevent consuming subsequent control-flow statements as part of the if body.

What changed?

The block! recovery macro now includes if, while, loop, and for as recovery stop tokens, in addition to the existing let, match, and return. This ensures that when the parser encounters a malformed if header (e.g., if a == with a missing right-hand expression), it stops recovery at the next control-flow keyword rather than consuming it as part of the broken if block.

Additionally, the diagnostic reported for a missing { after a broken if header was changed from a "Skipped tokens" error to a more precise "Missing token '{'" error, with a corresponding "Missing token '}'" error to close the implicit block.

How to test?

The existing parser diagnostic test suite covers this behavior. A new test case was added verifying that a broken if header followed by a while statement correctly produces missing-token diagnostics without swallowing the while as the if body:

fn f(a: felt252, b: bool) {
    if a ==
    while b { let _x = 1; }
    let _y = 2;
}

Why make this change?

Previously, a malformed if header would cause the parser to greedily consume the { ... } block of a subsequent control-flow statement (e.g., while, loop, for) as the body of the broken if. This led to confusing diagnostics and incorrect AST structure. By treating control-flow keywords as recovery boundaries, the parser now handles these cases more gracefully and produces cleaner, more accurate error messages.

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

orizi commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

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

@orizi
orizi marked this pull request as ready for review July 21, 2026 09:37
@cursor

cursor Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Localized parser error-recovery and diagnostic test updates; no runtime semantics or security-sensitive paths.

Overview
Block recovery boundaries in recovery.rs are expanded so the block! stop set includes break, continue, if, while, loop, and for in addition to let, match, and return. When an if header is incomplete (e.g. if a ==), recovery no longer treats a following control-flow statement’s { ... } as the broken if body—the next keyword stays a separate statement.

Diagnostics for a nested if in a condition now report missing { / } instead of a generic “Skipped tokens” expectation for the missing brace after the bad header.

Tests add cases for a broken if followed by while and for if { while b {} }, locking in the new recovery and error shapes.

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

@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 reviewed 2 files and all commit messages, and made 2 comments.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on orizi and TomerStarkware).


crates/cairo-lang-parser/src/recovery.rs line 108 at r1 (raw file):

            | SyntaxKind::TerminalMatch
            | SyntaxKind::TerminalReturn
            | SyntaxKind::TerminalIf

If we have return, what about continue?


crates/cairo-lang-parser/src/parser_test_data/diagnostics/if line 348 at r1 (raw file):

           ^

error[E1001]: Missing token '{'.

Add a test for bad if but good braces:
if {
while ....
}

The `block!` stop-set used by `parse_block`'s `skip_until` only listed
`let`/`match`/`return`. When recovering a broken block-owner header (e.g. an
`if` with a malformed condition), recovery walked past a subsequent, fully
valid `if`/`while`/`loop`/`for` statement's header and consumed its `{ ... }`
as the broken construct's body — silently deleting that statement from the AST
and reparenting its block, with no diagnostic pointing at it.

Add `TerminalIf`/`TerminalWhile`/`TerminalLoop`/`TerminalFor` to `block!` so
recovery halts at the next control-flow statement instead of swallowing it. The
broken block-owner now gets a synthesized empty body (missing `{`/`}`
diagnostics) and the following statement is preserved.

Updates the existing `diagnostics/if` "if inside if condition" golden (recovery
now keeps the inner `if`-expression as its own statement) and adds a regression
case covering a broken `if` header followed by a `while`.
@orizi
orizi force-pushed the orizi/07-21-fix_parser_stop_block_recovery_at_a_following_control-flow_statement branch from aaf222c to e1d0ab3 Compare July 21, 2026 10:20

@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 made 2 comments.
Reviewable status: 0 of 2 files reviewed, 2 unresolved discussions (waiting on eytan-starkware and TomerStarkware).


crates/cairo-lang-parser/src/recovery.rs line 108 at r1 (raw file):

Previously, eytan-starkware wrote…

If we have return, what about continue?

Done.


crates/cairo-lang-parser/src/parser_test_data/diagnostics/if line 348 at r1 (raw file):

Previously, eytan-starkware wrote…

Add a test for bad if but good braces:
if {
while ....
}

Done.

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

:lgtm:

@eytan-starkware reviewed 2 files and all commit messages, made 1 comment, and resolved 2 discussions.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on TomerStarkware).

@orizi
orizi added this pull request to the merge queue Jul 21, 2026
Merged via the queue into main with commit 1ed257a Jul 21, 2026
55 checks passed
@orizi
orizi deleted the orizi/07-21-fix_parser_stop_block_recovery_at_a_following_control-flow_statement branch July 21, 2026 14:22
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