How does EK9 detect discarded return values?
← Code Quality · Ref: Q316
EK9 detects four categories of discarded return values and rejects code that ignores them.
DISCARDED CONSTRUCTOR (E11055)
A constructor creates an object. Calling a constructor as a bare statement and discarding the result is always either a bug (forgot the assignment) or a misuse of constructors. If you want side effects without creating an object, use a function. This was discovered by the EK9 fuzzer: removing an assignment from valid code creates dead constructor calls that crash later compilation phases.
COMPUTATIONAL OPERATORS (E11050)
Operators like +, -, *, / produce new values. Calling them without capturing the result is always a bug. The operator did work but the result was thrown away. Example: calling 'list + item' without assigning the new list means the addition had no effect.
PURE FUNCTION RETURNS (E11051)
A pure function has no side effects. If you call a pure function and discard the return value, the call had no observable effect. This is dead code. EK9 flags it because it usually means the developer forgot to capture the result or called the wrong function.
RESULT AND OPTIONAL (E11052)
Result and Optional return types MUST be checked. These types exist specifically to force the caller to handle success/failure or presence/absence. Discarding them defeats their entire purpose. This is equivalent to catching an exception and ignoring it.
RUST ANALOGY
Rust has the #[must_use] attribute that warns when a return value is ignored. EK9 goes further: it is automatic for all constructors, all pure functions, all computational operators, and all Result/Optional types. No annotation needed, no way to suppress.
See Q311 for the full quality checks catalog. See Q48 for Result type. See Q47 for Optional type. See Q50 for function return values. See Q641 for pure function return capture patterns.
See Q693 for captured operator returns. See Q696 for complexity within limits. See Q700 for parameter count limits.
See Q732 for discarded return boundary examples.
Example
defines module qa.codequality.discarded defines function <?- Pure function: its return value MUST be captured. Calling this without capturing would trigger E11051. -?> doubleAmount() as pure -> amount as Integer <- rtn as Integer: amount * 2 <?- Another pure function demonstrating the pattern. -?> formatCurrency() as pure -> amount as Float <- rtn as String: `\$${amount}` defines program DiscardedReturnsDemo() stdout <- Stdout() // Correct: capturing the return value originalAmount <- 50 doubled <- doubleAmount(originalAmount) stdout.println(`Doubled: ${doubled}`) // Correct: using the return value in an expression price <- 29.99 formatted <- formatCurrency(price) stdout.println(`Price: ${formatted}`) // Correct: using operator result items <- List() of String updatedItems <- items + "first" stdout.println(`Items: ${updatedItems.length()}`)
Common mistakes
E50001 — Renaming the variable means later references to 'doubled' become unresolved, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
doubledXYZ <- doubleAmount(originalAmount)
Correct:
doubled <- doubleAmount(originalAmount)
E50001 — Renaming the variable means later references to 'updatedItems' become unresolved, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
updatedItemsXYZ <- items + "first"
Correct:
updatedItems <- items + "first"
Other ways to ask this
- What happens if I ignore a function return value in EK9?
- Does EK9 have must_use like Rust?
- Why does EK9 require capturing operator results?
Coming from another language?
Java: return values can always be silently discarded, no compiler enforcement. Rust: #[must_use] attribute warns but can be suppressed with let _ = expr. Go: return values can be discarded with _ blank identifier. Python: return values always silently discarded. C++: [[nodiscard]] attribute warns but can be cast to void. Kotlin: return values silently discarded. Swift: @discardableResult annotation opts specific functions into allowing discard (default is warn), can be silenced with _ = expr. EK9: automatic must-use for all pure functions, computational operators, and Result/Optional, cannot be suppressed.
Keywords: operator, migrate, guard, ok, code, constructor, must_use, metric, quality, return, safe, optional, immutable, discard, capture, dead, E11055, E11052, clean-code, error, E11051, E11050, result, absent, pure, swift, side-effect, discardableResult