KT-10006 Parameter Info does not work for smart cast receiver

#KT-10006 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-11-16 19:38:28 +03:00
parent 4e44466cf9
commit 603bcaa039
7 changed files with 56 additions and 12 deletions
@@ -79,7 +79,12 @@ public fun BindingTrace.recordScope(scope: LexicalScope, element: KtElement?) {
public fun BindingContext.getDataFlowInfo(position: PsiElement): DataFlowInfo { public fun BindingContext.getDataFlowInfo(position: PsiElement): DataFlowInfo {
for (element in position.parentsWithSelf) { for (element in position.parentsWithSelf) {
(element as? KtExpression)?.let { this[BindingContext.EXPRESSION_TYPE_INFO, it] }?.let { return it.dataFlowInfo } (element as? KtExpression)?.let {
val parent = it.parent
//TODO: it's a hack because KotlinTypeInfo with wrong DataFlowInfo stored for call expression after qualifier
if (parent is KtQualifiedExpression && it == parent.selectorExpression) return@let null
this[BindingContext.EXPRESSION_TYPE_INFO, it]
}?.let { return it.dataFlowInfo }
} }
return DataFlowInfo.EMPTY return DataFlowInfo.EMPTY
} }
@@ -31,11 +31,11 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS 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.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
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.calls.smartcasts.DataFlowValueFactory
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
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
@@ -127,9 +127,9 @@ 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) { if (theType1 != null) {
val dataFlowInfo = context[BindingContext.EXPRESSION_TYPE_INFO, this]?.dataFlowInfo val dataFlowInfo = context.getDataFlowInfo(this)
val possibleTypes = dataFlowInfo?.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context, module)) val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context, module))
return if (possibleTypes != null && possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1) return if (possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1)
} }
// expression has an expected type // expression has an expected type
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.psi.psiUtil.isInsideOf
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
@@ -175,16 +176,16 @@ data class ExtractionData(
} }
fun getPossibleTypes(expression: KtExpression, resolvedCall: ResolvedCall<*>?, context: BindingContext): Set<KotlinType> { fun getPossibleTypes(expression: KtExpression, resolvedCall: ResolvedCall<*>?, context: BindingContext): Set<KotlinType> {
val typeInfo = context[BindingContext.EXPRESSION_TYPE_INFO, expression] ?: return emptySet() val dataFlowInfo = context.getDataFlowInfo(expression)
(resolvedCall?.getImplicitReceiverValue() as? ImplicitReceiver)?.let { (resolvedCall?.getImplicitReceiverValue() as? ImplicitReceiver)?.let {
return typeInfo.dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValueForStableReceiver(it)) return dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValueForStableReceiver(it))
} }
val type = resolvedCall?.resultingDescriptor?.returnType ?: return emptySet() val type = resolvedCall?.resultingDescriptor?.returnType ?: return emptySet()
val containingDescriptor = expression.getResolutionScope(context, expression.getResolutionFacade()).ownerDescriptor val containingDescriptor = expression.getResolutionScope(context, expression.getResolutionFacade()).ownerDescriptor
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context, containingDescriptor) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context, containingDescriptor)
return typeInfo.dataFlowInfo.getPossibleTypes(dataFlowValue) return dataFlowInfo.getPossibleTypes(dataFlowValue)
} }
fun getBrokenReferencesInfo(body: KtBlockExpression): List<ResolvedReferenceInfo> { fun getBrokenReferencesInfo(body: KtBlockExpression): List<ResolvedReferenceInfo> {
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.psi.psiUtil.isInsideOf import org.jetbrains.kotlin.psi.psiUtil.isInsideOf
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke
@@ -270,6 +271,7 @@ private fun suggestParameterType(
originalDescriptor.valueParameters.map { it.type }, originalDescriptor.valueParameters.map { it.type },
originalDescriptor.returnType ?: builtIns.defaultReturnType) originalDescriptor.returnType ?: builtIns.defaultReturnType)
} }
parameterExpression != null -> parameterExpression != null ->
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null) (if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null)
?: bindingContext.getType(parameterExpression) ?: bindingContext.getType(parameterExpression)
@@ -277,17 +279,19 @@ private fun suggestParameterType(
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType (bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
} }
?: if (receiverToExtract.exists()) receiverToExtract.type else null ?: if (receiverToExtract.exists()) receiverToExtract.type else null
receiverToExtract is ImplicitReceiver -> { receiverToExtract is ImplicitReceiver -> {
val calleeExpression = resolvedCall!!.call.calleeExpression val calleeExpression = resolvedCall!!.call.calleeExpression
val typeByDataFlowInfo = if (useSmartCastsIfPossible) { val typeByDataFlowInfo = if (useSmartCastsIfPossible) {
bindingContext[BindingContext.EXPRESSION_TYPE_INFO, calleeExpression]?.dataFlowInfo?.let { dataFlowInfo -> val dataFlowInfo = bindingContext.getDataFlowInfo(resolvedCall!!.call.callElement)
val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValueForStableReceiver(receiverToExtract)) val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValueForStableReceiver(receiverToExtract))
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
}
} else null } else null
typeByDataFlowInfo ?: receiverToExtract.type typeByDataFlowInfo ?: receiverToExtract.type
} }
receiverToExtract.exists() -> receiverToExtract.type receiverToExtract.exists() -> receiverToExtract.type
else -> null else -> null
} ?: builtIns.defaultParameterType } ?: builtIns.defaultParameterType
} }
@@ -0,0 +1,11 @@
interface I
fun I.foo(p: Int): Boolean = true
fun foo(o: Any) {
if (o is I) {
o.foo(<caret>)
}
}
//Text: (<highlight>p: Int</highlight>), Disabled: false, Strikeout: false, Green: true
@@ -0,0 +1,11 @@
interface I
fun I.foo(p: Int): Boolean = true
fun foo(o: Any?) {
if (o is I?) {
o?.foo(<caret>)
}
}
//Text: (<highlight>p: Int</highlight>), Disabled: false, Strikeout: false, Green: true
@@ -214,6 +214,18 @@ public class ParameterInfoTestGenerated extends AbstractParameterInfoTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SmartCastReceiver.kt")
public void testSmartCastReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/functionCall/SmartCastReceiver.kt");
doTest(fileName);
}
@TestMetadata("SmartCastReceiver2.kt")
public void testSmartCastReceiver2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/functionCall/SmartCastReceiver2.kt");
doTest(fileName);
}
@TestMetadata("SubstituteExpectedType.kt") @TestMetadata("SubstituteExpectedType.kt")
public void testSubstituteExpectedType() throws Exception { public void testSubstituteExpectedType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/functionCall/SubstituteExpectedType.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/functionCall/SubstituteExpectedType.kt");