diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index f81b0faa64a..a630395347a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.resolve.calls +import com.intellij.psi.util.PsiUtil import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.isExtensionFunctionType @@ -14,6 +15,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.INVOKE_ON import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.NON_KOTLIN_FUNCTION import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.isNull import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext @@ -128,6 +130,17 @@ class DiagnosticReporterByTrackingStrategy( trace.report(UNRESOLVED_REFERENCE.on(it.callableReference, it.callableReference)) } } + + ArgumentTypeMismatchDiagnostic::class.java -> { + require(diagnostic is ArgumentTypeMismatchDiagnostic) + reportIfNonNull(callArgument.safeAs()?.valueArgument?.getArgumentExpression()) { + if (it.isNull()) { + trace.report(NULL_FOR_NONNULL_TYPE.on(it, diagnostic.expectedType)) + } else { + trace.report(TYPE_MISMATCH.on(it, diagnostic.expectedType, diagnostic.actualType)) + } + } + } } } diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index c78149c2525..804f86e3f08 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -25,10 +25,13 @@ import com.intellij.psi.impl.source.tree.TreeUtil import com.intellij.psi.search.PsiSearchScopeUtil import com.intellij.psi.search.SearchScope import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import java.util.* +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract // NOTE: in this file we collect only LANGUAGE INDEPENDENT methods working with PSI and not modifying it @@ -477,3 +480,11 @@ fun LazyParseablePsiElement.getContainingKtFile(): KtFile { val fileString = if (file != null && file.isValid) file.text else "" throw IllegalStateException("KtElement not inside KtFile: $file with text \"$fileString\" for element $this of type ${this::class.java} node = ${this.node}") } + +@UseExperimental(ExperimentalContracts::class) +fun KtExpression.isNull(): Boolean { + contract { + returns(true) implies (this@isNull is KtConstantExpression) + } + return this is KtConstantExpression && this.node.elementType == KtNodeTypes.NULL +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt index 901ed6d7f72..6b7f00d384a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.checker.captureFromExpression import org.jetbrains.kotlin.types.checker.hasSupertypeWithGivenTypeConstructor import org.jetbrains.kotlin.types.lowerIfFlexible +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.types.upperIfFlexible @@ -66,6 +67,14 @@ private fun checkExpressionArgument( return UnstableSmartCast(expressionArgument, unstableType) } } + + if (argumentType.isMarkedNullable) { + if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null + if (csBuilder.addSubtypeConstraintIfCompatible(argumentType.makeNotNullable(), actualExpectedType, position)) { + return ArgumentTypeMismatchDiagnostic(actualExpectedType, argumentType, expressionArgument) + } + } + csBuilder.addSubtypeConstraint(argumentType, actualExpectedType, position) return null } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt index 447cc24ed56..75074d2226d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt @@ -185,4 +185,14 @@ class NonApplicableCallForBuilderInferenceDiagnostic(val kotlinCall: KotlinCall) override fun report(reporter: DiagnosticReporter) { reporter.onCall(this) } +} + +class ArgumentTypeMismatchDiagnostic( + val expectedType: UnwrappedType, + val actualType: UnwrappedType, + val expressionArgument: ExpressionKotlinCallArgument +) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) { + override fun report(reporter: DiagnosticReporter) { + reporter.onCallArgument(expressionArgument, this) + } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt b/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt index aa1018a0275..4f13ef0db58 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt @@ -7,7 +7,7 @@ interface A fun infer(a: A) : T {} fun test(nothing: Nothing?) { - val i = infer(nothing) + val i = infer(nothing) } fun sum(a : IntArray) : Int { diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt index e4678c80353..efa24dd7bea 100644 --- a/compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt @@ -15,6 +15,6 @@ fun test1(a: ParameterizedChild?, b: Child): Base = myRun { elvis(a, b) } -fun test2(a: S?, b: S): S = myRun { - select(a, b) -} +fun test2(a: S?, b: S): S = myRun { + select(a, b) +} diff --git a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.kt b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.kt index d86c4a5d21d..bec2e0777e5 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.kt @@ -13,6 +13,6 @@ public class J { fun test(j: J, nullStr: String?, nullByte: Byte?, nullDouble: Double?) { j.foo(nullStr) - j.foo(nullDouble) + j.foo(nullDouble) j.foo(nullByte) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.kt b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.kt index bf2ef9f6c6a..764767b9c16 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.kt @@ -6,7 +6,7 @@ fun foo(x: Int) {} fun foo(x: Int, y: String) {} fun bar(nullX: Int?, nullY: String?, notNullY: String) { - foo(nullX) + foo(nullX) foo(nullX, notNullY) foo(nullX, nullY) foo() diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/arithmetic.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/arithmetic.kt index a6b574a0b63..e82ea974494 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/arithmetic.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/arithmetic.kt @@ -34,7 +34,7 @@ fun test() { platformJ++ 1 + platformNN - 1 + platformN + 1 + platformN 1 + platformJ platformNN + 1 @@ -42,7 +42,7 @@ fun test() { platformJ + 1 1 plus platformNN - 1 plus platformN + 1 plus platformN 1 plus platformJ platformNN plus 1 diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/plusplusMinusminus.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/plusplusMinusminus.kt index 89be6466f8a..619cc29f317 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/plusplusMinusminus.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/plusplusMinusminus.kt @@ -15,7 +15,7 @@ fun bar(arg: Long?): Long { return i-- + i } if (i++ == 7L) { - return i++ + i + return i++ + i } return 0L } \ No newline at end of file diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/43.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/43.kt index d27d635747f..c828ce9f5a4 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/43.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/43.kt @@ -23,7 +23,7 @@ fun case_1(x: Double?, y: Double?) : Double { } else if (x == null && y != null) { y } else { - x + y + x + y } }