KT-33211 Quickfix "add parameter" for method references should infer functional type instead of KFunction (#2664)
* "Add parameter to function" quick fix: add function type instead of KFunction for method references #KT-33211 Fixed * Support suspend function references
This commit is contained in:
committed by
GitHub
parent
6e67e1e78d
commit
4d2caa8e76
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement
|
|||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.builtins.isKSuspendFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
@@ -16,6 +17,7 @@ import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
|||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.core.setType
|
import org.jetbrains.kotlin.idea.core.setType
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.references.resolveToDescriptors
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||||
@@ -31,10 +33,11 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||||
import org.jetbrains.kotlin.types.isFlexible
|
import org.jetbrains.kotlin.types.isFlexible
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
import org.jetbrains.kotlin.util.OperatorChecks
|
import org.jetbrains.kotlin.util.OperatorChecks
|
||||||
import org.jetbrains.kotlin.idea.references.resolveToDescriptors
|
|
||||||
|
|
||||||
fun KtContainerNode.description(): String? {
|
fun KtContainerNode.description(): String? {
|
||||||
when (node.elementType) {
|
when (node.elementType) {
|
||||||
@@ -352,3 +355,11 @@ internal fun Sequence<PsiElement>.lastWithPersistedElementOrNull(elementShouldPe
|
|||||||
|
|
||||||
return if (checked) lastElement else null
|
return if (checked) lastElement else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KotlinType.reflectToRegularFunctionType(): KotlinType {
|
||||||
|
val isTypeAnnotatedWithExtensionFunctionType = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType) != null
|
||||||
|
val parameterCount = if (isTypeAnnotatedWithExtensionFunctionType) arguments.size - 2 else arguments.size - 1
|
||||||
|
val classDescriptor =
|
||||||
|
if (isKSuspendFunctionType) builtIns.getSuspendFunction(parameterCount) else builtIns.getFunction(parameterCount)
|
||||||
|
return KotlinTypeFactory.simpleNotNullType(annotations, classDescriptor, arguments)
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import com.intellij.psi.PsiNamedElement
|
|||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.search.PsiSearchHelper
|
import com.intellij.psi.search.PsiSearchHelper
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import org.jetbrains.kotlin.builtins.isKFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.isKSuspendFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
@@ -32,9 +34,10 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||||
import org.jetbrains.kotlin.idea.util.getDataFlowAwareTypes
|
import org.jetbrains.kotlin.idea.intentions.reflectToRegularFunctionType
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.getDataFlowAwareTypes
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
@@ -191,7 +194,9 @@ class AddFunctionParametersFix(
|
|||||||
): KotlinParameterInfo {
|
): KotlinParameterInfo {
|
||||||
val name = getNewArgumentName(argument, validator)
|
val name = getNewArgumentName(argument, validator)
|
||||||
val expression = argument.getArgumentExpression()
|
val expression = argument.getArgumentExpression()
|
||||||
val type = expression?.let { getDataFlowAwareTypes(it).firstOrNull() } ?: functionDescriptor.builtIns.nullableAnyType
|
val type = (expression?.let { getDataFlowAwareTypes(it).firstOrNull() } ?: functionDescriptor.builtIns.nullableAnyType).let {
|
||||||
|
if (it.isKFunctionType || it.isKSuspendFunctionType) it.reflectToRegularFunctionType() else it
|
||||||
|
}
|
||||||
return KotlinParameterInfo(functionDescriptor, -1, name, KotlinTypeInfo(false, null)).apply {
|
return KotlinParameterInfo(functionDescriptor, -1, name, KotlinTypeInfo(false, null)).apply {
|
||||||
currentTypeInfo = KotlinTypeInfo(false, type)
|
currentTypeInfo = KotlinTypeInfo(false, type)
|
||||||
originalTypeInfo.type?.let { typesToShorten.add(it) }
|
originalTypeInfo.type?.let { typesToShorten.add(it) }
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ import com.intellij.codeInsight.intention.IntentionAction
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
import org.jetbrains.kotlin.builtins.isKFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
@@ -30,6 +29,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.reflectToRegularFunctionType
|
||||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
|
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
@@ -48,21 +48,17 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
|||||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
|
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||||
import org.jetbrains.kotlin.types.typeUtil.*
|
import org.jetbrains.kotlin.types.typeUtil.isInterface
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
//TODO: should use change signature to deal with cases of multiple overridden descriptors
|
//TODO: should use change signature to deal with cases of multiple overridden descriptors
|
||||||
class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||||
|
|
||||||
private fun KotlinType.reflectToRegularFunctionType(): KotlinType {
|
|
||||||
val isTypeAnnotatedWithExtensionFunctionType = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType) != null
|
|
||||||
val parameterCount = if (isTypeAnnotatedWithExtensionFunctionType) arguments.size - 2 else arguments.size - 1
|
|
||||||
return KotlinTypeFactory.simpleNotNullType(annotations, builtIns.getFunction(parameterCount), arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
|
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
|
||||||
val actions = LinkedList<IntentionAction>()
|
val actions = LinkedList<IntentionAction>()
|
||||||
|
|
||||||
@@ -183,7 +179,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
|||||||
) {
|
) {
|
||||||
val scope = callable.getResolutionScope(context, callable.getResolutionFacade())
|
val scope = callable.getResolutionScope(context, callable.getResolutionFacade())
|
||||||
val typeToInsert = expressionType.approximateWithResolvableType(scope, false)
|
val typeToInsert = expressionType.approximateWithResolvableType(scope, false)
|
||||||
if (typeToInsert.constructor.declarationDescriptor?.getFunctionalClassKind() == FunctionClassDescriptor.Kind.KFunction) {
|
if (typeToInsert.isKFunctionType) {
|
||||||
actions.add(createFix(callable, typeToInsert.reflectToRegularFunctionType()))
|
actions.add(createFix(callable, typeToInsert.reflectToRegularFunctionType()))
|
||||||
}
|
}
|
||||||
actions.add(createFix(callable, typeToInsert))
|
actions.add(createFix(callable, typeToInsert))
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
fun baz() {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
baz(fun(i: Int): String { return i.toString() }<caret>)
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
fun baz(function: (Int) -> String) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
baz(fun(i: Int): String { return i.toString() }<caret>)
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
fun bar(): Int = 42
|
||||||
|
|
||||||
|
fun baz() {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
baz(::bar<caret>)
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
fun bar(): Int = 42
|
||||||
|
|
||||||
|
fun baz(kFunction0: () -> Int) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
baz(::bar)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
fun baz() {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
baz { i: Int -> i.toString() }<caret>
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
fun baz(function: (Int) -> String) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
baz { i: Int -> i.toString() }<caret>
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
suspend fun bar(): Int = 42
|
||||||
|
|
||||||
|
suspend fun baz() {}
|
||||||
|
|
||||||
|
suspend fun foo() {
|
||||||
|
baz(::bar<caret>)
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Add parameter to function 'baz'" "true"
|
||||||
|
suspend fun bar(): Int = 42
|
||||||
|
|
||||||
|
suspend fun baz(kSuspendFunction0: suspend () -> Int) {}
|
||||||
|
|
||||||
|
suspend fun foo() {
|
||||||
|
baz(::bar)
|
||||||
|
}
|
||||||
@@ -2323,6 +2323,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterLongNameRuntime.kt");
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterLongNameRuntime.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("addFunctionParameterWithAnonymousFunction.kt")
|
||||||
|
public void testAddFunctionParameterWithAnonymousFunction() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithAnonymousFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("addFunctionParameterWithFunctionReference.kt")
|
||||||
|
public void testAddFunctionParameterWithFunctionReference() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithFunctionReference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("addFunctionParameterWithLambda.kt")
|
||||||
|
public void testAddFunctionParameterWithLambda() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithLambda.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("addFunctionParameterWithSmartCast.kt")
|
@TestMetadata("addFunctionParameterWithSmartCast.kt")
|
||||||
public void testAddFunctionParameterWithSmartCast() throws Exception {
|
public void testAddFunctionParameterWithSmartCast() throws Exception {
|
||||||
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithSmartCast.kt");
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithSmartCast.kt");
|
||||||
@@ -2333,6 +2348,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithSmartcastedArgument.kt");
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithSmartcastedArgument.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("addFunctionParameterWithSuspendFunctionReference.kt")
|
||||||
|
public void testAddFunctionParameterWithSuspendFunctionReference() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithSuspendFunctionReference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("addNothingReturnType.kt")
|
@TestMetadata("addNothingReturnType.kt")
|
||||||
public void testAddNothingReturnType() throws Exception {
|
public void testAddNothingReturnType() throws Exception {
|
||||||
runTest("idea/testData/quickfix/changeSignature/addNothingReturnType.kt");
|
runTest("idea/testData/quickfix/changeSignature/addNothingReturnType.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user