KT-9883 prohibit using spread operator for nullable value
#KT-9883 Fixed
This commit is contained in:
@@ -448,6 +448,7 @@ public interface Errors {
|
|||||||
|
|
||||||
DiagnosticFactory0<KtExpression> VARARG_OUTSIDE_PARENTHESES = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> VARARG_OUTSIDE_PARENTHESES = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<LeafPsiElement> NON_VARARG_SPREAD = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<LeafPsiElement> NON_VARARG_SPREAD = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<LeafPsiElement> SPREAD_OF_NULLABLE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<KtExpression> MANY_FUNCTION_LITERAL_ARGUMENTS = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> MANY_FUNCTION_LITERAL_ARGUMENTS = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
|
|||||||
+1
@@ -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(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(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(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");
|
MAP.put(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER, "This property must either have a type annotation, be initialized or be delegated");
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.google.common.collect.Sets
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
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.diagnostics.Errors.SUPER_CANT_BE_EXTENSION_RECEIVER
|
||||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -349,6 +350,15 @@ public class CandidateResolver(
|
|||||||
else if (ErrorUtils.containsUninferredParameter(expectedType)) {
|
else if (ErrorUtils.containsUninferredParameter(expectedType)) {
|
||||||
matchStatus = ArgumentMatchStatus.MATCH_MODULO_UNINFERRED_TYPES
|
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)
|
argumentTypes.add(resultingType)
|
||||||
candidateCall.recordArgumentMatchStatus(argument, matchStatus)
|
candidateCall.recordArgumentMatchStatus(argument, matchStatus)
|
||||||
|
|||||||
@@ -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"};
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
fun bar(args: Array<String>?): 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"
|
||||||
|
}
|
||||||
@@ -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<String>? = null
|
||||||
|
|
||||||
|
fun bar(x: Int, vararg s: String) {}
|
||||||
|
|
||||||
|
fun baz(s: String) {}
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>args)
|
||||||
|
bar(2, <!SPREAD_OF_NULLABLE!>*<!><!TYPE_MISMATCH!>args<!>)
|
||||||
|
baz(<!NON_VARARG_SPREAD, SPREAD_OF_NULLABLE!>*<!>args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun g(args: Array<String>?) {
|
||||||
|
|
||||||
|
if (args != null) {
|
||||||
|
A().foo(1, *<!DEBUG_INFO_SMARTCAST!>args<!>)
|
||||||
|
}
|
||||||
|
A().foo(1, *A.ar)
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
var args: Array<String>? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun h(b: B) {
|
||||||
|
if (b.args != null) {
|
||||||
|
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!><!SMARTCAST_IMPOSSIBLE!>b.args<!>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val args: kotlin.Array<kotlin.String>? = null
|
||||||
|
public fun bar(/*0*/ x: kotlin.Int, /*1*/ vararg s: kotlin.String /*kotlin.Array<out kotlin.String>*/): kotlin.Unit
|
||||||
|
public fun baz(/*0*/ s: kotlin.String): kotlin.Unit
|
||||||
|
public fun f(): kotlin.Unit
|
||||||
|
public fun g(/*0*/ args: kotlin.Array<kotlin.String>?): 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<kotlin.String>?
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -16995,6 +16995,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("UnaryVsVararg.kt")
|
||||||
public void testUnaryVsVararg() throws Exception {
|
public void testUnaryVsVararg() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/varargs/UnaryVsVararg.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/varargs/UnaryVsVararg.kt");
|
||||||
|
|||||||
+7
@@ -119,6 +119,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
|
|||||||
doTestWithJava(fileName);
|
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")
|
@TestMetadata("compiler/testData/codegen/boxWithJava/annotatedFileClasses")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
@@ -722,4 +728,5 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user