Split off from #627 / #2128.
-AignoreDeadCode (#2128) stops errors from being reported on code that is physically
inside a dead branch (a literal-condition if, or code dataflow proves is CFG-unreachable). It
does not fix a related but distinct problem: precision loss on code after a branch, caused
by merging with a dead alternative. Example:
@Nullable Object x;
if (true) {
x = new Object();
} else {
x = null;
}
x.toString(); // false positive today, even though the else branch is dead
CFAbstractStore.leastUpperBound runs at the join point during GenericAnnotatedTypeFactory .analyze(), a dataflow pass that is entirely separate from, and runs before, whatever the
type-checking visitor later chooses to check. Skipping visitation of the dead branch (what
#2128 does) has no effect on that pass, so x still gets widened to @Nullable at the
join and x.toString() is still flagged.
#101 ("Introduce "bottom store" to reduce false positive in dead branch") fixes exactly
this, by making the untaken branch of a literal-condition if/while/for/ternary produce a
lattice-bottom store instead of a normal one, so leastUpperBound with it is the identity and
the live branch's precision is preserved across the merge. That PR is not mergeable as-is
(unconditional — no option gate, changes default behavior for everyone; touches dataflow/'s
Store.FlowRule and CFGTranslationPhaseOne for ternary handling; requires new constructors on
every checker's Store/Analysis pair using names that no longer match current code, e.g.
InitializationStore no longer exists under that name), but the core technique — an isBottom
flag on CFAbstractStore, a bottom-store singleton on CFAbstractAnalysis, and a
CFAbstractTransfer.visitBooleanLiteral hook for if/while/for conditions only (dropping
#101's ternary/FlowRule changes as the riskiest, least-reviewed part) — is worth
reviving, gated by -AignoreDeadCode, with a safe createEmptyStore() fallback for any
checker's Store that doesn't override the new bottom-store method (so nothing else needs to
change to keep compiling).
Neither #101 nor #633's own tests actually exercised this join-point case (both only
tested errors inside a dead branch), so this was scoped out of #2128 rather than bundled
in.
🤖 Generated with Claude Code
Split off from #627 / #2128.
-AignoreDeadCode(#2128) stops errors from being reported on code that is physicallyinside a dead branch (a literal-condition
if, or code dataflow proves is CFG-unreachable). Itdoes not fix a related but distinct problem: precision loss on code after a branch, caused
by merging with a dead alternative. Example:
CFAbstractStore.leastUpperBoundruns at the join point duringGenericAnnotatedTypeFactory .analyze(), a dataflow pass that is entirely separate from, and runs before, whatever thetype-checking visitor later chooses to check. Skipping visitation of the dead branch (what
#2128 does) has no effect on that pass, so
xstill gets widened to@Nullableat thejoin and
x.toString()is still flagged.#101 ("Introduce "bottom store" to reduce false positive in dead branch") fixes exactly
this, by making the untaken branch of a literal-condition
if/while/for/ternary produce alattice-bottom store instead of a normal one, so
leastUpperBoundwith it is the identity andthe live branch's precision is preserved across the merge. That PR is not mergeable as-is
(unconditional — no option gate, changes default behavior for everyone; touches
dataflow/'sStore.FlowRuleandCFGTranslationPhaseOnefor ternary handling; requires new constructors onevery checker's
Store/Analysispair using names that no longer match current code, e.g.InitializationStoreno longer exists under that name), but the core technique — anisBottomflag on
CFAbstractStore, a bottom-store singleton onCFAbstractAnalysis, and aCFAbstractTransfer.visitBooleanLiteralhook forif/while/forconditions only (dropping#101's ternary/
FlowRulechanges as the riskiest, least-reviewed part) — is worthreviving, gated by
-AignoreDeadCode, with a safecreateEmptyStore()fallback for anychecker's
Storethat doesn't override the new bottom-store method (so nothing else needs tochange to keep compiling).
Neither #101 nor #633's own tests actually exercised this join-point case (both only
tested errors inside a dead branch), so this was scoped out of #2128 rather than bundled
in.
🤖 Generated with Claude Code