[NI] Add check for non-null argument type in arguments check

#KT-31461 Fixed
This commit is contained in:
Dmitriy Novozhilov
2019-05-20 11:47:36 +03:00
parent b323298b0e
commit b4c8c79931
11 changed files with 53 additions and 10 deletions
@@ -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<PSIKotlinCallArgument>()?.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))
}
}
}
}
}
@@ -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
}
@@ -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
}
@@ -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)
}
}
@@ -7,7 +7,7 @@ interface A<T>
fun <T> infer(<!UNUSED_PARAMETER!>a<!>: A<T>) : T {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun test(nothing: Nothing?) {
val <!UNUSED_VARIABLE!>i<!> = <!OI;TYPE_INFERENCE_INCORPORATION_ERROR!>infer<!>(<!DEBUG_INFO_CONSTANT, TYPE_MISMATCH!>nothing<!>)
val <!UNUSED_VARIABLE!>i<!> = <!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_INCORPORATION_ERROR!>infer<!>(<!DEBUG_INFO_CONSTANT, TYPE_MISMATCH!>nothing<!>)
}
fun sum(<!UNUSED_PARAMETER!>a<!> : IntArray) : Int {
@@ -15,6 +15,6 @@ fun <S> test1(a: ParameterizedChild<S>?, b: Child): Base<S> = myRun {
elvis(a, b)
}
fun <S> test2(a: S?, b: S): S = <!TYPE_MISMATCH!>myRun {
<!TYPE_MISMATCH!>select(a, b)<!>
}<!>
fun <S> test2(a: S?, b: S): S = myRun {
<!TYPE_MISMATCH, TYPE_MISMATCH!>select(a, b)<!>
}
@@ -13,6 +13,6 @@ public class J {
fun test(j: J, nullStr: String?, nullByte: Byte?, nullDouble: Double?) {
j.foo(nullStr)
j.<!NI;NONE_APPLICABLE!>foo<!>(<!OI;TYPE_MISMATCH!>nullDouble<!>)
j.foo(<!TYPE_MISMATCH!>nullDouble<!>)
j.foo(nullByte)
}
@@ -6,7 +6,7 @@ fun foo(x: Int) {}
fun foo(x: Int, y: String) {}
fun bar(nullX: Int?, nullY: String?, notNullY: String) {
<!NI;NONE_APPLICABLE!>foo<!>(<!OI;TYPE_MISMATCH!>nullX<!>)
foo(<!TYPE_MISMATCH!>nullX<!>)
foo(<!TYPE_MISMATCH!>nullX<!>, notNullY)
foo(<!TYPE_MISMATCH!>nullX<!>, <!TYPE_MISMATCH!>nullY<!>)
<!NONE_APPLICABLE!>foo<!>()
@@ -34,7 +34,7 @@ fun test() {
platformJ++
1 + platformNN
1 <!NI;NONE_APPLICABLE!>+<!> <!OI;TYPE_MISMATCH!>platformN<!>
1 + <!TYPE_MISMATCH!>platformN<!>
1 + platformJ
platformNN + 1
@@ -42,7 +42,7 @@ fun test() {
platformJ + 1
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> platformNN
1 <!NI;NONE_APPLICABLE, OI;INFIX_MODIFIER_REQUIRED!>plus<!> <!OI;TYPE_MISMATCH!>platformN<!>
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> <!TYPE_MISMATCH!>platformN<!>
1 <!INFIX_MODIFIER_REQUIRED!>plus<!> platformJ
platformNN <!INFIX_MODIFIER_REQUIRED!>plus<!> 1
@@ -15,7 +15,7 @@ fun bar(arg: Long?): Long {
return i<!UNSAFE_CALL!>--<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> i
}
if (i++ == 7L) {
return i++ <!NI;NONE_APPLICABLE, OI;UNSAFE_OPERATOR_CALL!>+<!> <!OI;TYPE_MISMATCH!>i<!>
return i++ <!UNSAFE_OPERATOR_CALL!>+<!> <!TYPE_MISMATCH!>i<!>
}
return 0L
}
@@ -23,7 +23,7 @@ fun case_1(x: Double?, y: Double?) : Double {
} else if (x == null && y != null) {
<!DEBUG_INFO_SMARTCAST!>y<!>
} else {
x <!NONE_APPLICABLE!>+<!> y
x <!UNSAFE_OPERATOR_CALL!>+<!> <!TYPE_MISMATCH!>y<!>
}
}