Why does EK9 reject my isSet check as redundant?

← Code Quality · Ref: Q559

EK9 tracks whether each variable is set, unset, or unknown at every program point. When the compiler can prove the outcome of an isSet check, it flags it as redundant.

REDUNDANT ISSET CHECK (E08088)

If a variable is known to be set, checking it with ? is pointless. Examples: a variable assigned from a literal ('name <- hello; if name?'), a variable inside a guard block ('if name <- getName(); if name?' — the guard already proved it is set), or a variable assigned from a constructor ('list <- List(); if list?').

NEVER-SET ISSET CHECK (E08089)

If a variable is declared but never assigned, checking it with ? is always false. Example: 'name as String?; if name?' — the variable was declared unset and no assignment occurs before the check.

WHEN ISSET IS GENUINELY NEEDED

The ? operator is needed when the variable might or might not be set depending on runtime conditions. A value from a function call may be unset. A value from a Dict lookup may be absent. A value conditionally assigned in only one branch of an if statement needs checking afterward.

GUARD PATTERN REPLACES ISSET

Instead of 'result <- lookup(key); if result?', use the guard pattern: 'if result <- lookup(key)'. The guard combines the call and the isSet check into one expression, and the variable is only in scope when it is known to be set.

See Q557 for flow-sensitive dead code detection. See Q558 for tautological condition patterns. See Q311 for the full quality checks catalog. See Q640 for collection isSet check patterns.

Example

defines module qa.codequality.issetcheck

  defines function

    fetchName()
      <- rtn as String: "Alice"

    fetchOptionalTitle()
      <- rtn as String: String()

    <?-
      Correct: isSet check on a value from a function call is genuinely useful
      because the function might return an unset value.
    -?>
    greetPerson()
      -> name as String
      <- greeting as String: `Hello ${name}`

      title <- fetchOptionalTitle()
      if title?
        greeting: `Hello ${title} ${name}`

    <?-
      Correct: the guard pattern combines lookup and isSet check.
      The variable is only in scope when it is known to be set.
    -?>
    greetWithGuard()
      <- greeting as String: "Hello stranger"

      if name <- fetchName()
        greeting: `Hello ${name}`

    <?-
      Correct: isSet check on a value from another function call
      where the result might be unset. The compiler does not know
      the runtime return value, so the check is genuinely needed.
    -?>
    buildFullGreeting()
      -> name as String
      <- result as String: `Greeting for ${name}`

      optionalSuffix <- fetchOptionalTitle()
      if optionalSuffix?
        result: `Dear ${optionalSuffix} ${name}`

  defines program

    RedundantIsSetDemo()
      stdout <- Stdout()

      personGreeting <- greetPerson("Bob")
      stdout.println(personGreeting)

      guardGreeting <- greetWithGuard()
      stdout.println(guardGreeting)

      fullGreeting <- buildFullGreeting("Carol")
      stdout.println(fullGreeting)

Common mistakes

E08090 — Declaring a variable and never using it is dead code. Every variable must be referenced. See ek9 -h E08090 for details.

Incorrect:

unused <- greetPerson("Bob")

Correct:

personGreeting <- greetPerson("Bob")
      stdout.println(personGreeting)
Other ways to ask this
  • What is E08088 redundant isSet check?
  • What is E08089 never-set isSet check?
  • How does EK9 track set and unset variables?
  • When is the ? operator genuinely needed?

Coming from another language?

Java: no compiler tracking of null vs non-null at each program point. Optional.isPresent() is never flagged as redundant. Kotlin: smart casts track nullability but do not flag redundant null checks. Rust: Option matching is exhaustive but redundant Some checks are not flagged. Go: no nil tracking. Python: no None tracking. C++: no nullptr flow analysis. EK9: mandatory flow-sensitive tracking of set/unset state with compiler errors for redundant or always-failing checks.

Keywords: E08088, E08089, isSet, unset, safe, quality, check, tracking, redundant, clean-code, flow, isset, metric, set, variable, guard, null-safe