Quality review + bugfix: v0.3 surface#55
Merged
Conversation
The didOpen / didChange diagnostics path went straight to diagnostics_for without the _safe wrapper the request handlers use, so a buffer nested deep enough to exhaust the recursion limit (a clean parse the recursive reference collector walks) raised RecursionError out of the handler — contradicting the module's "can never crash the connection" guarantee. Wrap refresh_diagnostics in _safe so it publishes an empty list instead. format_edits had the same gap: it caught only (ValueError, TypeError), letting the formatter's RecursionError escape its documented "yields no edit" contract; catch RecursionError too. Regression tests cover both: a failing analysis still publishes [], a pathologically deep document survives didOpen, and format_edits returns no edit on deep nesting.
Drop the #19 / #20 / #21 issue numbers from the shipped module docstrings and comments (kept out of source per project convention), and correct the rules docstring that claimed nine structural checks — there are eight. Close the four remaining partial branches to 100% line + branch on the v0.3 surface: mark the three grammar-guaranteed is-not-None guards in the reference collector with pragma: no branch, and add a test exercising the value_references cache hit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
End-of-milestone quality review of the v0.3 surface (field catalogue, linter, pygls LSP, VS Code extension). Every formal gate was already green —
ruff check,ruff format --check,mypy --strict, and 2209 tests; the catalogue (102 types / 2614 fields), the linter's zero-false-positive bar on the stock R2026a corpus, and the extension's id/scopeName/launch wiring all hold. An adversarial review across the surface surfaced two real robustness gaps the corpus and feature tests miss, both fixed here with regression coverage, plus hygiene and a coverage close-out:refresh_diagnostics(thedidOpen/didChangepath) went straight todiagnostics_forwithout the_safewrapper the request handlers use. A buffer nested deep enough to exhaust the recursion limit parses cleanly, so the linter's recursive reference collector walks it and raisesRecursionErrorout of the notification handler — contradicting the module's documented "can never crash the connection" guarantee (the existing "survive malformed input" tests only feed syntax-error buffers).refresh_diagnosticsnow runs through_safeand publishes an empty list instead.format_editsleakedRecursionError. It caught only(ValueError, TypeError), so the whole-document formatter'sRecursionErroron a deeply nested expression escaped its documented "yields no edit" contract. It now catchesRecursionErrortoo.#19/#20/#21issue numbers from shipped module docstrings/comments (kept out of source per convention), and corrected therules.pydocstring that claimed nine structural checks — there are eight.pragma: no branchon the three grammar-guaranteedis not Noneguards in the reference collector (unreachable on a clean parse, which is the only path the linter walks), and a test exercising thevalue_referencescache hit.Out of scope (noted, not changed): the underlying recursion in
format()and the reference collector is left intact — the LSP boundary is what must stay up, and rewriting those walkers iteratively is a larger change than this review warrants. A library caller passing a 500-deep nested expression still gets aRecursionError, which is acceptable.Test plan
uv run pytest— 2213 passed (4 new regression tests)uv run pytest --cov=gmat_script— 100% line + branch overall, includingcatalog.py,lint/, andlsp/uv run mypy— clean (src + tests, strict)uv run ruff checkanduv run ruff format --check— cleanCloses #24