From 69e800c6958d042cfe956bd9990d27f3b9ab8dc6 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Mon, 30 Dec 2019 18:50:51 +0300 Subject: [PATCH] Remove dependencies on data flow aware expression type in BindingContext `BindingContext.getType`'s behaviour on argument expression has been different for OI and NI for some time. New inference used to rewrite argument's type after smartcast, which led to missing subsequent smartcasts in some cases. This commit makes retrieval of smartcasted argument type explicit. --- .../idea/quickfix/AddFunctionParametersFix.kt | 2 +- .../quickfix/ChangeFunctionSignatureFix.kt | 7 ++++-- .../callableBuilder/typeUtils.kt | 23 +---------------- .../CreateCallableFromCallActionFactory.kt | 7 +++++- .../quickfix/quickfixUtil/quickfixUtil.kt | 25 +++++++++++++++++++ .../resolve/AbstractPartialBodyResolveTest.kt | 5 +++- .../newJ2k/postProcessing/IfToElvis.kt | 2 +- 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt index 67264111424..7e659479cc1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.CollectingNameValidator -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getDataFlowAwareTypes +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.getDataFlowAwareTypes import org.jetbrains.kotlin.idea.refactoring.changeSignature.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt index c64cab02b3e..bdb3e865f62 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.getDataFlowAwareTypes import org.jetbrains.kotlin.idea.refactoring.canRefactor import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor @@ -102,8 +103,10 @@ abstract class ChangeFunctionSignatureFix( val call = originalElement.getCall(bindingContext) ?: return null val argumentToParameter = call.mapArgumentsToParameters(functionDescriptor) val hasTypeMismatches = argumentToParameter.any { (argument, parameter) -> - val argumentType = argument.getArgumentExpression()?.let { bindingContext.getType(it) } - argumentType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameter.type) + val argumentTypes = argument.getArgumentExpression()?.let { getDataFlowAwareTypes(it, bindingContext) } + argumentTypes?.none { dataFlowAwareType -> + KotlinTypeChecker.DEFAULT.isSubtypeOf(dataFlowAwareType, parameter.type) + } ?: true } return AddFunctionParametersFix(originalElement, functionDescriptor, hasTypeMismatches) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index 64721ec0129..4fc7c130541 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -9,12 +9,9 @@ import com.intellij.refactoring.psi.SearchUtils import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.cfg.pseudocode.* 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.project.builtIns -import org.jetbrains.kotlin.idea.project.languageVersionSettings +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.getDataFlowAwareTypes import org.jetbrains.kotlin.idea.references.KtSimpleNameReference -import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName @@ -24,9 +21,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope @@ -35,7 +30,6 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.supertypes -import org.jetbrains.kotlin.utils.ifEmpty import java.util.* internal operator fun KotlinType.contains(inner: KotlinType): Boolean { @@ -335,18 +329,3 @@ private fun TypePredicate.getRepresentativeTypes(): Set { else -> throw AssertionError("Invalid type predicate: $this") } } - -fun getDataFlowAwareTypes( - expression: KtExpression, - bindingContext: BindingContext = expression.analyze(), - originalType: KotlinType? = bindingContext.getType(expression) -): Collection { - if (originalType == null) return emptyList() - val dataFlowInfo = bindingContext.getDataFlowInfoAfter(expression) - val dataFlowValueFactory = expression.getResolutionFacade().frontendService() - val expressionType = bindingContext.getType(expression) ?: return listOf(originalType) - val dataFlowValue = dataFlowValueFactory.createDataFlowValue( - expression, expressionType, bindingContext, expression.getResolutionFacade().moduleDescriptor - ) - return dataFlowInfo.getCollectedTypes(dataFlowValue, expression.languageVersionSettings).ifEmpty { listOf(originalType) } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt index 164f13b909c..006e1b7aab3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.getDataFlowAwareTypes import org.jetbrains.kotlin.idea.refactoring.canRefactor import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass @@ -119,7 +120,11 @@ sealed class CreateCallableFromCallActionFactory( is ReceiverValue -> { val originalType = receiver.type val finalType = if (receiver is ExpressionReceiver) { - getDataFlowAwareTypes(receiver.expression, context, originalType).firstOrNull() ?: originalType + getDataFlowAwareTypes( + receiver.expression, + context, + originalType + ).firstOrNull() ?: originalType } else originalType TypeInfo(finalType, Variance.IN_VARIANCE) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil/quickfixUtil.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil/quickfixUtil.kt index 650c7b8f48b..65c4c4a8c90 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil/quickfixUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil/quickfixUtil.kt @@ -19,11 +19,21 @@ package org.jetbrains.kotlin.idea.quickfix.quickfixUtil import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.PsiElement import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory +import org.jetbrains.kotlin.idea.resolve.frontendService +import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPrimaryConstructor import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.ifEmpty inline fun Diagnostic.createIntentionForFirstParentOfType( factory: (T) -> KotlinQuickFixAction? @@ -40,3 +50,18 @@ fun KtPrimaryConstructor.addConstructorKeyword(): PsiElement? { val keyword = KtPsiFactory(this).createConstructorKeyword() return addAfter(keyword, modifierList ?: return null) } + +fun getDataFlowAwareTypes( + expression: KtExpression, + bindingContext: BindingContext = expression.analyze(), + originalType: KotlinType? = bindingContext.getType(expression) +): Collection { + if (originalType == null) return emptyList() + val dataFlowInfo = bindingContext.getDataFlowInfoAfter(expression) + val dataFlowValueFactory = expression.getResolutionFacade().frontendService() + val expressionType = bindingContext.getType(expression) ?: return listOf(originalType) + val dataFlowValue = dataFlowValueFactory.createDataFlowValue( + expression, expressionType, bindingContext, expression.getResolutionFacade().moduleDescriptor + ) + return dataFlowInfo.getCollectedTypes(dataFlowValue, expression.languageVersionSettings).ifEmpty { listOf(originalType) } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractPartialBodyResolveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractPartialBodyResolveTest.kt index 537d59b863f..a2c4c41736d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractPartialBodyResolveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractPartialBodyResolveTest.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.getDataFlowAwareTypes import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.idea.test.configureCompilerOptions @@ -148,7 +149,9 @@ abstract class AbstractPartialBodyResolveTest : KotlinLightCodeInsightFixtureTes } else { expression } - val type = bindingContext.getType(expressionWithType) + + val nonDataFlowAwareType = bindingContext.getType(expressionWithType) + val type = getDataFlowAwareTypes(expressionWithType, bindingContext, nonDataFlowAwareType).singleOrNull() ?: nonDataFlowAwareType return ResolveData(target, type, processedStatements) } diff --git a/nj2k/testData/newJ2k/postProcessing/IfToElvis.kt b/nj2k/testData/newJ2k/postProcessing/IfToElvis.kt index eb61b93373f..cd3cf237041 100644 --- a/nj2k/testData/newJ2k/postProcessing/IfToElvis.kt +++ b/nj2k/testData/newJ2k/postProcessing/IfToElvis.kt @@ -1,5 +1,5 @@ internal class C { - fun foo(s: String?): String? { + fun foo(s: String?): String { return s ?: "" } } \ No newline at end of file