extensionsUtils.kt uses FuzzyType + more correct treatment of receiver nullability there
This commit is contained in:
+32
-35
@@ -36,6 +36,7 @@ import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
import org.jetbrains.jet.lexer.JetTokens
|
||||
import org.jetbrains.jet.plugin.util.CallType
|
||||
import org.jetbrains.jet.plugin.util.substituteExtensionIfCallable
|
||||
|
||||
public class ReferenceVariantsHelper(
|
||||
@@ -43,12 +44,6 @@ public class ReferenceVariantsHelper(
|
||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
|
||||
) {
|
||||
|
||||
public enum class CallType {
|
||||
NORMAL
|
||||
INFIX
|
||||
SAFE
|
||||
}
|
||||
|
||||
public data class ReceiversData(
|
||||
public val receivers: Collection<ReceiverValue>,
|
||||
public val callType: CallType
|
||||
@@ -114,7 +109,7 @@ public class ReferenceVariantsHelper(
|
||||
variant.getMemberScope().getDescriptorsFiltered(mask, nameFilter).filterTo(descriptors, ::filterIfInfix)
|
||||
}
|
||||
|
||||
descriptors.addCallableExtensions(resolutionScope, receiverValue, dataFlowInfo, callType == CallType.INFIX, kindFilter, nameFilter)
|
||||
descriptors.addCallableExtensions(resolutionScope, receiverValue, dataFlowInfo, callType, kindFilter, nameFilter)
|
||||
}
|
||||
|
||||
return descriptors
|
||||
@@ -132,7 +127,7 @@ public class ReferenceVariantsHelper(
|
||||
|
||||
for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) {
|
||||
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) {
|
||||
descriptorsSet.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, false))
|
||||
descriptorsSet.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, CallType.NORMAL))
|
||||
}
|
||||
else {
|
||||
descriptorsSet.add(descriptor)
|
||||
@@ -165,42 +160,17 @@ public class ReferenceVariantsHelper(
|
||||
return type
|
||||
}
|
||||
|
||||
private fun getReferenceVariantsReceiver(expression: JetSimpleNameExpression): Pair<JetExpression, CallType>? {
|
||||
val receiverExpression = expression.getReceiverExpression() ?: return null
|
||||
val parent = expression.getParent()
|
||||
val callType = when (parent) {
|
||||
is JetBinaryExpression -> CallType.INFIX
|
||||
|
||||
is JetCallExpression -> {
|
||||
if ((parent.getParent() as JetQualifiedExpression).getOperationSign() == JetTokens.SAFE_ACCESS)
|
||||
CallType.SAFE
|
||||
else
|
||||
CallType.NORMAL
|
||||
}
|
||||
|
||||
is JetQualifiedExpression -> {
|
||||
if (parent.getOperationSign() == JetTokens.SAFE_ACCESS)
|
||||
CallType.SAFE
|
||||
else
|
||||
CallType.NORMAL
|
||||
}
|
||||
|
||||
else -> return null
|
||||
}
|
||||
return receiverExpression to callType
|
||||
}
|
||||
|
||||
private fun MutableCollection<DeclarationDescriptor>.addCallableExtensions(
|
||||
resolutionScope: JetScope,
|
||||
receiver: ReceiverValue,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
isInfixCall: Boolean,
|
||||
callType: CallType,
|
||||
kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean
|
||||
) {
|
||||
if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) {
|
||||
for (callable in resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter)) {
|
||||
addAll((callable as CallableDescriptor).substituteExtensionIfCallable(receiver, isInfixCall, context, dataFlowInfo))
|
||||
addAll((callable as CallableDescriptor).substituteExtensionIfCallable(receiver, callType, context, dataFlowInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,4 +182,31 @@ public class ReferenceVariantsHelper(
|
||||
val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf()
|
||||
return resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.PACKAGES, nameFilter).filter(visibilityFilter)
|
||||
}
|
||||
|
||||
class object {
|
||||
public fun getReferenceVariantsReceiver(expression: JetSimpleNameExpression): Pair<JetExpression, CallType>? {
|
||||
val receiverExpression = expression.getReceiverExpression() ?: return null
|
||||
val parent = expression.getParent()
|
||||
val callType = when (parent) {
|
||||
is JetBinaryExpression -> CallType.INFIX
|
||||
|
||||
is JetCallExpression -> {
|
||||
if ((parent.getParent() as JetQualifiedExpression).getOperationSign() == JetTokens.SAFE_ACCESS)
|
||||
CallType.SAFE
|
||||
else
|
||||
CallType.NORMAL
|
||||
}
|
||||
|
||||
is JetQualifiedExpression -> {
|
||||
if (parent.getOperationSign() == JetTokens.SAFE_ACCESS)
|
||||
CallType.SAFE
|
||||
else
|
||||
CallType.NORMAL
|
||||
}
|
||||
|
||||
else -> return null
|
||||
}
|
||||
return receiverExpression to callType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ import java.util.LinkedHashMap
|
||||
import org.jetbrains.jet.lang.types.Variance
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil
|
||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||
import org.jetbrains.jet.plugin.util.makeNullable
|
||||
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||
import org.jetbrains.jet.lang.types.typeUtil.isSubtypeOf
|
||||
|
||||
@@ -36,7 +34,6 @@ fun CallableDescriptor.fuzzyReturnType(): FuzzyType? {
|
||||
return FuzzyType(returnType, getTypeParameters())
|
||||
}
|
||||
|
||||
//TODO: replace code in extensionsUtils.kt with use of FuzzyType
|
||||
fun CallableDescriptor.fuzzyExtensionReceiverType(): FuzzyType? {
|
||||
val receiverParameter = getExtensionReceiverParameter()
|
||||
return if (receiverParameter != null) FuzzyType(receiverParameter.getType(), getTypeParameters()) else null
|
||||
@@ -64,10 +61,30 @@ class FuzzyType(
|
||||
}
|
||||
}
|
||||
|
||||
public fun matchedSubstitutor(expectedType: JetType): TypeSubstitutor? {
|
||||
public fun checkIsSubtypeOf(otherType: JetType): TypeSubstitutor?
|
||||
= matchedSubstitutor(otherType, MatchKind.IS_SUBTYPE)
|
||||
|
||||
public fun checkIsSuperTypeOf(otherType: JetType): TypeSubstitutor?
|
||||
= matchedSubstitutor(otherType, MatchKind.IS_SUPERTYPE)
|
||||
|
||||
private enum class MatchKind {
|
||||
IS_SUBTYPE
|
||||
IS_SUPERTYPE
|
||||
}
|
||||
|
||||
private fun matchedSubstitutor(otherType: JetType, matchKind: MatchKind): TypeSubstitutor? {
|
||||
if (type.isError()) return null
|
||||
if (otherType.isError()) return null
|
||||
|
||||
fun JetType.checkInheritance(otherType: JetType): Boolean {
|
||||
return when (matchKind) {
|
||||
MatchKind.IS_SUBTYPE -> this.isSubtypeOf(otherType)
|
||||
MatchKind.IS_SUPERTYPE -> otherType.isSubtypeOf(this)
|
||||
}
|
||||
}
|
||||
|
||||
if (usedTypeParameters == null || usedTypeParameters.isEmpty()) {
|
||||
return if (type.isSubtypeOf(expectedType)) TypeSubstitutor.EMPTY else null
|
||||
return if (type.checkInheritance(otherType)) TypeSubstitutor.EMPTY else null
|
||||
}
|
||||
|
||||
val constraintSystem = ConstraintSystemImpl()
|
||||
@@ -79,11 +96,15 @@ class FuzzyType(
|
||||
}
|
||||
constraintSystem.registerTypeVariables(typeVariables)
|
||||
|
||||
constraintSystem.addSubtypeConstraint(type, expectedType, ConstraintPosition.SPECIAL/*TODO?*/)
|
||||
when (matchKind) {
|
||||
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType, ConstraintPosition.SPECIAL)
|
||||
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType, type, ConstraintPosition.SPECIAL)
|
||||
}
|
||||
|
||||
if (constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true)) {
|
||||
val substitutor = constraintSystem.getResultingSubstitutor()
|
||||
val substitutedType = substitutor.substitute(type, Variance.INVARIANT/*TODO?*/)
|
||||
return if (substitutedType.isSubtypeOf(expectedType)) substitutor else null
|
||||
val substitutedType = substitutor.substitute(type, Variance.INVARIANT)
|
||||
return if (substitutedType != null && substitutedType.checkInheritance(otherType)) substitutor else null
|
||||
}
|
||||
else {
|
||||
return null
|
||||
|
||||
@@ -23,23 +23,19 @@ import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.calls.smartcasts.SmartCastUtils
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl
|
||||
import java.util.LinkedHashMap
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.jet.lang.types.Variance
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil
|
||||
import org.jetbrains.jet.utils.addIfNotNull
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
|
||||
public enum class CallType {
|
||||
NORMAL
|
||||
SAFE
|
||||
INFIX
|
||||
}
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallable(receivers: Collection<ReceiverValue>,
|
||||
context: BindingContext,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
isInfixCall: Boolean): Collection<CallableDescriptor> {
|
||||
val stream = receivers.stream().flatMap { substituteExtensionIfCallable(it, isInfixCall, context, dataFlowInfo).stream() }
|
||||
callType: CallType): Collection<CallableDescriptor> {
|
||||
val stream = receivers.stream().flatMap { substituteExtensionIfCallable(it, callType, context, dataFlowInfo).stream() }
|
||||
if (getTypeParameters().isEmpty()) { // optimization for non-generic callables
|
||||
return stream.firstOrNull()?.let { listOf(it) } ?: listOf()
|
||||
}
|
||||
@@ -49,24 +45,36 @@ public fun CallableDescriptor.substituteExtensionIfCallable(receivers: Collectio
|
||||
}
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): Collection<CallableDescriptor>
|
||||
= substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, false)
|
||||
= substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, CallType.NORMAL)
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
receiver: ReceiverValue,
|
||||
isInfixCall: Boolean,
|
||||
callType: CallType,
|
||||
bindingContext: BindingContext,
|
||||
dataFlowInfo: DataFlowInfo
|
||||
): Collection<CallableDescriptor> {
|
||||
val receiverParameter = getExtensionReceiverParameter()!!
|
||||
if (!receiver.exists()) return listOf()
|
||||
|
||||
if (isInfixCall && (this !is SimpleFunctionDescriptor || getValueParameters().size() != 1)) {
|
||||
if (callType == CallType.INFIX && (this !is SimpleFunctionDescriptor || getValueParameters().size() != 1)) {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
val substitutors = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo)
|
||||
.stream()
|
||||
.map { checkReceiverResolution(it, receiverParameter, getTypeParameters()) }
|
||||
var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo).stream()
|
||||
|
||||
if (callType == CallType.SAFE) {
|
||||
types = types.map { it.makeNotNullable() }
|
||||
}
|
||||
|
||||
val extensionReceiverType = fuzzyExtensionReceiverType()!!
|
||||
val substitutors = types
|
||||
.map {
|
||||
var substitutor = extensionReceiverType.checkIsSuperTypeOf(it)
|
||||
// check if we may fail due to receiver expression being nullable
|
||||
if (substitutor == null && TypeUtils.isNullableType(it) && !TypeUtils.isNullableType(extensionReceiverType.type)) {
|
||||
substitutor = extensionReceiverType.checkIsSuperTypeOf(it.makeNotNullable())
|
||||
}
|
||||
substitutor
|
||||
}
|
||||
.filterNotNull()
|
||||
if (getTypeParameters().isEmpty()) { // optimization for non-generic callables
|
||||
return if (substitutors.any()) listOf(this) else listOf()
|
||||
@@ -76,38 +84,3 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkReceiverResolution(
|
||||
receiverType: JetType,
|
||||
receiverParameter: ReceiverParameterDescriptor,
|
||||
typeParameters: List<TypeParameterDescriptor>
|
||||
): TypeSubstitutor? {
|
||||
val typeParamsInReceiver = HashSet<TypeParameterDescriptor>()
|
||||
typeParamsInReceiver.addUsedTypeParameters(receiverParameter.getType())
|
||||
|
||||
val constraintSystem = ConstraintSystemImpl()
|
||||
val typeVariables = LinkedHashMap<TypeParameterDescriptor, Variance>()
|
||||
for (typeParameter in typeParameters) {
|
||||
if (typeParamsInReceiver.contains(typeParameter)) {
|
||||
typeVariables[typeParameter] = Variance.INVARIANT
|
||||
}
|
||||
}
|
||||
constraintSystem.registerTypeVariables(typeVariables)
|
||||
|
||||
constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION)
|
||||
|
||||
if (constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true)) {
|
||||
return constraintSystem.getResultingSubstitutor()
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableSet<TypeParameterDescriptor>.addUsedTypeParameters(jetType: JetType) {
|
||||
addIfNotNull(jetType.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor)
|
||||
|
||||
for (argument in jetType.getArguments()) {
|
||||
addUsedTypeParameters(argument.getType())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user