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.expressions.DoubleColonExpressionResolver
|
||||
import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import java.util.*
|
||||
|
||||
class CandidateResolver(
|
||||
@@ -376,7 +377,7 @@ class CandidateResolver(
|
||||
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) {
|
||||
val smartCast = smartCastValueArgumentTypeIfPossible(expression, newContext.expectedType, type, newContext)
|
||||
if (smartCast == null) {
|
||||
resultStatus = OTHER_ERROR
|
||||
resultStatus = tryNotNullableArgument(type, expectedType) ?: OTHER_ERROR
|
||||
matchStatus = ArgumentMatchStatus.TYPE_MISMATCH
|
||||
}
|
||||
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 {
|
||||
val extensionReceiver = candidateDescriptor.extensionReceiverParameter
|
||||
val dispatchReceiver = candidateDescriptor.dispatchReceiverParameter
|
||||
|
||||
@@ -26,6 +26,7 @@ public enum ResolutionStatus {
|
||||
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
|
||||
UNSTABLE_SMARTCAST_ERROR,
|
||||
INVISIBLE_MEMBER_ERROR,
|
||||
NULLABLE_ARGUMENT_TYPE_MISMATCH,
|
||||
OTHER_ERROR,
|
||||
ARGUMENTS_MAPPING_ERROR,
|
||||
// '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(UNSTABLE_SMARTCAST_ERROR),
|
||||
EnumSet.of(INVISIBLE_MEMBER_ERROR),
|
||||
EnumSet.of(NULLABLE_ARGUMENT_TYPE_MISMATCH),
|
||||
EnumSet.of(OTHER_ERROR),
|
||||
EnumSet.of(ARGUMENTS_MAPPING_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++
|
||||
|
||||
1 + platformNN
|
||||
1 <!NONE_APPLICABLE!>+<!> platformN
|
||||
1 + <!TYPE_MISMATCH!>platformN<!>
|
||||
1 + platformJ
|
||||
|
||||
platformNN + 1
|
||||
@@ -41,7 +41,7 @@ fun test() {
|
||||
platformJ + 1
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
if (i++ == 7L) {
|
||||
return i++ <!NONE_APPLICABLE!>+<!> i
|
||||
return i++ <!UNSAFE_OPERATOR_CALL!>+<!> <!TYPE_MISMATCH!>i<!>
|
||||
}
|
||||
return 0L
|
||||
}
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
// FILE: KotlinFile.kt
|
||||
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<!>)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ fun valuesNullable(map: MutableMap<Int, String?>) {
|
||||
// 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
|
||||
map.merge(1, null) { old, new -> old + new }
|
||||
// OTHER_ERROR
|
||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out 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
|
||||
// NULLABLE_ARGUMENT_TYPE_MISMATCH
|
||||
// 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
|
||||
}
|
||||
|
||||
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?) {
|
||||
map.merge(1, newValue) { old, new -> new }
|
||||
// OTHER_ERROR
|
||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out 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
|
||||
// NULLABLE_ARGUMENT_TYPE_MISMATCH
|
||||
// ORIGINAL: fun merge(K, V, (V, V) -> V?): V? 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 }
|
||||
// SUCCESS
|
||||
// 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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testRedundantNullable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/redundantNullable.kt");
|
||||
|
||||
Reference in New Issue
Block a user