Dim the code that cannot be reached - #430
Open
petrovo-as wants to merge 1 commit into
Open
Conversation
petrovo-as
force-pushed
the
diagnostics-unreachable-code
branch
from
September 8, 2026 14:40
5aca710 to
b23db4f
Compare
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
A statement after one that always leaves the block never runs, and nothing said so. `return`, `throw`, `exit`, `die`, `continue`, `break`, and an `if` whose every branch does one of those all end a block; an `if` without an `else` never does, since a path through it always falls through. Reported as a Hint tagged `Unnecessary`, so editors grey the text out rather than underlining it — dead code is tidying, not a defect, and it is rendered the way an unused import already is. The check reads the shape of the statement list and nothing else, which keeps it in the fast phase alongside the syntax and unused-symbol checks. Two things only look dead. A declaration at the top level of a file is hoisted, so it holds whether or not control reaches the line it is written on, while the same declaration inside a function body is created by running the statement and after a `return` never comes into being at all — the two are told apart by where they sit rather than by what they are. A `goto` label is an entry point, so it ends the dead run rather than being swallowed by it. Either can sit in the middle of otherwise dead code, so one block may hold several runs and each is reported on its own. A call to a function declared `never` also ends a block. Recognising it takes the type engine, which would move this check into the expensive phase to catch a case the reader can already see, so it is left to the narrowing code that already has the types in hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
petrovo-as
force-pushed
the
diagnostics-unreachable-code
branch
from
September 8, 2026 14:58
b23db4f to
c65dfc6
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.
Note
I am not a Rust developer. This was written with AI assistance and reviewed line by line, including two rounds of adversarial review that between them caught two false positives around
goto: one where a jump was treated as leaving the block it stayed inside, and one where areturnthe jump skipped over dimmed the live code after that block. Please read it as a proposal rather than as finished work.The problem
A statement after one that always leaves the block never runs, and nothing said so. This is D6 in the backlog, which the change implements and removes.
The change
return,throw,exit,die,continue,break, and anifwhose every branch does one of those all end a block. Anifwithout anelsenever does, since a path through it always falls through.Reported as a Hint tagged
Unnecessary, per D6, so editors grey the text out rather than underlining it. The check reads the shape of the statement list and nothing else, which keeps it in the fast phase.Two things only look dead. A declaration at the top level of a file is hoisted and holds wherever it sits, while the same declaration inside a function body is created by running the statement and after a
returnnever comes into being — they are told apart by where they sit, not by what they are. Agotolabel is an entry point that ends the dead run. Either can sit in the middle of otherwise dead code, so one block may hold several runs, each reported on its own.Not included
A call to a function declared
neveralso ends a block. Recognising it needs the type engine, which would move the check into the expensive phase to catch a case the reader can already see.Two gaps are filed rather than fixed, as D24 and D25: the "Remove unreachable code" action reads PHPStan's diagnostics only and deletes to the next closing brace instead of using the reported range, so wiring the native code to it needs more than adding the code; and a braced
namespaceordeclarebody is treated as a fresh statement list, so reachability neither flows in nor out.Testing
36 integration tests in
diagnostics_unreachable_code.rs, asserting on the text that was dimmed rather than on line numbers. Demos added toexamples/php/diagnostics.php. Full suite,clippy --all-targets -D warnings, andfmt --checkpass.Checklist
If applicable:
CHANGELOG.mdREADME.md,docs/,examples/)config-schema.json)how it works, how it's organized), including any code drafted by an LLM.
an eye towards deleting anything that is irrelevant, clarifying anything
that is confusing, and adding details that are relevant. This includes,
for example, commit descriptions, PR descriptions, and code comments.
🤖 Generated with Claude Code