Convert KFunction to Function in type mismatch fixes #KT-16770 Fixed
This commit is contained in:
+41
-11
@@ -19,7 +19,11 @@ package org.jetbrains.kotlin.idea.quickfix
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.*
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.BuiltInAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
@@ -41,15 +45,29 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isInterface
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
import java.util.*
|
||||
|
||||
//TODO: should use change signature to deal with cases of multiple overridden descriptors
|
||||
class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
|
||||
private fun KotlinType.dropParameterNameAnnotations(): KotlinType {
|
||||
return KotlinTypeFactory.simpleNotNullType(annotations, constructor.declarationDescriptor as ClassDescriptor, arguments.map {
|
||||
TypeProjectionImpl(
|
||||
it.projectionKind,
|
||||
it.type.replaceAnnotations(AnnotationsImpl(it.type.annotations.filter { it !is BuiltInAnnotationDescriptor }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
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> {
|
||||
val actions = LinkedList<IntentionAction>()
|
||||
|
||||
@@ -142,6 +160,22 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
}
|
||||
}
|
||||
|
||||
fun <D : KtCallableDeclaration> addChangeTypeFix(
|
||||
callable: D,
|
||||
expressionType: KotlinType,
|
||||
createFix: (D, KotlinType) -> KotlinQuickFixAction<KtCallableDeclaration>
|
||||
) {
|
||||
val scope = callable.getResolutionScope(context, callable.getResolutionFacade())
|
||||
val typeToInsert = expressionType.approximateWithResolvableType(scope, false)
|
||||
if (typeToInsert.constructor.declarationDescriptor?.getFunctionalClassKind() == FunctionClassDescriptor.Kind.KFunction) {
|
||||
val reflectType = typeToInsert.dropParameterNameAnnotations()
|
||||
actions.add(createFix(callable, reflectType.reflectToRegularFunctionType()))
|
||||
actions.add(createFix(callable, reflectType))
|
||||
} else {
|
||||
actions.add(createFix(callable, typeToInsert))
|
||||
}
|
||||
}
|
||||
|
||||
// Property initializer type mismatch property type:
|
||||
val property = PsiTreeUtil.getParentOfType(diagnosticElement, KtProperty::class.java)
|
||||
if (property != null) {
|
||||
@@ -150,9 +184,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
if (QuickFixUtil.canEvaluateTo(initializer, diagnosticElement)
|
||||
|| getter != null && QuickFixUtil.canFunctionOrGetterReturnExpression(getter, diagnosticElement)
|
||||
) {
|
||||
val scope = property.getResolutionScope(context, property.getResolutionFacade())
|
||||
val typeToInsert = expressionType.approximateWithResolvableType(scope, false)
|
||||
actions.add(ChangeVariableTypeFix(property, typeToInsert))
|
||||
addChangeTypeFix(property, expressionType, ::ChangeVariableTypeFix)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,9 +197,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
else
|
||||
PsiTreeUtil.getParentOfType(diagnosticElement, KtFunction::class.java, true)
|
||||
if (function is KtFunction && QuickFixUtil.canFunctionOrGetterReturnExpression(function, diagnosticElement)) {
|
||||
val scope = function.getResolutionScope(context, function.getResolutionFacade())
|
||||
val typeToInsert = expressionType.approximateWithResolvableType(scope, false)
|
||||
actions.add(ChangeCallableReturnTypeFix.ForEnclosing(function, typeToInsert))
|
||||
addChangeTypeFix(function, expressionType, ChangeCallableReturnTypeFix::ForEnclosing)
|
||||
}
|
||||
|
||||
// Fixing overloaded operators:
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change return type of enclosing function 'myFunction' to '(Int) -> Boolean'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
fun myFunction(s: String): (String, Int) -> Boolean = <caret>s::verifyData
|
||||
}
|
||||
|
||||
fun String.verifyData(a: Int) = this.length > a
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change return type of enclosing function 'myFunction' to '(Int) -> Boolean'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
fun myFunction(s: String): (Int) -> Boolean = s::verifyData
|
||||
}
|
||||
|
||||
fun String.verifyData(a: Int) = this.length > a
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change type of 'myFunction' to '(Int, (Int) -> Boolean) -> Boolean'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int, Int) -> Int = <caret>::verifyData
|
||||
}
|
||||
|
||||
fun verifyData(a: Int, b: (Int) -> Boolean) = b(a)
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change type of 'myFunction' to '(Int, (Int) -> Boolean) -> Boolean'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int, (Int) -> Boolean) -> Boolean = ::verifyData
|
||||
}
|
||||
|
||||
fun verifyData(a: Int, b: (Int) -> Boolean) = b(a)
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Change type of 'myFunction' to '(Int) -> KFunction0<Boolean>'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int, Int) -> Int = <caret>::verifyData
|
||||
}
|
||||
|
||||
fun Int.internalVerifyData() = this > 0
|
||||
|
||||
fun verifyData(a: Int) = a::internalVerifyData
|
||||
@@ -0,0 +1,12 @@
|
||||
import kotlin.reflect.KFunction0
|
||||
|
||||
// "Change type of 'myFunction' to '(Int) -> KFunction0<Boolean>'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int) -> KFunction0<Boolean> = ::verifyData
|
||||
}
|
||||
|
||||
fun Int.internalVerifyData() = this > 0
|
||||
|
||||
fun verifyData(a: Int) = a::internalVerifyData
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change type of 'myFunction' to 'KFunction2<Int, Int, Boolean>'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int, Int) -> Int = <caret>::verifyData
|
||||
}
|
||||
|
||||
fun verifyData(a: Int, b: Int) = (a > 10 && b > 10)
|
||||
@@ -0,0 +1,10 @@
|
||||
import kotlin.reflect.KFunction2
|
||||
|
||||
// "Change type of 'myFunction' to 'KFunction2<Int, Int, Boolean>'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: KFunction2<Int, Int, Boolean> = ::verifyData
|
||||
}
|
||||
|
||||
fun verifyData(a: Int, b: Int) = (a > 10 && b > 10)
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change type of 'myFunction' to '(Int, Int) -> Boolean'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int, Int) -> Int = <caret>::verifyData
|
||||
}
|
||||
|
||||
fun verifyData(a: Int, b: Int) = (a > 10 && b > 10)
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Change type of 'myFunction' to '(Int, Int) -> Boolean'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var myFunction: (Int, Int) -> Boolean = ::verifyData
|
||||
}
|
||||
|
||||
fun verifyData(a: Int, b: Int) = (a > 10 && b > 10)
|
||||
@@ -10858,6 +10858,31 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
runTest("idea/testData/quickfix/typeMismatch/expectedParameterTypeMismatchLongNameRuntime.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionExtensionType.kt")
|
||||
public void testFunctionExtensionType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/functionExtensionType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionNestedType.kt")
|
||||
public void testFunctionNestedType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/functionNestedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionNestedType2.kt")
|
||||
public void testFunctionNestedType2() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/functionNestedType2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionReflectType.kt")
|
||||
public void testFunctionReflectType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/functionReflectType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionType.kt")
|
||||
public void testFunctionType() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/functionType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasNextFunctionReturnTypeMismatch.kt")
|
||||
public void testHasNextFunctionReturnTypeMismatch() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/hasNextFunctionReturnTypeMismatch.kt");
|
||||
|
||||
Reference in New Issue
Block a user