Suspend Fun Flow Return
ID |
kotlin.suspend_fun_flow_return |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Kotlin |
Tags |
concurrency, reliability |
Description
Reports functions declared suspend that return a Flow<T>. Flow is a
cold, already-deferred stream: the work doesn’t start until a terminal
operator (collect, first, …) suspends and pulls from it. Wrapping the
producer in a suspend modifier forces the caller to suspend just to obtain
the flow, without any actual work being done at that point — the modifier is
misleading at best, and at worst introduces a dispatcher hop for nothing.
Rationale
// Bad
suspend fun loadAll(): Flow<Item> = flow {
emit(fetchAll())
}
The caller is now forced to be inside a coroutine just to receive the
Flow<Item> object — yet at that point no real work has happened. The actual
suspending work runs only when the caller calls collect. The suspend
modifier on the producer adds API friction without buying anything.
Remediation
Drop the suspend modifier. The producer body still runs in the collector’s
coroutine and is fully suspending there.
fun loadAll(): Flow<Item> = flow {
emit(fetchAll())
}
Exceptions
Functions returning a non-Flow type (a single Item, a List<Item>, a
Channel) are not affected by this rule. Their suspend modifier is doing
real work.