[NI] Take into account return without expression in lambdas
This commit is contained in:
committed by
Mikhail Zarechenskiy
parent
17cdbcca03
commit
4963580f90
+23
-12
@@ -63,7 +63,7 @@ class KotlinResolutionCallbacksImpl(
|
|||||||
|
|
||||||
class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) {
|
class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) {
|
||||||
var dataFlowInfoAfter: DataFlowInfo? = null
|
var dataFlowInfoAfter: DataFlowInfo? = null
|
||||||
val returnStatements = ArrayList<Pair<KtReturnExpression, KotlinTypeInfo>>()
|
val returnStatements = ArrayList<Pair<KtReturnExpression, KotlinTypeInfo?>>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val STUB_EMPTY = LambdaInfo(TypeUtils.NO_EXPECTED_TYPE, ContextDependency.INDEPENDENT)
|
val STUB_EMPTY = LambdaInfo(TypeUtils.NO_EXPECTED_TYPE, ContextDependency.INDEPENDENT)
|
||||||
@@ -86,7 +86,6 @@ class KotlinResolutionCallbacksImpl(
|
|||||||
createSimplePSICallArgument(trace.bindingContext, outerCallContext.statementFilter, outerCallContext.scope.ownerDescriptor,
|
createSimplePSICallArgument(trace.bindingContext, outerCallContext.statementFilter, outerCallContext.scope.ownerDescriptor,
|
||||||
CallMaker.makeExternalValueArgument(ktExpression), DataFlowInfo.EMPTY, typeInfo)
|
CallMaker.makeExternalValueArgument(ktExpression), DataFlowInfo.EMPTY, typeInfo)
|
||||||
|
|
||||||
|
|
||||||
val expression: KtExpression = (psiCallArgument as? LambdaKotlinCallArgumentImpl)?.ktLambdaExpression ?:
|
val expression: KtExpression = (psiCallArgument as? LambdaKotlinCallArgumentImpl)?.ktLambdaExpression ?:
|
||||||
(psiCallArgument as FunctionExpressionImpl).ktFunction
|
(psiCallArgument as FunctionExpressionImpl).ktFunction
|
||||||
|
|
||||||
@@ -113,19 +112,31 @@ class KotlinResolutionCallbacksImpl(
|
|||||||
val functionTypeInfo = expressionTypingServices.getTypeInfo(expression, actualContext)
|
val functionTypeInfo = expressionTypingServices.getTypeInfo(expression, actualContext)
|
||||||
trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, ktFunction, LambdaInfo.STUB_EMPTY)
|
trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, ktFunction, LambdaInfo.STUB_EMPTY)
|
||||||
|
|
||||||
val lastExpressionArgument = getLastDeparentesizedExpression(psiCallArgument)?.let { lastExpression ->
|
var hasReturnWithoutExpression = false
|
||||||
if (expectedReturnType?.isUnit() == true) return@let null // coercion to Unit
|
|
||||||
|
|
||||||
val lastExpressionType = trace.getType(lastExpression)
|
|
||||||
val lastExpressionTypeInfo = KotlinTypeInfo(lastExpressionType, lambdaInfo.dataFlowInfoAfter ?: functionTypeInfo.dataFlowInfo)
|
|
||||||
createCallArgument(lastExpression, lastExpressionTypeInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
val returnArguments = lambdaInfo.returnStatements.mapNotNullTo(ArrayList()) { (expression, typeInfo) ->
|
val returnArguments = lambdaInfo.returnStatements.mapNotNullTo(ArrayList()) { (expression, typeInfo) ->
|
||||||
expression.returnedExpression?.let { createCallArgument(it, typeInfo) }
|
val returnedExpression = expression.returnedExpression
|
||||||
|
if (returnedExpression != null) {
|
||||||
|
createCallArgument(
|
||||||
|
returnedExpression,
|
||||||
|
typeInfo ?: throw AssertionError("typeInfo should be non-null for return with expression")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hasReturnWithoutExpression = true
|
||||||
|
EmptyLabeledReturn(expression, builtIns)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnArguments.addIfNotNull(lastExpressionArgument)
|
if (!hasReturnWithoutExpression) {
|
||||||
|
val lastExpressionArgument = getLastDeparentesizedExpression(psiCallArgument)?.let { lastExpression ->
|
||||||
|
if (expectedReturnType?.isUnit() == true) return@let null // coercion to Unit
|
||||||
|
|
||||||
|
val lastExpressionType = trace.getType(lastExpression)
|
||||||
|
val lastExpressionTypeInfo = KotlinTypeInfo(lastExpressionType, lambdaInfo.dataFlowInfoAfter ?: functionTypeInfo.dataFlowInfo)
|
||||||
|
createCallArgument(lastExpression, lastExpressionTypeInfo)
|
||||||
|
}
|
||||||
|
returnArguments.addIfNotNull(lastExpressionArgument)
|
||||||
|
}
|
||||||
|
|
||||||
return returnArguments
|
return returnArguments
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -166,7 +166,7 @@ class KotlinToResolvedCallTransformer(
|
|||||||
updateTraceForLambdaReturnType(lambdaArgument, trace, returnType)
|
updateTraceForLambdaReturnType(lambdaArgument, trace, returnType)
|
||||||
|
|
||||||
for (lambdaResult in lambdaArgument.resultArguments) {
|
for (lambdaResult in lambdaArgument.resultArguments) {
|
||||||
val resultValueArgument = lambdaResult.psiCallArgument
|
val resultValueArgument = lambdaResult as? PSIKotlinCallArgument ?: continue
|
||||||
val newContext =
|
val newContext =
|
||||||
context.replaceDataFlowInfo(resultValueArgument.dataFlowInfoAfterThisArgument)
|
context.replaceDataFlowInfo(resultValueArgument.dataFlowInfoAfterThisArgument)
|
||||||
.replaceExpectedType(returnType)
|
.replaceExpectedType(returnType)
|
||||||
|
|||||||
@@ -159,6 +159,16 @@ class FakeValueArgumentForLeftCallableReference(val ktExpression: KtCallableRefe
|
|||||||
override fun isExternal(): Boolean = false
|
override fun isExternal(): Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EmptyLabeledReturn(
|
||||||
|
val returnExpression: KtReturnExpression,
|
||||||
|
builtIns: KotlinBuiltIns
|
||||||
|
) : ExpressionKotlinCallArgument {
|
||||||
|
override val isSpread: Boolean get() = false
|
||||||
|
override val argumentName: Name? get() = null
|
||||||
|
override val receiver = ReceiverValueWithSmartCastInfo(TransientReceiver(builtIns.unitType), emptySet(), true)
|
||||||
|
override val isSafeCall: Boolean get() = false
|
||||||
|
}
|
||||||
|
|
||||||
// context here is context for value argument analysis
|
// context here is context for value argument analysis
|
||||||
internal fun createSimplePSICallArgument(
|
internal fun createSimplePSICallArgument(
|
||||||
contextForArgument: BasicCallResolutionContext,
|
contextForArgument: BasicCallResolutionContext,
|
||||||
|
|||||||
+3
@@ -681,6 +681,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (!noExpectedType(expectedType) && !KotlinBuiltIns.isUnit(expectedType) && !isDontCarePlaceholder(expectedType)) {
|
if (!noExpectedType(expectedType) && !KotlinBuiltIns.isUnit(expectedType) && !isDontCarePlaceholder(expectedType)) {
|
||||||
context.trace.report(RETURN_TYPE_MISMATCH.on(expression, expectedType));
|
context.trace.report(RETURN_TYPE_MISMATCH.on(expression, expectedType));
|
||||||
}
|
}
|
||||||
|
if (newInferenceLambdaInfo != null) {
|
||||||
|
newInferenceLambdaInfo.getReturnStatements().add(new kotlin.Pair<>(expression, null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return components.dataFlowAnalyzer.createCheckedTypeInfo(resultType, context, expression);
|
return components.dataFlowAnalyzer.createCheckedTypeInfo(resultType, context, expression);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,10 +48,3 @@ class ReceiverExpressionKotlinCallArgument private constructor(
|
|||||||
) = ReceiverExpressionKotlinCallArgument(receiver.prepareReceiverRegardingCaptureTypes(), isSafeCall, isVariableReceiverForInvoke)
|
) = ReceiverExpressionKotlinCallArgument(receiver.prepareReceiverRegardingCaptureTypes(), isSafeCall, isVariableReceiverForInvoke)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EmptyLabeledReturn(builtIns: KotlinBuiltIns) : ExpressionKotlinCallArgument {
|
|
||||||
override val isSpread: Boolean get() = false
|
|
||||||
override val argumentName: Name? get() = null
|
|
||||||
override val receiver = ReceiverValueWithSmartCastInfo(TransientReceiver(builtIns.unitType), emptySet(), true)
|
|
||||||
override val isSafeCall: Boolean get() = false
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,16 @@ fun test2() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO we don't see 'return@lambda' inside internal lambda when we analyze an external lambda,
|
||||||
|
// so type information from NI is actually incorrect, see KT-18392
|
||||||
|
fun test3() {
|
||||||
|
run lambda@{
|
||||||
|
run {
|
||||||
|
return@lambda
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun testLrmFoo1(ints: List<Int>) {
|
fun testLrmFoo1(ints: List<Int>) {
|
||||||
ints.forEach lit@ {
|
ints.forEach lit@ {
|
||||||
if (it == 0) return@lit
|
if (it == 0) return@lit
|
||||||
|
|||||||
@@ -29,6 +29,24 @@ FILE /nonLocalReturn.kt
|
|||||||
RETURN type=kotlin.Nothing from='<anonymous>(): Unit'
|
RETURN type=kotlin.Nothing from='<anonymous>(): Unit'
|
||||||
GET_OBJECT 'Unit' type=kotlin.Unit
|
GET_OBJECT 'Unit' type=kotlin.Unit
|
||||||
FUNCTION_REFERENCE '<anonymous>(): Unit' type=() -> kotlin.Unit origin=LAMBDA
|
FUNCTION_REFERENCE '<anonymous>(): Unit' type=() -> kotlin.Unit origin=LAMBDA
|
||||||
|
FUN public fun test3(): kotlin.Unit
|
||||||
|
BLOCK_BODY
|
||||||
|
CALL 'run(() -> Nothing): Nothing' type=kotlin.Nothing origin=null
|
||||||
|
<R>: Nothing
|
||||||
|
block: BLOCK type=() -> kotlin.Nothing origin=LAMBDA
|
||||||
|
FUN LOCAL_FUNCTION_FOR_LAMBDA local final fun <anonymous>(): kotlin.Nothing
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<anonymous>(): Nothing'
|
||||||
|
CALL 'run(() -> Nothing): Nothing' type=kotlin.Nothing origin=null
|
||||||
|
<R>: Nothing
|
||||||
|
block: BLOCK type=() -> kotlin.Nothing origin=LAMBDA
|
||||||
|
FUN LOCAL_FUNCTION_FOR_LAMBDA local final fun <anonymous>(): kotlin.Nothing
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<anonymous>(): Nothing'
|
||||||
|
TYPE_OP type=kotlin.Nothing origin=IMPLICIT_CAST typeOperand=kotlin.Nothing
|
||||||
|
GET_OBJECT 'Unit' type=kotlin.Unit
|
||||||
|
FUNCTION_REFERENCE '<anonymous>(): Nothing' type=() -> kotlin.Nothing origin=LAMBDA
|
||||||
|
FUNCTION_REFERENCE '<anonymous>(): Nothing' type=() -> kotlin.Nothing origin=LAMBDA
|
||||||
FUN public fun testLrmFoo1(ints: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
FUN public fun testLrmFoo1(ints: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
||||||
VALUE_PARAMETER value-parameter ints: kotlin.collections.List<kotlin.Int>
|
VALUE_PARAMETER value-parameter ints: kotlin.collections.List<kotlin.Int>
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
|
|||||||
Reference in New Issue
Block a user