Convert KFunction to Function in type mismatch fixes #KT-16770 Fixed

This commit is contained in:
Mikhail Glukhikh
2018-05-03 21:03:37 +03:00
parent 25609f1159
commit 8c3e787584
12 changed files with 154 additions and 11 deletions
@@ -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)
+8
View File
@@ -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");