Fixed 'OnlyInputTypes' working with number types
This commit is contained in:
@@ -149,7 +149,7 @@ public class ArgumentTypeResolver {
|
|||||||
|
|
||||||
private void checkArgumentTypeWithNoCallee(CallResolutionContext<?> context, KtExpression argumentExpression) {
|
private void checkArgumentTypeWithNoCallee(CallResolutionContext<?> context, KtExpression argumentExpression) {
|
||||||
expressionTypingServices.getTypeInfo(argumentExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
|
expressionTypingServices.getTypeInfo(argumentExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
|
||||||
updateResultArgumentTypeIfNotDenotable(context, argumentExpression, null);
|
updateResultArgumentTypeIfNotDenotable(context, argumentExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isFunctionLiteralArgument(
|
public static boolean isFunctionLiteralArgument(
|
||||||
@@ -349,10 +349,9 @@ public class ArgumentTypeResolver {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public KotlinType updateResultArgumentTypeIfNotDenotable(
|
public KotlinType updateResultArgumentTypeIfNotDenotable(
|
||||||
@NotNull ResolutionContext context,
|
@NotNull ResolutionContext context,
|
||||||
@NotNull KtExpression expression,
|
@NotNull KtExpression expression
|
||||||
@Nullable KotlinType argumentType
|
|
||||||
) {
|
) {
|
||||||
KotlinType type = (argumentType != null) ? argumentType : context.trace.getType(expression);
|
KotlinType type = context.trace.getType(expression);
|
||||||
if (type != null && !type.getConstructor().isDenotable()) {
|
if (type != null && !type.getConstructor().isDenotable()) {
|
||||||
if (type.getConstructor() instanceof IntegerValueTypeConstructor) {
|
if (type.getConstructor() instanceof IntegerValueTypeConstructor) {
|
||||||
IntegerValueTypeConstructor constructor = (IntegerValueTypeConstructor) type.getConstructor();
|
IntegerValueTypeConstructor constructor = (IntegerValueTypeConstructor) type.getConstructor();
|
||||||
@@ -361,6 +360,6 @@ public class ArgumentTypeResolver {
|
|||||||
return primitiveType;
|
return primitiveType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return type;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ public class CallCompleter(
|
|||||||
// For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.),
|
// For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.),
|
||||||
// so while the expected type is not known, it's IntegerValueType(1), and should be updated when the expected type is known.
|
// so while the expected type is not known, it's IntegerValueType(1), and should be updated when the expected type is known.
|
||||||
if (recordedType != null && !recordedType.getConstructor().isDenotable()) {
|
if (recordedType != null && !recordedType.getConstructor().isDenotable()) {
|
||||||
updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression, updatedType)
|
updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression) ?: updatedType
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedType = updateRecordedTypeForArgument(updatedType, recordedType, expression, context.trace)
|
updatedType = updateRecordedTypeForArgument(updatedType, recordedType, expression, context.trace)
|
||||||
|
|||||||
+3
@@ -11,6 +11,9 @@ class D
|
|||||||
fun test1(a: A, b: B, c: C) {
|
fun test1(a: A, b: B, c: C) {
|
||||||
assertEquals1(a, b)
|
assertEquals1(a, b)
|
||||||
<!TYPE_INFERENCE_ONLY_INPUT_TYPES!>assertEquals1<!>(b, c)
|
<!TYPE_INFERENCE_ONLY_INPUT_TYPES!>assertEquals1<!>(b, c)
|
||||||
|
|
||||||
|
assertEquals1(3, 3)
|
||||||
|
assertEquals1(1 or 2, 2 or 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
|||||||
+19
-11
@@ -51,11 +51,7 @@ public class TypeBoundsImpl(
|
|||||||
bounds.add(bound)
|
bounds.add(bound)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filterBounds(bounds: Collection<Bound>, kind: BoundKind): Set<KotlinType> {
|
private fun filterBounds(bounds: Collection<Bound>, kind: BoundKind, errorValues: MutableCollection<KotlinType>? = null): Set<KotlinType> {
|
||||||
return filterBounds(bounds, kind, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun filterBounds(bounds: Collection<Bound>, kind: BoundKind, errorValues: MutableCollection<KotlinType>?): Set<KotlinType> {
|
|
||||||
val result = LinkedHashSet<KotlinType>()
|
val result = LinkedHashSet<KotlinType>()
|
||||||
for (bound in bounds) {
|
for (bound in bounds) {
|
||||||
if (bound.kind == kind) {
|
if (bound.kind == kind) {
|
||||||
@@ -106,7 +102,7 @@ public class TypeBoundsImpl(
|
|||||||
values.addAll(exactBounds)
|
values.addAll(exactBounds)
|
||||||
|
|
||||||
val (numberLowerBounds, generalLowerBounds) =
|
val (numberLowerBounds, generalLowerBounds) =
|
||||||
filterBounds(bounds, LOWER_BOUND, values).partition { it.getConstructor() is IntegerValueTypeConstructor }
|
filterBounds(bounds, LOWER_BOUND, values).partition { it.constructor is IntegerValueTypeConstructor }
|
||||||
|
|
||||||
val superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds)
|
val superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds)
|
||||||
if (tryPossibleAnswer(bounds, superTypeOfLowerBounds)) {
|
if (tryPossibleAnswer(bounds, superTypeOfLowerBounds)) {
|
||||||
@@ -146,15 +142,27 @@ public class TypeBoundsImpl(
|
|||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkOnlyInputTypes(bounds: Collection<Bound>, possibleAnswer: KotlinType): Boolean {
|
||||||
|
if (!typeVariable.hasOnlyInputTypesAnnotation()) return true
|
||||||
|
|
||||||
|
// Only type mentioned in bounds might be the result
|
||||||
|
val typesInBoundsSet = bounds.filter { it.isProper && it.constrainingType.constructor.isDenotable }.map { it.constrainingType }.toSet()
|
||||||
|
if (typesInBoundsSet.contains(possibleAnswer)) return true
|
||||||
|
|
||||||
|
// For non-denotable number types only, no valid types are mentioned, so common supertype is valid
|
||||||
|
val numberLowerBounds = filterBounds(bounds, LOWER_BOUND).filter { it.constructor is IntegerValueTypeConstructor }
|
||||||
|
val superTypeOfNumberLowerBounds = TypeUtils.commonSupertypeForNumberTypes(numberLowerBounds)
|
||||||
|
if (possibleAnswer == superTypeOfNumberLowerBounds) return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun tryPossibleAnswer(bounds: Collection<Bound>, possibleAnswer: KotlinType?): Boolean {
|
private fun tryPossibleAnswer(bounds: Collection<Bound>, possibleAnswer: KotlinType?): Boolean {
|
||||||
if (possibleAnswer == null) return false
|
if (possibleAnswer == null) return false
|
||||||
// a captured type might be an answer
|
// a captured type might be an answer
|
||||||
if (!possibleAnswer.getConstructor().isDenotable() && !possibleAnswer.isCaptured()) return false
|
if (!possibleAnswer.constructor.isDenotable && !possibleAnswer.isCaptured()) return false
|
||||||
|
|
||||||
if (typeVariable.hasOnlyInputTypesAnnotation()) {
|
if (!checkOnlyInputTypes(bounds, possibleAnswer)) return false
|
||||||
val typesInBoundsSet = bounds.filter { it.isProper && it.constrainingType.constructor.isDenotable }.map { it.constrainingType }.toSet()
|
|
||||||
if (!typesInBoundsSet.contains(possibleAnswer)) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for (bound in bounds) {
|
for (bound in bounds) {
|
||||||
when (bound.kind) {
|
when (bound.kind) {
|
||||||
|
|||||||
Reference in New Issue
Block a user