Skip to content

Move parse error recovery for expression operators "out of line" & refactor in the area - #162591

Open
fmease wants to merge 9 commits into
rust-lang:mainfrom
fmease:out-of-line-recovery
Open

Move parse error recovery for expression operators "out of line" & refactor in the area#162591
fmease wants to merge 9 commits into
rust-lang:mainfrom
fmease:out-of-line-recovery

Conversation

@fmease

@fmease fmease commented Sep 10, 2026

Copy link
Copy Markdown
Member

Background: While we do have 3k-line module rustc_parse/src/parser/diagnostics.rs1 dedicated to syntax error diagnostics & parse error recovery, the rest of the parser is still "littered" or "interwoven" through and through with complex or verbose diagnostic code. We support numerous recoveries from syntaxes found in other languages right next to code that actually decides what is and what isn't part of Rust syntactically. This makes the parser code very messy, illegible, bug prone and otherwise hard to maintain.

So my long-term plan is to push all this diagnostic & recovery code "out of line", namely into new rustc_parse/src/parser/$fragment/error.rs files, to keep the "inline code" of parsing routines in $fragment.rs focused on actually parsing Rust code.

Why not just use the pre-existing parser/diagnostics.rs module? Well, making diagnostic code for expressions, patterns, types etc. share the same module is the definition of a hodgepodge. Moreover, moving all diagnostic code there would make the file way too large. Of course, if there turns out to be common code, a new parser/errors.rs would do the trick2.

Best reviewed commit by commit.

Commit Refactor check_assoc_op to make it more legible was cherry-picked from my PR #161775.

(No LLM was or will be used by me during the entire creation process of this PR)

Footnotes

  1. Not to be confused with rustc_parse/src/diagnostics.rs which holds diagnostic structs.

  2. I'd like to see parser/diagnostics.rs gone since its name makes it easy to confuse with the module that holds the diagnostic structs & since diagnostics/{mod,impl}.rs isn't an option here (cc MCP 1003).

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 10, 2026
@rustbot

rustbot commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

r? @JohnTitor

rustbot has assigned @JohnTitor.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler, parser
  • compiler, parser expanded to 76 candidates
  • Random selection from 21 candidates

@fmease
fmease force-pushed the out-of-line-recovery branch from f26a1f9 to 4b51f2d Compare September 10, 2026 12:09
@fmease fmease added the C-cleanup Category: PRs that clean code up or issues documenting cleanup. label Sep 10, 2026
…value

These functions didn't actually modifiy the operand or return a new or
different expression. So essentially the "`fn(Box<Expr>) -> Box<Expr>`
part" was an identity function. Just change it to "fn(&Expr)".
@fmease
fmease force-pushed the out-of-line-recovery branch from 4b51f2d to 55f7d6b Compare September 11, 2026 08:12
`recover_from_inc_dec` *always* returns a (fatal) `Err(_)` *except* if
the increment/decrement operator is a subexpression *and* the source of
the operand is not available in which case it emits the diagnostic and
returns `Ok(_)` (rendering it non-fatal).

This makes no sense whatsoever. For illustration purposes, listed below
are steps that would make us reach this case:

1. `rustc a.rs --crate-type=lib` where `a.rs` contains:
   `#[macro_export] macro_rules! m { () => { i++ } }`.
2. Move or remove `a.rs`
3. `rustc b.rs --edition 2018 --extern a -L.` where
   `b.rs` contains:
   `fn main() { (a::m!()); }`.

Just make the error unconditionally fatal and add a FIXME to make it non
fatal in the future which would allow us to report name resolution errors
and what not. However, since that would be slightly more involved and
represent a behavior change (in the error path), this is out of scope for
a mere cleanup commit like this one.
There's literally no upside to use it and only downsides:
It's not more concise, only adds code and obfuscates.
Its `MultiSugg::emit{,_verbose}` didn't even *emit* the diagnostic,
they merely *decorated* it!
@fmease
fmease force-pushed the out-of-line-recovery branch from 55f7d6b to 129a2b9 Compare September 11, 2026 08:55
1. Remove unnecessary rebindings (`op_span` and `op = op.node`)
2. Remove binding `cur_op_span` as it's equal to `op.span`
3. Merge two `match`es on `op.node` into one to make the control flow
   more obvious and to render everything more legible. Moreover,
   it allows us to drop an ungly `unreachable!()`
Previously we would check if the current operator was `Binary(Lt)` and
the current token was `>` to determine if we're looking at `<>`.

However, since `AssocOp::from_token` also treats `<-` as `Binary(Lt)`
for better error recovery, the condition would also hold for `<->`
(`<-`, `>`) which is not what we want. E.g., given `1 <-> 2` we would
previously emit diagnostic "invalid comparison operator `<>`".

---

Also update `recover_from_spaceship_cmp_op` to do something similar --
not to fix anything but simply to eliminate param `op: Spanned<AssocOp>`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-cleanup Category: PRs that clean code up or issues documenting cleanup. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants