Report suspend function declared as extension on CoroutineScope

Description

With Kotlin coroutines there is a convention to declare functions starting a coroutine and return immediatly as extension function on CoroutineScope.

And there is also a language keyword “suspend” to tell the opposite: that the function may not resume immediatly, and may suspend a coroutine without blocking.

Therefore writing a suspend extension function on CoroutineScope is very confusing for the reader: Does it resume immediatly or not?

Snippet of non-compliant code

suspend fun CoroutineScope.doSomething() {
	val part1 = async { computePart1() }
	val part2 = async { computePart2() }
	return combine(part1.await(), part2.await())
}

Snippet of compliant code (fixing the above noncompliant code)

Let it suspend and remove CoroutineScope receiver from the signature

suspend fun doSomething() = coroutineScope {
	val part1 = async { computePart1() }
	val part2 = async { computePart2() }
	
	combine(part1.await(), part2.await())
}

Or let the CoroutineScope receiver from the signature and remove the suspend modifier

fun CoroutineScope.doSomething() {
	launch {
		val part1 = async { computePart1() }
		val part2 = async { computePart2() }
		
		println(combine(part1.await(), part2.await()))
	}
}

External references

The maintener of kotlinx.coroutines, Roman Elizarov, wrote a good article about it here: https://medium.com/@elizarov/explicit-concurrency-67a8e8fd9b25

Type: Code Smell

It confuse the reader and makes harder to understand if it launches coroutines or if it suspend.

I would personally set the default severity to “Major”

Tags:

multi-threading, bad-practice, design, convention

Thank you very much, @jcornaz, for the effort put into specifying this rule. We will come up with a follow-up message once we evaluate all the rules you have suggested.