KT-9883 prohibit using spread operator for nullable value

#KT-9883 Fixed
This commit is contained in:
Michael Nedzelsky
2015-11-25 17:18:25 +03:00
parent 63fa71606f
commit 2fa34ae170
9 changed files with 122 additions and 0 deletions
@@ -448,6 +448,7 @@ public interface Errors {
DiagnosticFactory0<KtExpression> VARARG_OUTSIDE_PARENTHESES = 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);
@@ -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");
@@ -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)
+7
View File
@@ -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"};
}
+19
View File
@@ -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<!>)
}
}
@@ -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);
}
@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");
@@ -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
}
}
}