Improve diagnostic on overload resolution ambiguity
Report type mismatch on argument when a nullable argument is passed to non-null parameter. Note that this affects only functions with simple types without generics #KT-2007 Fixed #KT-9282 Fixed
This commit is contained in:
@@ -53,6 +53,7 @@ import org.jetbrains.kotlin.types.checker.ErrorTypesAreEqualToAnything
|
|||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
|
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
|
||||||
import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments
|
import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CandidateResolver(
|
class CandidateResolver(
|
||||||
@@ -376,7 +377,7 @@ class CandidateResolver(
|
|||||||
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) {
|
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) {
|
||||||
val smartCast = smartCastValueArgumentTypeIfPossible(expression, newContext.expectedType, type, newContext)
|
val smartCast = smartCastValueArgumentTypeIfPossible(expression, newContext.expectedType, type, newContext)
|
||||||
if (smartCast == null) {
|
if (smartCast == null) {
|
||||||
resultStatus = OTHER_ERROR
|
resultStatus = tryNotNullableArgument(type, expectedType) ?: OTHER_ERROR
|
||||||
matchStatus = ArgumentMatchStatus.TYPE_MISMATCH
|
matchStatus = ArgumentMatchStatus.TYPE_MISMATCH
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -417,6 +418,14 @@ class CandidateResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun tryNotNullableArgument(argumentType: KotlinType, parameterType: KotlinType): ResolutionStatus? {
|
||||||
|
if (!argumentType.isMarkedNullable || parameterType.isMarkedNullable) return null
|
||||||
|
|
||||||
|
val notNullableArgumentType = argumentType.makeNotNullable()
|
||||||
|
val isApplicable = ArgumentTypeResolver.isSubtypeOfForArgumentType(notNullableArgumentType, parameterType)
|
||||||
|
return if (isApplicable) NULLABLE_ARGUMENT_TYPE_MISMATCH else null
|
||||||
|
}
|
||||||
|
|
||||||
private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(): Unit = check {
|
private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(): Unit = check {
|
||||||
val extensionReceiver = candidateDescriptor.extensionReceiverParameter
|
val extensionReceiver = candidateDescriptor.extensionReceiverParameter
|
||||||
val dispatchReceiver = candidateDescriptor.dispatchReceiverParameter
|
val dispatchReceiver = candidateDescriptor.dispatchReceiverParameter
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public enum ResolutionStatus {
|
|||||||
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
|
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
|
||||||
UNSTABLE_SMARTCAST_ERROR,
|
UNSTABLE_SMARTCAST_ERROR,
|
||||||
INVISIBLE_MEMBER_ERROR,
|
INVISIBLE_MEMBER_ERROR,
|
||||||
|
NULLABLE_ARGUMENT_TYPE_MISMATCH,
|
||||||
OTHER_ERROR,
|
OTHER_ERROR,
|
||||||
ARGUMENTS_MAPPING_ERROR,
|
ARGUMENTS_MAPPING_ERROR,
|
||||||
// '1.foo()' shouldn't be resolved to 'fun String.foo()'
|
// '1.foo()' shouldn't be resolved to 'fun String.foo()'
|
||||||
@@ -44,6 +45,7 @@ public enum ResolutionStatus {
|
|||||||
EnumSet.of(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR),
|
EnumSet.of(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR),
|
||||||
EnumSet.of(UNSTABLE_SMARTCAST_ERROR),
|
EnumSet.of(UNSTABLE_SMARTCAST_ERROR),
|
||||||
EnumSet.of(INVISIBLE_MEMBER_ERROR),
|
EnumSet.of(INVISIBLE_MEMBER_ERROR),
|
||||||
|
EnumSet.of(NULLABLE_ARGUMENT_TYPE_MISMATCH),
|
||||||
EnumSet.of(OTHER_ERROR),
|
EnumSet.of(OTHER_ERROR),
|
||||||
EnumSet.of(ARGUMENTS_MAPPING_ERROR),
|
EnumSet.of(ARGUMENTS_MAPPING_ERROR),
|
||||||
EnumSet.of(RECEIVER_TYPE_ERROR),
|
EnumSet.of(RECEIVER_TYPE_ERROR),
|
||||||
|
|||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// FILE: J.java
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
|
public class J {
|
||||||
|
void foo(String x) {}
|
||||||
|
void foo(@NotNull Double x) {}
|
||||||
|
void foo(@Nullable Byte x) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun test(j: J, nullStr: String?, nullByte: Byte?, nullDouble: Double?) {
|
||||||
|
j.foo(nullStr)
|
||||||
|
j.foo(<!TYPE_MISMATCH!>nullDouble<!>)
|
||||||
|
j.foo(nullByte)
|
||||||
|
}
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(/*0*/ j: J, /*1*/ nullStr: kotlin.String?, /*2*/ nullByte: kotlin.Byte?, /*3*/ nullDouble: kotlin.Double?): kotlin.Unit
|
||||||
|
|
||||||
|
public open class J {
|
||||||
|
public constructor J()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public/*package*/ open fun foo(/*0*/ @org.jetbrains.annotations.Nullable x: kotlin.Byte?): kotlin.Unit
|
||||||
|
public/*package*/ open fun foo(/*0*/ @org.jetbrains.annotations.NotNull x: kotlin.Double): kotlin.Unit
|
||||||
|
public/*package*/ open fun foo(/*0*/ x: kotlin.String!): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
fun foo(x: String) {}
|
||||||
|
fun foo(x: Int) {}
|
||||||
|
fun foo(x: Int, y: String) {}
|
||||||
|
|
||||||
|
fun bar(nullX: Int?, nullY: String?, notNullY: String) {
|
||||||
|
foo(<!TYPE_MISMATCH!>nullX<!>)
|
||||||
|
foo(<!TYPE_MISMATCH!>nullX<!>, notNullY)
|
||||||
|
foo(<!TYPE_MISMATCH!>nullX<!>, <!TYPE_MISMATCH!>nullY<!>)
|
||||||
|
<!NONE_APPLICABLE!>foo<!>()
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun bar(/*0*/ nullX: kotlin.Int?, /*1*/ nullY: kotlin.String?, /*2*/ notNullY: kotlin.String): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ x: kotlin.String): kotlin.Unit
|
||||||
+2
-2
@@ -33,7 +33,7 @@ fun test() {
|
|||||||
platformJ++
|
platformJ++
|
||||||
|
|
||||||
1 + platformNN
|
1 + platformNN
|
||||||
1 <!NONE_APPLICABLE!>+<!> platformN
|
1 + <!TYPE_MISMATCH!>platformN<!>
|
||||||
1 + platformJ
|
1 + platformJ
|
||||||
|
|
||||||
platformNN + 1
|
platformNN + 1
|
||||||
@@ -41,7 +41,7 @@ fun test() {
|
|||||||
platformJ + 1
|
platformJ + 1
|
||||||
|
|
||||||
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> platformNN
|
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> platformNN
|
||||||
1 <!NONE_APPLICABLE!>plus<!> platformN
|
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> <!TYPE_MISMATCH!>platformN<!>
|
||||||
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> platformJ
|
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> platformJ
|
||||||
|
|
||||||
platformNN <!INFIX_MODIFIER_REQUIRED!>plus<!> 1
|
platformNN <!INFIX_MODIFIER_REQUIRED!>plus<!> 1
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ fun bar(arg: Long?): Long {
|
|||||||
return i<!UNSAFE_CALL!>--<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> i
|
return i<!UNSAFE_CALL!>--<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> i
|
||||||
}
|
}
|
||||||
if (i++ == 7L) {
|
if (i++ == 7L) {
|
||||||
return i++ <!NONE_APPLICABLE!>+<!> i
|
return i++ <!UNSAFE_OPERATOR_CALL!>+<!> <!TYPE_MISMATCH!>i<!>
|
||||||
}
|
}
|
||||||
return 0L
|
return 0L
|
||||||
}
|
}
|
||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// FILE: KotlinFile.kt
|
// FILE: KotlinFile.kt
|
||||||
fun foo(javaInterface: JavaInterface) {
|
fun foo(javaInterface: JavaInterface) {
|
||||||
javaInterface.doIt(<!NULL_FOR_NONNULL_TYPE!>null<!>) <!TYPE_MISMATCH!>{ }<!>
|
javaInterface.doIt(<!NULL_FOR_NONNULL_TYPE!>null<!>) { }
|
||||||
javaInterface.doIt("", <!NULL_FOR_NONNULL_TYPE!>null<!>)
|
javaInterface.doIt("", <!NULL_FOR_NONNULL_TYPE!>null<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ fun valuesNullable(map: MutableMap<Int, String?>) {
|
|||||||
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? defined in kotlin.collections.MutableMap
|
||||||
// SUBSTITUTED: fun merge(Int, String, (String, String) -> String?): String? defined in kotlin.collections.MutableMap
|
// SUBSTITUTED: fun merge(Int, String, (String, String) -> String?): String? defined in kotlin.collections.MutableMap
|
||||||
map.merge(1, null) { old, new -> old + new }
|
map.merge(1, null) { old, new -> old + new }
|
||||||
// OTHER_ERROR
|
// NULLABLE_ARGUMENT_TYPE_MISMATCH
|
||||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? defined in kotlin.collections.MutableMap
|
||||||
// SUBSTITUTED: fun merge(Int, String, BiFunction<in String, in String, out String?>): String? defined in kotlin.collections.MutableMap
|
// SUBSTITUTED: fun merge(Int, String, (String, String) -> String?): String? defined in kotlin.collections.MutableMap
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> valuesT(map: MutableMap<Int, T>, newValue: T) {
|
fun <T> valuesT(map: MutableMap<Int, T>, newValue: T) {
|
||||||
@@ -32,9 +32,9 @@ fun <T : Any> valuesTNotNull(map: MutableMap<Int, T>, newValue: T) {
|
|||||||
|
|
||||||
fun <T : Any> valuesTNullable(map: MutableMap<Int, T?>, newValue: T?) {
|
fun <T : Any> valuesTNullable(map: MutableMap<Int, T?>, newValue: T?) {
|
||||||
map.merge(1, newValue) { old, new -> new }
|
map.merge(1, newValue) { old, new -> new }
|
||||||
// OTHER_ERROR
|
// NULLABLE_ARGUMENT_TYPE_MISMATCH
|
||||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? defined in kotlin.collections.MutableMap
|
||||||
// SUBSTITUTED: fun merge(Int, T, BiFunction<in T, in T, out T?>): T? defined in kotlin.collections.MutableMap
|
// SUBSTITUTED: fun merge(Int, T, (T, T) -> T?): T? defined in kotlin.collections.MutableMap
|
||||||
map.merge(1, newValue!!) { old, new -> new }
|
map.merge(1, newValue!!) { old, new -> new }
|
||||||
// SUCCESS
|
// SUCCESS
|
||||||
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? defined in kotlin.collections.MutableMap
|
||||||
|
|||||||
@@ -14011,6 +14011,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableArgumentToNonNullParameterPlatform.kt")
|
||||||
|
public void testNullableArgumentToNonNullParameterPlatform() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableArgumentToNonNullParameterSimple.kt")
|
||||||
|
public void testNullableArgumentToNonNullParameterSimple() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("redundantNullable.kt")
|
@TestMetadata("redundantNullable.kt")
|
||||||
public void testRedundantNullable() throws Exception {
|
public void testRedundantNullable() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/redundantNullable.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/redundantNullable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user