KT-10006 Parameter Info does not work for smart cast receiver
#KT-10006 Fixed
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -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
|
||||||
|
|||||||
+4
-3
@@ -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> {
|
||||||
|
|||||||
+8
-4
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user