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 {
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
}
@@ -31,11 +31,11 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
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.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
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.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
@@ -127,9 +127,9 @@ fun KtExpression.guessTypes(
// if we know the actual type of the expression
val theType1 = context.getType(this)
if (theType1 != null) {
val dataFlowInfo = context[BindingContext.EXPRESSION_TYPE_INFO, this]?.dataFlowInfo
val possibleTypes = dataFlowInfo?.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context, module))
return if (possibleTypes != null && possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1)
val dataFlowInfo = context.getDataFlowInfo(this)
val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context, module))
return if (possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1)
}
// 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.DescriptorToSourceUtils
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.model.ResolvedCall
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> {
val typeInfo = context[BindingContext.EXPRESSION_TYPE_INFO, expression] ?: return emptySet()
val dataFlowInfo = context.getDataFlowInfo(expression)
(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 containingDescriptor = expression.getResolutionScope(context, expression.getResolutionFacade()).ownerDescriptor
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context, containingDescriptor)
return typeInfo.dataFlowInfo.getPossibleTypes(dataFlowValue)
return dataFlowInfo.getPossibleTypes(dataFlowValue)
}
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.isInsideOf
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.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke
@@ -270,6 +271,7 @@ private fun suggestParameterType(
originalDescriptor.valueParameters.map { it.type },
originalDescriptor.returnType ?: builtIns.defaultReturnType)
}
parameterExpression != null ->
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null)
?: bindingContext.getType(parameterExpression)
@@ -277,17 +279,19 @@ private fun suggestParameterType(
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
}
?: if (receiverToExtract.exists()) receiverToExtract.type else null
receiverToExtract is ImplicitReceiver -> {
val calleeExpression = resolvedCall!!.call.calleeExpression
val typeByDataFlowInfo = if (useSmartCastsIfPossible) {
bindingContext[BindingContext.EXPRESSION_TYPE_INFO, calleeExpression]?.dataFlowInfo?.let { dataFlowInfo ->
val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValueForStableReceiver(receiverToExtract))
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
}
val dataFlowInfo = bindingContext.getDataFlowInfo(resolvedCall!!.call.callElement)
val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValueForStableReceiver(receiverToExtract))
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
} else null
typeByDataFlowInfo ?: receiverToExtract.type
}
receiverToExtract.exists() -> receiverToExtract.type
else -> null
} ?: 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);
}
@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")
public void testSubstituteExpectedType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/functionCall/SubstituteExpectedType.kt");