[FE 1.0] Make type of safe call always nullable

^KT-46860 In Progress
This commit is contained in:
Dmitriy Novozhilov
2021-06-21 15:06:06 +03:00
committed by teamcityserver
parent 805fad980f
commit 26b9948e5f
10 changed files with 86 additions and 6 deletions
@@ -27416,6 +27416,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
}
@Test
@TestMetadata("safeCallIsAlwaysNullable.kt")
public void testSafeCallIsAlwaysNullable() throws Exception {
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeCallIsAlwaysNullable.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
@@ -27416,6 +27416,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
}
@Test
@TestMetadata("safeCallIsAlwaysNullable.kt")
public void testSafeCallIsAlwaysNullable() throws Exception {
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeCallIsAlwaysNullable.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
@@ -359,8 +359,10 @@ class CallExpressionResolver(
KotlinTypeInfo {
var initialDataFlowInfoForArguments = context.dataFlowInfo
val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { dataFlowValueFactory.createDataFlowValue(it, context) }
val receiverCanBeNull = receiverDataFlowValue != null &&
initialDataFlowInfoForArguments.getStableNullability(receiverDataFlowValue).canBeNull()
val receiverCanBeNull = receiverDataFlowValue != null && initialDataFlowInfoForArguments.getStableNullability(receiverDataFlowValue).canBeNull()
val shouldNullifySafeCallType =
receiverCanBeNull || context.languageVersionSettings.supportsFeature(LanguageFeature.SafeCallsAreAlwaysNullable)
val callOperationNode = AstLoadingFilter.forceAllowTreeLoading(element.qualified.containingFile, ThrowableComputable {
element.node
@@ -368,11 +370,12 @@ class CallExpressionResolver(
if (receiverDataFlowValue != null && element.safe) {
// Additional "receiver != null" information should be applied if we consider a safe call
if (receiverCanBeNull) {
if (shouldNullifySafeCallType) {
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings
)
} else {
}
if (!receiverCanBeNull) {
reportUnnecessarySafeCall(context.trace, receiver.type, callOperationNode, receiver)
}
}
@@ -393,7 +396,7 @@ class CallExpressionResolver(
val selectorType = selectorTypeInfo.type
if (selectorType != null) {
if (element.safe && receiverCanBeNull) {
if (element.safe && shouldNullifySafeCallType) {
selectorTypeInfo = selectorTypeInfo.replaceType(TypeUtils.makeNullable(selectorType))
}
// TODO : this is suspicious: remove this code?
@@ -5,4 +5,4 @@ fun test(s: String) = s?.length
// 0 IFNULL
// 0 IFNONNULL
// 0 intValue
// 0 valueOf
// 1 valueOf
@@ -0,0 +1,20 @@
// LANGUAGE: +SafeCallsAreAlwaysNullable
// ISSUE: KT-46860
interface A {
fun foo(): Int
}
fun takeInt(x: Int) {}
fun test_1(a: A) {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>a<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!>
takeInt(x) // should be an error
}
fun test_2(a: A?) {
if (a != null) {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>a<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!>
takeInt(x) // should be an error
}
}
@@ -0,0 +1,20 @@
// LANGUAGE: +SafeCallsAreAlwaysNullable
// ISSUE: KT-46860
interface A {
fun foo(): Int
}
fun takeInt(x: Int) {}
fun test_1(a: A) {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>a<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!>
takeInt(<!TYPE_MISMATCH!>x<!>) // should be an error
}
fun test_2(a: A?) {
if (a != null) {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>a<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!>
takeInt(<!TYPE_MISMATCH!>x<!>) // should be an error
}
}
@@ -0,0 +1,12 @@
package
public fun takeInt(/*0*/ x: kotlin.Int): kotlin.Unit
public fun test_1(/*0*/ a: A): kotlin.Unit
public fun test_2(/*0*/ a: A?): kotlin.Unit
public interface A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -27506,6 +27506,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
}
@Test
@TestMetadata("safeCallIsAlwaysNullable.kt")
public void testSafeCallIsAlwaysNullable() throws Exception {
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeCallIsAlwaysNullable.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
@@ -211,6 +211,7 @@ enum class LanguageFeature(
UnrestrictedBuilderInference(KOTLIN_1_6),
ProperTypeInferenceConstraintsProcessing(KOTLIN_1_6, kind = BUG_FIX),
ClassTypeParameterAnnotations(KOTLIN_1_6),
SafeCallsAreAlwaysNullable(KOTLIN_1_6),
// Temporarily disabled, see KT-27084/KT-22379
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
@@ -27416,6 +27416,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
}
@Test
@TestMetadata("safeCallIsAlwaysNullable.kt")
public void testSafeCallIsAlwaysNullable() throws Exception {
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeCallIsAlwaysNullable.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {