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.
This commit is contained in:
Pavel Kirpichenkov
2019-12-30 18:50:51 +03:00
parent 4d11fac365
commit 69e800c695
7 changed files with 43 additions and 28 deletions
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
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.quickfix.quickfixUtil.getDataFlowAwareTypes
import org.jetbrains.kotlin.idea.refactoring.changeSignature.* import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters 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.canRefactor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
@@ -102,8 +103,10 @@ abstract class ChangeFunctionSignatureFix(
val call = originalElement.getCall(bindingContext) ?: return null val call = originalElement.getCall(bindingContext) ?: return null
val argumentToParameter = call.mapArgumentsToParameters(functionDescriptor) val argumentToParameter = call.mapArgumentsToParameters(functionDescriptor)
val hasTypeMismatches = argumentToParameter.any { (argument, parameter) -> val hasTypeMismatches = argumentToParameter.any { (argument, parameter) ->
val argumentType = argument.getArgumentExpression()?.let { bindingContext.getType(it) } val argumentTypes = argument.getArgumentExpression()?.let { getDataFlowAwareTypes(it, bindingContext) }
argumentType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameter.type) argumentTypes?.none { dataFlowAwareType ->
KotlinTypeChecker.DEFAULT.isSubtypeOf(dataFlowAwareType, parameter.type)
} ?: true
} }
return AddFunctionParametersFix(originalElement, functionDescriptor, hasTypeMismatches) return AddFunctionParametersFix(originalElement, functionDescriptor, hasTypeMismatches)
} }
@@ -9,12 +9,9 @@ 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.project.builtIns 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.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName 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.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext 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.bindingContextUtil.isUsedAsStatement
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope 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.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 {
@@ -335,18 +329,3 @@ 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(),
originalType: KotlinType? = bindingContext.getType(expression)
): Collection<KotlinType> {
if (originalType == null) return emptyList()
val dataFlowInfo = bindingContext.getDataFlowInfoAfter(expression)
val dataFlowValueFactory = expression.getResolutionFacade().frontendService<DataFlowValueFactory>()
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) }
}
@@ -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.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* 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.canRefactor
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
@@ -119,7 +120,11 @@ sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
is ReceiverValue -> { is ReceiverValue -> {
val originalType = receiver.type val originalType = receiver.type
val finalType = if (receiver is ExpressionReceiver) { val finalType = if (receiver is ExpressionReceiver) {
getDataFlowAwareTypes(receiver.expression, context, originalType).firstOrNull() ?: originalType getDataFlowAwareTypes(
receiver.expression,
context,
originalType
).firstOrNull() ?: originalType
} else originalType } else originalType
TypeInfo(finalType, Variance.IN_VARIANCE) TypeInfo(finalType, Variance.IN_VARIANCE)
} }
@@ -19,11 +19,21 @@ package org.jetbrains.kotlin.idea.quickfix.quickfixUtil
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic 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.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory 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.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType 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 <reified T : PsiElement> Diagnostic.createIntentionForFirstParentOfType( inline fun <reified T : PsiElement> Diagnostic.createIntentionForFirstParentOfType(
factory: (T) -> KotlinQuickFixAction<T>? factory: (T) -> KotlinQuickFixAction<T>?
@@ -40,3 +50,18 @@ fun KtPrimaryConstructor.addConstructorKeyword(): PsiElement? {
val keyword = KtPsiFactory(this).createConstructorKeyword() val keyword = KtPsiFactory(this).createConstructorKeyword()
return addAfter(keyword, modifierList ?: return null) return addAfter(keyword, modifierList ?: return null)
} }
fun getDataFlowAwareTypes(
expression: KtExpression,
bindingContext: BindingContext = expression.analyze(),
originalType: KotlinType? = bindingContext.getType(expression)
): Collection<KotlinType> {
if (originalType == null) return emptyList()
val dataFlowInfo = bindingContext.getDataFlowInfoAfter(expression)
val dataFlowValueFactory = expression.getResolutionFacade().frontendService<DataFlowValueFactory>()
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) }
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade 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.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.configureCompilerOptions import org.jetbrains.kotlin.idea.test.configureCompilerOptions
@@ -148,7 +149,9 @@ abstract class AbstractPartialBodyResolveTest : KotlinLightCodeInsightFixtureTes
} else { } else {
expression expression
} }
val type = bindingContext.getType(expressionWithType)
val nonDataFlowAwareType = bindingContext.getType(expressionWithType)
val type = getDataFlowAwareTypes(expressionWithType, bindingContext, nonDataFlowAwareType).singleOrNull() ?: nonDataFlowAwareType
return ResolveData(target, type, processedStatements) return ResolveData(target, type, processedStatements)
} }
+1 -1
View File
@@ -1,5 +1,5 @@
internal class C { internal class C {
fun foo(s: String?): String? { fun foo(s: String?): String {
return s ?: "" return s ?: ""
} }
} }