diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index a09a0d6029d..5b703f1f76a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -448,6 +448,7 @@ public interface Errors { DiagnosticFactory0 VARARG_OUTSIDE_PARENTHESES = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NON_VARARG_SPREAD = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 SPREAD_OF_NULLABLE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 MANY_FUNCTION_LITERAL_ARGUMENTS = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 56304e62f11..a8d4140efd7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -185,6 +185,7 @@ public class DefaultErrorMessages { MAP.put(VARARG_OUTSIDE_PARENTHESES, "Passing value as a vararg is only allowed inside a parenthesized argument list"); MAP.put(NON_VARARG_SPREAD, "The spread operator (*foo) may only be applied in a vararg position"); + MAP.put(SPREAD_OF_NULLABLE, "The spread operator (*foo) may not be applied to an argument of nullable type"); MAP.put(MANY_FUNCTION_LITERAL_ARGUMENTS, "Only one function literal is allowed outside a parenthesized argument list"); MAP.put(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER, "This property must either have a type annotation, be initialized or be delegated"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index 808ea08a8eb..40a79e164c4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -21,6 +21,7 @@ import com.google.common.collect.Sets import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.SUPER_CANT_BE_EXTENSION_RECEIVER import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.psi.* @@ -349,6 +350,15 @@ public class CandidateResolver( else if (ErrorUtils.containsUninferredParameter(expectedType)) { matchStatus = ArgumentMatchStatus.MATCH_MODULO_UNINFERRED_TYPES } + + val spreadElement = argument.getSpreadElement() + if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) { + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context) + val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, expectedType, expression, context, null, false) + if (smartCastResult == null || !smartCastResult.isCorrect) { + context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement)); + } + } } argumentTypes.add(resultingType) candidateCall.recordArgumentMatchStatus(argument, matchStatus) diff --git a/compiler/testData/codegen/boxWithJava/varargs/A.java b/compiler/testData/codegen/boxWithJava/varargs/A.java new file mode 100644 index 00000000000..c21e1304771 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/varargs/A.java @@ -0,0 +1,7 @@ +public class A { + public int foo(int x, String ... args) { + return x + args.length; + } + + public static String[] ar = new String[] { "a", "b"}; +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/varargs/A.kt b/compiler/testData/codegen/boxWithJava/varargs/A.kt new file mode 100644 index 00000000000..f6e2595886f --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/varargs/A.kt @@ -0,0 +1,19 @@ +fun bar(args: Array?): Int { + var res = 0 + + if (args != null) { + res += A().foo(1, *args) + } + + res += A().foo(1, *A.ar) + + return res +} + +fun box(): String { + if (bar(null) != 3) return "Fail" + + if (bar(A.ar) != 6) return "Fail" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt new file mode 100644 index 00000000000..ec307ded2b3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt @@ -0,0 +1,44 @@ +// !DIAGNOSTICS:-UNUSED_PARAMETER + +// KT-9883 prohibit using spread operator for nullable value + +// FILE: A.java + +public class A { + public void foo(int x, String ... args) {} + public static String[] ar; +} + + +// FILE: 1.kt +val args: Array? = null + +fun bar(x: Int, vararg s: String) {} + +fun baz(s: String) {} + +fun f() { + A().foo(1, *args) + bar(2, *args) + baz(*args) +} + +fun g(args: Array?) { + + if (args != null) { + A().foo(1, *args) + } + A().foo(1, *A.ar) +} + +class B { + var args: Array? = null +} + +fun h(b: B) { + if (b.args != null) { + A().foo(1, *b.args) + } +} + + diff --git a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt new file mode 100644 index 00000000000..7074593a679 --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.txt @@ -0,0 +1,27 @@ +package + +public val args: kotlin.Array? = null +public fun bar(/*0*/ x: kotlin.Int, /*1*/ vararg s: kotlin.String /*kotlin.Array*/): kotlin.Unit +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 h(/*0*/ b: B): kotlin.Unit + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ x: kotlin.Int, /*1*/ vararg args: kotlin.String! /*kotlin.Array<(out) kotlin.String!>!*/): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final var ar: kotlin.Array<(out) kotlin.String!>! +} + +public final class B { + public constructor B() + public final var args: kotlin.Array? + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 00192c943d2..8a0d42997f0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -16995,6 +16995,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("NullableTypeForVarargArgument.kt") + public void testNullableTypeForVarargArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.kt"); + doTest(fileName); + } + @TestMetadata("UnaryVsVararg.kt") public void testUnaryVsVararg() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/varargs/UnaryVsVararg.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index d0cbf926487..4ff90861ab7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -119,6 +119,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("varargs") + public void testVarargs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/varargs/"); + doTestWithJava(fileName); + } + @TestMetadata("compiler/testData/codegen/boxWithJava/annotatedFileClasses") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -722,4 +728,5 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege } } + }