Class RedundantIsSetCheckOrError
- All Implemented Interfaces:
Consumer<EK9Parser.ExpressionContext>
The postfix ? operator in EK9 checks if a variable is set. This checker uses the
ValueTrackingAnalyzer to determine when such checks are redundant because the variable
is provably SET (e.g., assigned from a literal, or inside a guard's true-branch) or provably
UNSET (e.g., declared with ? and never assigned).
Examples detected:
x <- "hello"; if x?— always true, string literal is always SET (E08088)if name <- getName() { if name? }— always true, guard proved SET (E08088)name as String?; if name?— always false, declared UNSET never assigned (E08089)
Inside require/assert: only the bug-class NEVER_SET case (E08089) fires.
The REDUNDANT case (E08088) is suppressed because require x? is the established
idiom for "touch this variable" since EK9 enforces capture and has no discard pattern.
Conditions inside loop bodies are suppressed because widening has not yet been applied.
Decision D7 (Steve, 2026-10-07) extends the same errors to the set-checks a reader does not see written as
x?, and to the ones hidden inside a larger condition:
- A guard's own implicit gate (
acceptGate(EK9Parser.PreFlowStatementContext, ConditionResult, boolean)) -if seven <- 7always enters (E08088),if v <- Integer()never does (E08089), andif late :=? 5on a provably unsetlatealways does. A Boolean truth guard's gate is "set AND true" - a condition, not a set-check - so a provably decided one is E08086 / E08087. A:=?whose target is provably set is left to E08095. - Re-testing the guard's own target in its
then/withcondition -if v <- f() then v?andif x :=? f() then x?- is ALWAYS redundant, with no data flow needed: the condition is evaluated only once the gate has passed (GuardedConditionEvaluator). So isthen bafter a Boolean truth guard onb(D9.2), which is E08086. Onlyif/else if: aswitch'sthenexpression is the value switched on, and awhilere-evaluates its condition every iteration while its gate runs once at entry, so a re-test there can matter. - A conjunct - an
x?that is an operand of a top-levelandchain of a condition (if x := 7 then x? and ready) - is judged by data flow exactly as a whole condition is. A blindif x := f() then x?stays clean: nothing provesf()set or unset.
-
Field Summary
Fields inherited from class RuleSupport
errorListener, symbolsAndScopes -
Constructor Summary
ConstructorsConstructorDescriptionRedundantIsSetCheckOrError(SymbolsAndScopes symbolsAndScopes, ErrorListener errorListener, ValueTrackingAnalyzer valueTracker) -
Method Summary
Modifier and TypeMethodDescriptionvoid(package private) voidacceptGate(EK9Parser.PreFlowStatementContext preFlow, ConditionResult targetBefore, boolean storesUnset) The implicit gate of a guard head (<-or:=?), judged once the pre-flow's store has been tracked (D7.1).(package private) voidA bare identifier that is the condition (or a conjunct of the condition) of anif/else ifheaded by a Boolean truth guard on that very identifier:if ok <- check() then ok.Methods inherited from class TypedSymbolAccess
emitTypeNotResolvedError, getRecordedAndTypedSymbol, isProcessingScopePure, recordATypedSymbol
-
Constructor Details
-
RedundantIsSetCheckOrError
RedundantIsSetCheckOrError(SymbolsAndScopes symbolsAndScopes, ErrorListener errorListener, ValueTrackingAnalyzer valueTracker)
-
-
Method Details
-
accept
- Specified by:
acceptin interfaceConsumer<EK9Parser.ExpressionContext>
-
acceptIdentifierCondition
A bare identifier that is the condition (or a conjunct of the condition) of anif/else ifheaded by a Boolean truth guard on that very identifier:if ok <- check() then ok. The gate has already requiredokto be set and true, so the condition is always true (E08086, D7.2 with D9.2).- Parameters:
expr- any expression; only a bare identifier reference is examined
-
acceptGate
void acceptGate(EK9Parser.PreFlowStatementContext preFlow, ConditionResult targetBefore, boolean storesUnset) The implicit gate of a guard head (<-or:=?), judged once the pre-flow's store has been tracked (D7.1). Inside a loop nothing is judged, exactly as for a writtenx?.A literal type's no-argument constructor (
Integer(),String(), ...) makes a present but UNSET value, so a guard storing one never enters. That fact is applied to the gate only: the value tracker in general does not yet record it (see theConstrainedSpecNegative"known limit"), because doing so moves E08095/E08089 across teaching examples that rely on the gap - a separate decision.- Parameters:
preFlow- the pre-flow, after its store has been trackedtargetBefore- a:=?target's set-state BEFORE the store (UNKNOWN for a declaration). A provably set target is E08095's case (the assignment never applies) and is not reported again here.storesUnset- whether the pre-flow's right-hand side is provably unset (a literal type's no-argument constructor)
-