Hi, I’m wondering whether there’s any way to disallow or at least compiler warn when an unqualified return is used inside a lambda, especially if it is the last expression?
Considering the following case:
private val cache = WeakHashMap<SomeLibraryObject, Int>()
fun SomeLibraryObject.cachedExpensiveCalculation(): Int = cache.getOrPut(this) {
val result = someExpensiveCalculation()
return result
}
The return
at the end returns the nearest function inside the lambda (cachedExpensiveCalculation
in this case) and appears to work, but it never returns so the value is never put into the WeakHashMap
.
Ideally I’d like a warning to force return@cachedExpensiveCalculation
in this case
It’s your responsibility to write the correct program, not the compiler. If a programmer wants return
- he got return
.
Couldn’t you say that about all compiler warnings?
Maybe I’m missing something but here’s an example that shows it is already disallowed
Here’s a runnable example of the problem. The desired behavior is to print “ABC”. Instead, it prints “AB”:
inline fun getOrPut(block: () -> String): String {
return block() + "C" // We'll just stub this to always run for the example
}
fun getValue1(): String = getOrPut {
val result = "A" + "B"
return result
}
fun main() {
println(getValue1())
}
There are three ways to go about fixing this that I can think of off the top of my head:
- The
getOrPost
function says it shouldn’t ever be early-returned–which isn’t possible. But the getOrPost
function is always able to add some defensive coding with a finally
block to handle exits from returns and exceptions. So this option is off the table and handled already.
- The caller of the lambda somehow indicates they don’t want to return from a lambda (maybe with an annotation or some way that produces a warning).
- Some static code analysis to catch the usage.
Let’s look at the second option.
First, marking the call site with an annotation:
You could imagine some annotation that you’d mark your call of the lambda with to get an error. This has the added benefit of being do-able with code inspection tools instead of requiring it be added by JetBrains to Kotlin.
fun exampleFunction: Int = cache.getOrPut(key) @NoReturn {
// ...
return 5 // WARNING! Warning marked since the lambda has a @NoReturn annotation
}
// Implicit return of last line. Here is where we really want to return
}
This is nice, but there’s another solution that doesn’t require any new code. Since we’re changing our code to make it clear what we want to return and what we don’t, let’s just write our code in a way that’s more clear:
fun exampleFunction: Int {
val result = cache.getOrPut(key) {
// ...
// When the lambda uses contracts--if someone adds a return here, we get a warning that the other lines are unreachable
// Even without contracts, a return
}
return result // It's clear we want to return here
}
1 Like
Just to note that IntelliJ already has an inspection that catches this (but by default is “fix only” instead of warn); “Unlabeled return inside lambda”. If I change this to a warning, the IDE will highlight which is what I wanted in this instance;
2 Likes
If you change your getOrPut
function to inline fun
it’ll reproduce the behaviour.
1 Like
Thanks! I don’t know how I missed that–I even had the thought of “this should be inline, why isn’t it working?” and skipped past it
I’ve edited my example above.
1 Like