From 7f48897e21663c6838fe51234f4d6a96ce5b660b Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 17 Jan 2019 15:28:27 +0300 Subject: [PATCH] Add `NON_VARARG_SPREAD` and `SPREAD_OF_NULLABLE` diagnostics to new inference --- .../resolve/PlatformConfiguratorBase.kt | 2 +- .../NullableVarargArgumentCallChecker.kt | 43 +++++++++++++++++++ .../calls/components/ArgumentsUtils.kt | 2 +- .../varargs/NullableTypeForVarargArgument.kt | 28 ++++++++++-- .../varargs/NullableTypeForVarargArgument.txt | 20 +++++++++ 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NullableVarargArgumentCallChecker.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 456c2e52aff..ee6ab790978 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -45,7 +45,7 @@ private val DEFAULT_CALL_CHECKERS = listOf( CallableReferenceCompatibilityChecker(), LateinitIntrinsicApplicabilityChecker, UnderscoreUsageChecker, AssigningNamedArgumentToVarargChecker(), PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker, - UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker() + UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NullableVarargArgumentCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NullableVarargArgumentCallChecker.kt new file mode 100644 index 00000000000..0e69b370e66 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/NullableVarargArgumentCallChecker.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.resolve.calls.components.stableType +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall +import org.jetbrains.kotlin.resolve.calls.tower.ExpressionKotlinCallArgumentImpl +import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl +import org.jetbrains.kotlin.resolve.calls.tower.NewVariableAsFunctionResolvedCallImpl +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.types.FlexibleType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +object NullableVarargArgumentCallChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) return + if (resolvedCall is VariableAsFunctionResolvedCall) { + check(resolvedCall.functionCall, reportOn, context) + return + } + if (resolvedCall !is NewResolvedCallImpl<*>) return + for (argument in resolvedCall.argumentMappingByOriginal.values) { + for (arg in argument.arguments) { + if (!arg.isSpread || arg !is ExpressionKotlinCallArgumentImpl) continue + val spreadElement = arg.valueArgument.getSpreadElement() ?: continue + + val receiver = arg.receiver.safeAs() ?: continue + val type = receiver.stableType + if (type !is FlexibleType && TypeUtils.isNullableType(type)) { + context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement)) + } + } + } + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt index 0ac38a4b11a..3aeb7d7d631 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt @@ -45,7 +45,7 @@ internal val ReceiverValueWithSmartCastInfo.unstableType: UnwrappedType? } // with all smart casts if stable -internal val ReceiverValueWithSmartCastInfo.stableType: UnwrappedType +val ReceiverValueWithSmartCastInfo.stableType: UnwrappedType get() { if (!isStable || possibleTypes.isEmpty()) return receiverValue.type.unwrap() return intersectWrappedTypes(possibleTypes + receiverValue.type) diff --git a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt index e673aed34dd..5e7c55d1075 100644 --- a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt +++ b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt @@ -10,6 +10,15 @@ public class A { public static String[] ar; } +// FILE: J.java + +public class J { + public interface Invoke { + void invoke(String ...args); + } + + public static Invoke staticFun; +} // FILE: 1.kt val args: Array? = null @@ -18,10 +27,12 @@ fun bar(x: Int, vararg s: String) {} fun baz(s: String) {} +fun getArr(): Array? = null + fun f() { - A().foo(1, *args) - bar(2, *args) - baz(*args) + A().foo(1, *args) + bar(2, *args) + baz(*args) } fun g(args: Array?) { @@ -38,8 +49,17 @@ class B { fun h(b: B) { if (b.args != null) { - A().foo(1, *b.args) + A().foo(1, *b.args) } } +fun k() { + A().foo(1, *getArr()) + bar(2, *getArr()) + baz(*getArr()) +} +fun invokeTest(goodArgs: Array) { + J.staticFun(*goodArgs) + J.staticFun(*args) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt index 7074593a679..37db3644465 100644 --- a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt +++ b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt @@ -5,7 +5,10 @@ public fun bar(/*0*/ x: kotlin.Int, /*1*/ vararg s: kotlin.String /*kotlin.Array public fun baz(/*0*/ s: kotlin.String): kotlin.Unit public fun f(): kotlin.Unit public fun g(/*0*/ args: kotlin.Array?): kotlin.Unit +public fun getArr(): kotlin.Array? public fun h(/*0*/ b: B): kotlin.Unit +public fun invokeTest(/*0*/ goodArgs: kotlin.Array): kotlin.Unit +public fun k(): kotlin.Unit public open class A { public constructor A() @@ -25,3 +28,20 @@ public final class B { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +public open class J { + public constructor J() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public interface Invoke { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract operator fun invoke(/*0*/ vararg args: kotlin.String! /*kotlin.Array<(out) kotlin.String!>!*/): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + // Static members + public final var staticFun: J.Invoke! +}