Skip to content

Recover from mismatched loop terminators with quick fixes#1696

Open
chrisdp wants to merge 3 commits intomasterfrom
feature/while-next-recovery
Open

Recover from mismatched loop terminators with quick fixes#1696
chrisdp wants to merge 3 commits intomasterfrom
feature/while-next-recovery

Conversation

@chrisdp
Copy link
Copy Markdown
Contributor

@chrisdp chrisdp commented May 3, 2026

Summary

When a user writes a common loop-terminator mistake, the parser used to emit two cascading diagnostics that didn't connect the user's intent to the actual problem. This PR adds error recovery in three loop-parsing paths and a single parameterized diagnostic with a structured data payload that drives the quick fixes.

Before

while n <= 3
    n = n + 1
next

emitted:

  • Unexpected token 'next'
  • Could not find matching 'end while'

After

Same input emits one diagnostic:

  • Expected 'end while' but found 'next' (code 1147)

with a quick fix Convert 'next' to 'end while'.

Cases handled

Input Diagnostic message Quick fix(es)
while ... next Expected 'end while' but found 'next' Convert 'next' to 'end while'
for ... end while Expected 'end for' or 'next' but found 'end while' Convert 'end while' to 'end for' (preferred), Convert 'end while' to 'next'
for each ... end while same as for same two fixes

When the same mistake appears multiple times in a file, fix-all variants are also offered.

Implementation

  • New mismatchedEndingToken(expected: string[], found: string) diagnostic (code 1147). The expected array is preferred-first, and both fields are kept on diagnostic.data so consumers can build fixes mechanically rather than dispatching on per-pair codes.
  • whileStatement accepts Next as a recovery terminator; consumes it on the bogus path and stores it on WhileStatement.tokens.endWhile so quick fixes can target the token's range. Calls mismatchedEndingToken(['end while'], 'next').
  • forStatement and forEachStatement accept EndWhile as a recovery terminator; same shape. They call mismatchedEndingToken(['end for', 'next'], 'end while'). forEachStatement also refactored from "throw on missing terminator" to recover-and-continue.
  • CodeActionsProcessor.suggestMismatchedEndingTokenQuickFixes reads expected[]/found from the diagnostic and emits one fix per legal terminator (first entry is isPreferred). No per-pair handler.
  • New isDiagnosticOfType(diagnostic, key) type guard refines BsDiagnostic to the concrete DiagnosticMessageType<K> when its code matches, used by the code-action dispatcher.

Test plan

  • npm test — 2838 passing, 0 failing
  • npm run lint — clean
  • Parser specs assert the new diagnostic code and data: { expected, found } payload for while, for, and for each
  • Code-action specs cover single-fix, two-fix (with preferred), and fix-all variants

When a `while` block has no matching `end while` and the next dangling
terminator is a `next`, treat it as a malformed `end while`. Symmetric
treatment for `for`/`for each` blocks closed with a stray `end while`.

The previous behavior was a cascade of two unhelpful diagnostics
(`Unexpected token` + `Could not find matching end <kw>`) per case.
Now each case produces one targeted diagnostic:
- 1147 `Expected 'end while' but found 'next'`
- 1148 `Expected 'end for' or 'next' but found 'end while'`

Quick fixes are attached to each diagnostic (with fix-all variants
when the same mistake appears multiple times in the file):
- `while ... next` → `Convert 'next' to 'end while'`
- `for ... end while` → `Convert 'end while' to 'end for'` (preferred)
                       or `Convert 'end while' to 'next'`
- `for each ... end while` → same two fixes as `for`
Comment thread src/DiagnosticMessages.ts Outdated
Comment thread src/DiagnosticMessages.ts Outdated
chrisdp added 2 commits May 5, 2026 16:22
…recovery

# Conflicts:
#	src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
Replace whileLoopTerminatedWithNext (1147) and forLoopTerminatedWithEndWhile
(1148) with a single mismatchedEndingToken diagnostic that carries
{ expected, found } on its data payload. Code actions read the structured
data to build "Convert '<found>' to '<expected[i]>'" fixes, so adding new
recovery sites doesn't require new diagnostic codes or new handlers.

Also adds isDiagnosticOfType type guard for refining BsDiagnostic to a
specific DiagnosticMessageType<K>.
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.

2 participants