Add Parameter Fix: Support smart casts
#KT-24207 Fixed
This commit is contained in:
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
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.quickfix.createFromUsage.callableBuilder.getDataFlowAwareTypes
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
||||||
import org.jetbrains.kotlin.psi.KtCallElement
|
import org.jetbrains.kotlin.psi.KtCallElement
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
@@ -141,7 +142,7 @@ 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 { it.analyze().getType(it) } ?: functionDescriptor.builtIns.nullableAnyType
|
val type = expression?.let { getDataFlowAwareTypes(it).firstOrNull() } ?: functionDescriptor.builtIns.nullableAnyType
|
||||||
return KotlinParameterInfo(functionDescriptor, -1, name, KotlinTypeInfo(false, null))
|
return KotlinParameterInfo(functionDescriptor, -1, name, KotlinTypeInfo(false, null))
|
||||||
.apply { currentTypeInfo = KotlinTypeInfo(false, type) }
|
.apply { currentTypeInfo = KotlinTypeInfo(false, type) }
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-6
@@ -20,6 +20,7 @@ import com.intellij.refactoring.psi.SearchUtils
|
|||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.*
|
import org.jetbrains.kotlin.cfg.pseudocode.*
|
||||||
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.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.project.builtIns
|
import org.jetbrains.kotlin.idea.project.builtIns
|
||||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
@@ -44,6 +45,7 @@ import org.jetbrains.kotlin.types.*
|
|||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
|
import org.jetbrains.kotlin.utils.ifEmpty
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal operator fun KotlinType.contains(inner: KotlinType): Boolean {
|
internal operator fun KotlinType.contains(inner: KotlinType): Boolean {
|
||||||
@@ -162,12 +164,7 @@ fun KtExpression.guessTypes(
|
|||||||
// if we know the actual type of the expression
|
// if we know the actual type of the expression
|
||||||
val theType1 = context.getType(this)
|
val theType1 = context.getType(this)
|
||||||
if (theType1 != null && isAcceptable(theType1)) {
|
if (theType1 != null && isAcceptable(theType1)) {
|
||||||
val dataFlowInfo = context.getDataFlowInfoAfter(this)
|
return getDataFlowAwareTypes(this, context).toTypedArray()
|
||||||
val dataFlowValueFactory = this.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
|
||||||
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(this, theType1, context, module)
|
|
||||||
|
|
||||||
val possibleTypes = dataFlowInfo.getCollectedTypes(dataFlowValue, languageVersionSettings)
|
|
||||||
return if (possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,3 +340,15 @@ private fun TypePredicate.getRepresentativeTypes(): Set<KotlinType> {
|
|||||||
else -> throw AssertionError("Invalid type predicate: ${this}")
|
else -> throw AssertionError("Invalid type predicate: ${this}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getDataFlowAwareTypes(expression: KtExpression, bindingContext: BindingContext = expression.analyze()): List<KotlinType> {
|
||||||
|
val originalType = bindingContext.getType(expression) ?: return emptyList()
|
||||||
|
val dataFlowInfo = bindingContext.getDataFlowInfoAfter(expression)
|
||||||
|
val dataFlowValueFactory = expression.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||||
|
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(
|
||||||
|
expression,
|
||||||
|
bindingContext.getType(expression)!!,
|
||||||
|
bindingContext, expression.getResolutionFacade().moduleDescriptor
|
||||||
|
)
|
||||||
|
return dataFlowInfo.getCollectedTypes(dataFlowValue, expression.languageVersionSettings).ifEmpty { listOf(originalType) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// "Add parameter to function 'fooFun'" "true"
|
||||||
|
sealed class Foo {
|
||||||
|
class SubFoo : Foo()
|
||||||
|
class Sub2Foo : Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fooFun(): Int = 1
|
||||||
|
|
||||||
|
val subFoo: Foo = Foo.SubFoo()
|
||||||
|
val bar = when(subFoo){
|
||||||
|
is Foo.SubFoo -> fooFun(<caret>subFoo)
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// "Add parameter to function 'fooFun'" "true"
|
||||||
|
sealed class Foo {
|
||||||
|
class SubFoo : Foo()
|
||||||
|
class Sub2Foo : Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fooFun(subFoo: Foo.SubFoo): Int = 1
|
||||||
|
|
||||||
|
val subFoo: Foo = Foo.SubFoo()
|
||||||
|
val bar = when(subFoo){
|
||||||
|
is Foo.SubFoo -> fooFun(<caret>subFoo)
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
@@ -1509,6 +1509,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterLongNameRuntime.kt");
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterLongNameRuntime.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("addFunctionParameterWithSmartCast.kt")
|
||||||
|
public void testAddFunctionParameterWithSmartCast() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/changeSignature/addFunctionParameterWithSmartCast.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");
|
||||||
@@ -3086,8 +3091,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
|
|
||||||
@TestMetadata("nullableReceiver.kt")
|
@TestMetadata("nullableReceiver.kt")
|
||||||
public void testNullableReceiver() throws Exception {
|
public void testNullableReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt");
|
runTest("idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt");
|
||||||
doTest(fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("objectMemberFunNoReceiver.kt")
|
@TestMetadata("objectMemberFunNoReceiver.kt")
|
||||||
@@ -4607,8 +4611,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
|
|
||||||
@TestMetadata("nullableReceiver.kt")
|
@TestMetadata("nullableReceiver.kt")
|
||||||
public void testNullableReceiver() throws Exception {
|
public void testNullableReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt");
|
runTest("idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt");
|
||||||
doTest(fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("objectMemberValNoReceiver.kt")
|
@TestMetadata("objectMemberValNoReceiver.kt")
|
||||||
|
|||||||
Reference in New Issue
Block a user