refactor(minifier): extract the class removability decision into a classifier#24386
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Merging this PR will not alter performance
Comparing Footnotes
|
6893f78 to
0e84107
Compare
b97c045 to
7d33363
Compare
0e84107 to
984ae8d
Compare
4e88e2b to
cdc877f
Compare
Merge activity
|
|
Considering this is a pure refactor and makes the code cleaner, I will merge it first. |
…assifier (#24386) `remove_unused_class` decides inline whether an unused class can be removed and what its removal leaves behind: side-effectful heritage or computed keys and present static values get extracted into the surrounding code. The next PR in this stack adds a liveness analysis that has to make the same decision at a different place and time, and the two decisions must agree exactly — a class the analysis treats as cleanly removable must not extract anything at the removal site, or the extracted expressions would reference declarations that were removed together with it. This moves the decision into one classifier, `classify_class_removability`, which reports `Keep`, `Extracts`, or `RemovesClean`. `remove_unused_class` classifies once up front; `RemovesClean` skips the extraction walk it cannot need. The `Extracts` path still re-runs the heritage and computed-key purity checks in its extraction loop — the classifier is shared with callers that never extract, so it reports no extraction plan; that path only runs when an `Extracts` class is actually removed. `remove_unused_expression` now also routes through `expr_has_specialized_unused_handler`, so the list of handlers that can leave residue cannot drift from the dispatch. The same analysis relies on that list to know which initializers are dropped whole. A new specialized arm added without a predicate entry becomes dead code that its author's own tests catch; a predicate entry without a dispatch arm hits an `unreachable!`. No behavior change. Minified output and iteration counts are byte-identical on every fixture; 553 unit tests pass. Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy.
cdc877f to
c415c23
Compare

remove_unused_classdecides inline whether an unused class can be removed and what its removal leaves behind: side-effectful heritage or computed keys and present static values get extracted into the surrounding code. The next PR in this stack adds a liveness analysis that has to make the same decision at a different place and time, and the two decisions must agree exactly — a class the analysis treats as cleanly removable must not extract anything at the removal site, or the extracted expressions would reference declarations that were removed together with it.This moves the decision into one classifier,
classify_class_removability, which reportsKeep,Extracts, orRemovesClean.remove_unused_classclassifies once up front;RemovesCleanskips the extraction walk it cannot need. TheExtractspath still re-runs the heritage and computed-key purity checks in its extraction loop — the classifier is shared with callers that never extract, so it reports no extraction plan; that path only runs when anExtractsclass is actually removed.remove_unused_expressionnow also routes throughexpr_has_specialized_unused_handler, so the list of handlers that can leave residue cannot drift from the dispatch. The same analysis relies on that list to know which initializers are dropped whole. A new specialized arm added without a predicate entry becomes dead code that its author's own tests catch; a predicate entry without a dispatch arm hits anunreachable!.No behavior change. Minified output and iteration counts are byte-identical on every fixture; 553 unit tests pass.
Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy.