How does EK9 detect redundant empty and isSet checks?

← Code Quality · Ref: Q736

EK9 tracks variable state through data flow and detects three categories of redundant checks.

ISSET ALWAYS FALSE (E08089)

If the compiler has already proved a variable is unset, checking it with ? again is always false. For example, in the else branch of 'if name?' a second 'else if name?' can never succeed, so the code inside it is dead code. A variable declared with '?' and never assigned is a different error: using it is E08020 (might be used before being initialised).

EMPTY ALWAYS TRUE (E08092)

If a collection is created with no elements and not modified before an empty check, the check is always true. An empty Optional is also always empty.

EMPTY ALWAYS FALSE (E08093)

If a collection is created from a literal with elements, an empty check is always false. An Optional created with a value is never empty.

CORRECT PATTERNS

Assign a value before checking isSet. Populate collections before checking empty. Use function returns where the state is genuinely unknown.

See Q559 for redundant isSet overview. See Q640 for collection isSet patterns.

Example

defines module qa.codequality.emptyissetflow

  defines function

    findName()
      -> key as String
      <- result as String: String()

      if key == "admin"
        result: "Administrator"

    describeItems()
      -> items as List of String
      <- description as String: "empty"

      if not items empty
        description: `${length items} items`

  defines program

    EmptyIsSetFlowDemo()
      stdout <- Stdout()

      //isSet check on function return - genuinely needed
      name <- findName("admin")
      if name?
        stdout.println(`Found: ${name}`)

      //empty check on function return - genuinely needed
      items <- ["alpha", "beta"]
      stdout.println(describeItems(items))

      emptyList <- List() of String
      stdout.println(describeItems(emptyList))

Common mistakes

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

name <- findName("admin").toUpperCase()

Correct:

name <- findName("admin")

E50060 — String has no getValue() method. findName() already returns a String. See ek9 -h E50060 for details.

Incorrect:

name <- findName("admin").getValue()

Correct:

name <- findName("admin")

E50060 — List has no toString() method. Use string interpolation or the $ operator. See ek9 -h E50060 for details.

Incorrect:

stdout.println(items.toString())

Correct:

stdout.println(describeItems(items))
Other ways to ask this
  • What is E08089 isSet always false in EK9?
  • What is E08092 empty always true in EK9?
  • What is E08093 empty always false in EK9?

Coming from another language?

Java: no built-in detection, optional IDE inspections. Rust: clippy detects some redundant Option checks. Python: pylint detects some unreachable code. Go: go vet detects some unreachable code. EK9: mandatory compiler error with full data flow tracking for isSet and empty checks.

Keywords: E08092, E08093, optional, always, collection, empty, E08089, quality, flow, never, code, isset, redundant, dead