Lambda to reference: check potential reference arguments by descriptors and not by names #KT-14420 Fixed
This commit is contained in:
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
|||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
|
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext.FUNCTION
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
|
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||||
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||||
@@ -105,10 +106,14 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio
|
|||||||
val receiverShift = if (callHasReceiver) 1 else 0
|
val receiverShift = if (callHasReceiver) 1 else 0
|
||||||
val parametersCount = if (hasSpecification) lambdaExpression.valueParameters.size else 1
|
val parametersCount = if (hasSpecification) lambdaExpression.valueParameters.size else 1
|
||||||
if (parametersCount != callableArgumentsCount + receiverShift) return false
|
if (parametersCount != callableArgumentsCount + receiverShift) return false
|
||||||
|
val lambdaValueParameters = context[FUNCTION, lambdaExpression.functionLiteral]?.valueParameters ?: return false
|
||||||
if (explicitReceiver != null) {
|
if (explicitReceiver != null) {
|
||||||
if (explicitReceiver !is KtNameReferenceExpression) return false
|
if (explicitReceiver !is KtNameReferenceExpression) return false
|
||||||
val callReceiverDescriptor = context[REFERENCE_TARGET, explicitReceiver] as? ParameterDescriptor ?: return false
|
if (lambdaValueParameters.isEmpty()) return false
|
||||||
val receiverType = callReceiverDescriptor.type
|
val explicitReceiverTarget = context[REFERENCE_TARGET, explicitReceiver] as? ParameterDescriptor ?: return false
|
||||||
|
if (explicitReceiverTarget != lambdaValueParameters[0]) return false
|
||||||
|
|
||||||
|
val receiverType = explicitReceiverTarget.type
|
||||||
// No exotic receiver types
|
// No exotic receiver types
|
||||||
if (receiverType.isTypeParameter() || receiverType.isError || receiverType.isDynamic() ||
|
if (receiverType.isTypeParameter() || receiverType.isError || receiverType.isDynamic() ||
|
||||||
!receiverType.constructor.isDenotable || receiverType.isFunctionType) return false
|
!receiverType.constructor.isDenotable || receiverType.isFunctionType) return false
|
||||||
@@ -121,16 +126,14 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio
|
|||||||
explicitReceiver, null, context, explicitReceiver.getResolutionFacade()
|
explicitReceiver, null, context, explicitReceiver.getResolutionFacade()
|
||||||
)) return false
|
)) return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val parameterName = if (hasSpecification) lambdaExpression.valueParameters[0].name else "it"
|
|
||||||
if (explicitReceiver.getReferencedName() != parameterName) return false
|
|
||||||
}
|
}
|
||||||
// Same lambda / references function parameter order
|
// Same lambda / references function parameter order
|
||||||
if (callableExpression is KtCallExpression) {
|
if (callableExpression is KtCallExpression) {
|
||||||
|
if (lambdaValueParameters.size < receiverShift + callableExpression.valueArguments.size) return false
|
||||||
callableExpression.valueArguments.forEachIndexed { i, argument ->
|
callableExpression.valueArguments.forEachIndexed { i, argument ->
|
||||||
val argumentExpression = argument.getArgumentExpression() as? KtNameReferenceExpression ?: return false
|
val argumentExpression = argument.getArgumentExpression() as? KtNameReferenceExpression ?: return false
|
||||||
val parameterName = if (hasSpecification) lambdaExpression.valueParameters[i + receiverShift].name else "it"
|
val argumentTarget = context[REFERENCE_TARGET, argumentExpression] as? ValueParameterDescriptor ?: return false
|
||||||
if (argumentExpression.getReferencedName() != parameterName) return false
|
if (argumentTarget != lambdaValueParameters[i + receiverShift]) return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1).forEach { run { <caret>bar(it) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(i: Int) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1).forEach { run { <caret>it.bar() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Int.bar() {
|
||||||
|
}
|
||||||
@@ -4123,6 +4123,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedLambda.kt")
|
||||||
|
public void testNestedLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nestedLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedLambdaWithReceiver.kt")
|
||||||
|
public void testNestedLambdaWithReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nestedLambdaWithReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nullable.kt")
|
@TestMetadata("nullable.kt")
|
||||||
public void testNullable() throws Exception {
|
public void testNullable() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nullable.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nullable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user