in Kotlin expressions like
fun main() {
throw throw throw RuntimeException("boom")
}
or
fun main() {
throw throw return
}
are syntactically correct
I understand ideas behind it, but I wonder why there is no Warn (at least in Itellij) when writing such nonsense.
CodePudding user response:
This is reported as a minor problem in KT-22621 "throw throw Exception()": False negative UNREACHABLE_CODE warning. It should be noted that unreachable code is warned if you put parentheses:
throw (throw Exception())
// or
throw (return)
Also, return throws Exception()
gives the warning too.
Unfortunately, it is still an open issue after 4 years.
After reading Kotlin's source code for a bit, I think this is unintentional. Let's consider ControlFlowProcessor.kt, visitThrowExpression
:
override fun visitThrowExpression(expression: KtThrowExpression) {
mark(expression)
generateJumpsToCatchAndFinally()
val thrownExpression = expression.thrownExpression ?: return
generateInstructions(thrownExpression)
val thrownValue = builder.getBoundValue(thrownExpression) ?: return
builder.throwException(expression, thrownValue)
}
Consider throw throw Exception()
. When the first throw
is visited, generateInstructions(thrownExpression)
generates the part of the CFG that corresponds to throw Exception()
. This causes the second throw
to be visited, generating the correct CFG for throw Exception()
.
The problem comes, when builder.getValue(thrownExpression)
gets called. This gets a PseudoValue
from the CFG builder that represents the thrown value. However, (I suspect that) this returns nil and builder.throwException
is not called. This is because when building the CFG for throw Exception()
, no value is bound. Compare this to what happens in visitParenthesizedExpression
, or visitCallExpression
, but you'd need to trace deeper.
This doesn't affect control flow analysis too much, since the part of the CFG generated for throw Exception()
is correct. The CFG does end up missing a node to represent the outer throw
though, which is what builder.throwException
would have added. This node would have been marked as unreachable and a warning been issued, but this node doesn't even exist, so no warnings.