feat(lint): W018 — e.kind compared against an out-of-set error kind (#469)#525
Merged
Merged
Conversation
| obj->data.ident.name, (op[0] == '=' ? "==" : op), lit, sugg); | ||
| } | ||
|
|
||
| static void w018_scan(ASTNode *n, LintContext *ctx, W018Scope *sc) { |
…469) A catch handler comparing a caught error's `.kind` against a string that is a near-miss of a real kind — a case variant ("IO"), a single-character typo ("index_rage"), or a kind renamed out from under the handler — is dead code that silently never fires. W018 warns and suggests the intended kind (the silent-tolerance class the lint train fences). Zero-false-positive by construction, three gates: 1. the `.kind` must be read off a CATCH-BOUND variable (tracked through the walk via trycatch.err_name), so `.kind` on an unrelated user dict never fires; 2. an exactly-valid kind is silent; 3. only a near-miss fires — a case variant, or a bounded-Levenshtein edit distance of 1 — so a genuinely custom `throw {kind: "..."}` value, many edits from every builtin, stays silent. The closed kind set is derived from err_kind_name(EK_INTERNAL..EK_USER) at run time — no hand list to drift, and it already covers the post-#509 `deadlock` kind (verified: a valid `deadlock` comparison stays silent). Runs inside the shared lint_run_checks, so it also flows to the LSP. Calibrated to ZERO hits over lib/, the test corpus, examples/, and the consumer repos (ouroboros/liferaft/tidelog/DMG/Tidepool). Regression: tests/test_lint.sh — fires on index_rage (+ suggests index_range) and the IO case variant; silent on valid kinds incl. deadlock, a far-off custom kind, and .kind off a non-catch var. Full suite green release + ASan (leaks on), 2700/2700, no leak-tally increase. Closes #469 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
40e67ac to
35b34a3
Compare
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.
What
A
catchhandler comparing a caught error's.kindagainst a string that is a near-miss of a real kind — a case variant ("IO"), a single-character typo ("index_rage"), or a kind renamed out from under the handler — is dead code that silently never fires.W018warns and suggests the intended kind — the silent-tolerance class the lint train exists to fence.Zero-false-positive by construction
Three gates, all required to fire:
.kindis read off a catch-bound variable (tracked through the walk viatrycatch.err_name) —.kindon an unrelated user dict never fires;throw {kind: "..."}value, many edits from every builtin, stays silent.The closed kind set is derived from
err_kind_name(EK_INTERNAL..EK_USER)at run time — no hand list to drift when a kind is added, and it already covers the post-#509deadlock(verified: a validdeadlockcomparison stays silent). The check lives in the sharedlint_run_checks, so it also flows to the LSP.Calibration: zero hits over
lib/, the test corpus,examples/, and the consumer repos (ouroboros/liferaft/tidelog/DMG/Tidepool).Verification
tests/test_lint.sh(6 new cases): fires onindex_rage(+ suggestsindex_range) and theIOcase variant; silent on valid kinds incl.deadlock, a far-off custom kind, and.kindoff a non-catch var.detect_leaks=1): 2700/2700, 0 failed, no leak-tally increase.Docs
docs/DIAGNOSTICS.md:W018row in the W-code table.CHANGELOG.md: entry under[Unreleased] → Added.Closes #469
🤖 Generated with Claude Code