mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
KT-39532 Support intention to convert reference to lambda and vice versa for adapted references (#3495)
* Convert lambda to reference: support a function which has default parameters/unit return type/suspendability #KT-39532 Fixed
This commit is contained in:
committed by
GitHub
parent
a87b25d10e
commit
e822e871f5
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
|
||||
@@ -63,6 +64,7 @@ open class ConvertLambdaToReferenceIntention(textGetter: () -> String) : SelfTar
|
||||
explicitReceiver: KtExpression? = null,
|
||||
lambdaExpression: KtLambdaExpression
|
||||
): Boolean {
|
||||
val languageVersionSettings = callableExpression.languageVersionSettings
|
||||
val context = callableExpression.analyze()
|
||||
val calleeReferenceExpression = when (callableExpression) {
|
||||
is KtCallExpression -> callableExpression.calleeExpression as? KtNameReferenceExpression ?: return false
|
||||
@@ -80,7 +82,10 @@ open class ConvertLambdaToReferenceIntention(textGetter: () -> String) : SelfTar
|
||||
|
||||
val lambdaParameterIsSuspend = lambdaParameterType?.isSuspendFunctionType == true
|
||||
val calleeFunctionIsSuspend = (calleeDescriptor as? FunctionDescriptor)?.isSuspend == true
|
||||
if (lambdaParameterIsSuspend && !calleeFunctionIsSuspend || !lambdaParameterIsSuspend && calleeFunctionIsSuspend) return false
|
||||
if (!lambdaParameterIsSuspend && calleeFunctionIsSuspend) return false
|
||||
if (lambdaParameterIsSuspend && !calleeFunctionIsSuspend &&
|
||||
!languageVersionSettings.supportsFeature(LanguageFeature.SuspendConversion)
|
||||
) return false
|
||||
|
||||
// No references with type parameters
|
||||
if (calleeDescriptor.typeParameters.isNotEmpty()) return false
|
||||
@@ -94,17 +99,25 @@ open class ConvertLambdaToReferenceIntention(textGetter: () -> String) : SelfTar
|
||||
}
|
||||
|
||||
if (!descriptorHasReceiver && explicitReceiver != null && calleeDescriptor !is ClassConstructorDescriptor) return false
|
||||
val noBoundReferences = !callableExpression.languageVersionSettings.supportsFeature(LanguageFeature.BoundCallableReferences)
|
||||
val noBoundReferences = !languageVersionSettings.supportsFeature(LanguageFeature.BoundCallableReferences)
|
||||
if (noBoundReferences && descriptorHasReceiver && explicitReceiver == null) return false
|
||||
|
||||
val callableArgumentsCount = (callableExpression as? KtCallExpression)?.valueArguments?.size ?: 0
|
||||
if (calleeDescriptor.valueParameters.size != callableArgumentsCount) return false
|
||||
val lambdaMustReturnUnit =
|
||||
if (lambdaParameterType?.isFunctionType == true) lambdaParameterType.getReturnTypeFromFunctionType().isUnit() else false
|
||||
if (lambdaMustReturnUnit) {
|
||||
calleeDescriptor.returnType.let {
|
||||
// If Unit required, no references to non-Unit callables
|
||||
if (it == null || !it.isUnit()) return false
|
||||
val enableFunctionReferenceWithDefaultValueAsOtherType =
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType)
|
||||
if (enableFunctionReferenceWithDefaultValueAsOtherType) {
|
||||
if (calleeDescriptor.valueParameters.size != callableArgumentsCount &&
|
||||
(lambdaExpression.parentValueArgument() == null || calleeDescriptor.valueParameters.none { it.declaresDefaultValue() })
|
||||
) return false
|
||||
} else {
|
||||
if (calleeDescriptor.valueParameters.size != callableArgumentsCount) return false
|
||||
val lambdaMustReturnUnit =
|
||||
if (lambdaParameterType?.isFunctionType == true) lambdaParameterType.getReturnTypeFromFunctionType().isUnit() else false
|
||||
if (lambdaMustReturnUnit) {
|
||||
calleeDescriptor.returnType.let {
|
||||
// If Unit required, no references to non-Unit callables
|
||||
if (it == null || !it.isUnit()) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +150,7 @@ open class ConvertLambdaToReferenceIntention(textGetter: () -> String) : SelfTar
|
||||
if (lambdaValueParameterDescriptors.size < explicitReceiverShift + callableExpression.valueArguments.size) return false
|
||||
val resolvedCall = callableExpression.getResolvedCall(context) ?: return false
|
||||
resolvedCall.valueArguments.entries.forEach { (valueParameter, resolvedArgument) ->
|
||||
if (resolvedArgument is DefaultValueArgument && enableFunctionReferenceWithDefaultValueAsOtherType) return@forEach
|
||||
val argument = resolvedArgument.arguments.singleOrNull() ?: return false
|
||||
if (resolvedArgument is VarargValueArgument && argument.getSpreadElement() == null) return false
|
||||
val argumentExpression = argument.getArgumentExpression() as? KtNameReferenceExpression ?: return false
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-FunctionReferenceWithDefaultValueAsOtherType
|
||||
|
||||
fun foo(z: Int, y: Int = 0) = y + z
|
||||
|
||||
|
||||
5
idea/testData/intentions/convertLambdaToReference/defaultArgument2.kt
vendored
Normal file
5
idea/testData/intentions/convertLambdaToReference/defaultArgument2.kt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
fun foo(z: Int, y: Int = 0) = y + z
|
||||
|
||||
val x = { arg: Int <caret>-> foo(arg) }
|
||||
7
idea/testData/intentions/convertLambdaToReference/defaultArgument3.kt
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/defaultArgument3.kt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun foo(f: () -> Unit) {}
|
||||
|
||||
fun bar(a: Int = 42) {}
|
||||
|
||||
fun test() {
|
||||
foo <caret>{ bar() }
|
||||
}
|
||||
7
idea/testData/intentions/convertLambdaToReference/defaultArgument3.kt.after
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/defaultArgument3.kt.after
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun foo(f: () -> Unit) {}
|
||||
|
||||
fun bar(a: Int = 42) {}
|
||||
|
||||
fun test() {
|
||||
foo(::bar)
|
||||
}
|
||||
7
idea/testData/intentions/convertLambdaToReference/defaultArgument4.kt
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/defaultArgument4.kt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun foo(f: (x: Int) -> Unit) {}
|
||||
|
||||
fun bar(x: Int, y: Int = 42) {}
|
||||
|
||||
fun test() {
|
||||
foo <caret>{ bar(it) }
|
||||
}
|
||||
7
idea/testData/intentions/convertLambdaToReference/defaultArgument4.kt.after
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/defaultArgument4.kt.after
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun foo(f: (x: Int) -> Unit) {}
|
||||
|
||||
fun bar(x: Int, y: Int = 42) {}
|
||||
|
||||
fun test() {
|
||||
foo(::bar)
|
||||
}
|
||||
7
idea/testData/intentions/convertLambdaToReference/defaultArgument5.kt
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/defaultArgument5.kt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun Int.foo(x: Int, y: Int = 42) = x + y
|
||||
|
||||
fun bar(f: (Int, Int) -> Int) {}
|
||||
|
||||
fun test() {
|
||||
bar <caret>{ i, x -> i.foo(x) }
|
||||
}
|
||||
7
idea/testData/intentions/convertLambdaToReference/defaultArgument5.kt.after
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/defaultArgument5.kt.after
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun Int.foo(x: Int, y: Int = 42) = x + y
|
||||
|
||||
fun bar(f: (Int, Int) -> Int) {}
|
||||
|
||||
fun test() {
|
||||
bar(Int::foo)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-SuspendConversion
|
||||
|
||||
fun coroutine(block: suspend () -> Unit) {}
|
||||
|
||||
|
||||
8
idea/testData/intentions/convertLambdaToReference/suspendFunctionParameter3.kt
vendored
Normal file
8
idea/testData/intentions/convertLambdaToReference/suspendFunctionParameter3.kt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+SuspendConversion
|
||||
fun foo(a: suspend () -> Unit) {}
|
||||
|
||||
fun action() {}
|
||||
|
||||
fun usage() {
|
||||
foo { action() <caret> }
|
||||
}
|
||||
8
idea/testData/intentions/convertLambdaToReference/suspendFunctionParameter3.kt.after
vendored
Normal file
8
idea/testData/intentions/convertLambdaToReference/suspendFunctionParameter3.kt.after
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+SuspendConversion
|
||||
fun foo(a: suspend () -> Unit) {}
|
||||
|
||||
fun action() {}
|
||||
|
||||
fun usage() {
|
||||
foo(::action)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-FunctionReferenceWithDefaultValueAsOtherType
|
||||
|
||||
fun Int.exec(f: (Int) -> Unit) = f(this)
|
||||
|
||||
|
||||
7
idea/testData/intentions/convertLambdaToReference/unit2.kt
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/unit2.kt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun Int.exec(f: (Int) -> Unit) = f(this)
|
||||
|
||||
fun bar(x: Int) = x
|
||||
|
||||
fun foo() {
|
||||
2.exec {<caret> bar(it) }
|
||||
}
|
||||
7
idea/testData/intentions/convertLambdaToReference/unit2.kt.after
vendored
Normal file
7
idea/testData/intentions/convertLambdaToReference/unit2.kt.after
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fun Int.exec(f: (Int) -> Unit) = f(this)
|
||||
|
||||
fun bar(x: Int) = x
|
||||
|
||||
fun foo() {
|
||||
2.exec(::bar)
|
||||
}
|
||||
@@ -4990,6 +4990,26 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/defaultArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultArgument2.kt")
|
||||
public void testDefaultArgument2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/defaultArgument2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultArgument3.kt")
|
||||
public void testDefaultArgument3() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/defaultArgument3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultArgument4.kt")
|
||||
public void testDefaultArgument4() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/defaultArgument4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultArgument5.kt")
|
||||
public void testDefaultArgument5() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/defaultArgument5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultBeforeLambda.kt")
|
||||
public void testDefaultBeforeLambda() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/defaultBeforeLambda.kt");
|
||||
@@ -5225,6 +5245,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/suspendFunctionParameter2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("suspendFunctionParameter3.kt")
|
||||
public void testSuspendFunctionParameter3() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/suspendFunctionParameter3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("syntheticProperty.kt")
|
||||
public void testSyntheticProperty() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/syntheticProperty.kt");
|
||||
@@ -5295,6 +5320,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/unit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unit2.kt")
|
||||
public void testUnit2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/unit2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unwrap.kt")
|
||||
public void testUnwrap() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLambdaToReference/unwrap.kt");
|
||||
|
||||
Reference in New Issue
Block a user