[FE 1.0] Make type of safe call always nullable
^KT-46860 In Progress
This commit is contained in:
committed by
teamcityserver
parent
805fad980f
commit
26b9948e5f
+6
@@ -27416,6 +27416,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
|
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
|
@Test
|
||||||
@TestMetadata("simple.kt")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
|
|||||||
+6
@@ -27416,6 +27416,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
|
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
|
@Test
|
||||||
@TestMetadata("simple.kt")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
|
|||||||
@@ -359,8 +359,10 @@ class CallExpressionResolver(
|
|||||||
KotlinTypeInfo {
|
KotlinTypeInfo {
|
||||||
var initialDataFlowInfoForArguments = context.dataFlowInfo
|
var initialDataFlowInfoForArguments = context.dataFlowInfo
|
||||||
val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { dataFlowValueFactory.createDataFlowValue(it, context) }
|
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 {
|
val callOperationNode = AstLoadingFilter.forceAllowTreeLoading(element.qualified.containingFile, ThrowableComputable {
|
||||||
element.node
|
element.node
|
||||||
@@ -368,11 +370,12 @@ class CallExpressionResolver(
|
|||||||
|
|
||||||
if (receiverDataFlowValue != null && element.safe) {
|
if (receiverDataFlowValue != null && element.safe) {
|
||||||
// Additional "receiver != null" information should be applied if we consider a safe call
|
// Additional "receiver != null" information should be applied if we consider a safe call
|
||||||
if (receiverCanBeNull) {
|
if (shouldNullifySafeCallType) {
|
||||||
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
|
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
|
||||||
receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings
|
receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
|
if (!receiverCanBeNull) {
|
||||||
reportUnnecessarySafeCall(context.trace, receiver.type, callOperationNode, receiver)
|
reportUnnecessarySafeCall(context.trace, receiver.type, callOperationNode, receiver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,7 +396,7 @@ class CallExpressionResolver(
|
|||||||
|
|
||||||
val selectorType = selectorTypeInfo.type
|
val selectorType = selectorTypeInfo.type
|
||||||
if (selectorType != null) {
|
if (selectorType != null) {
|
||||||
if (element.safe && receiverCanBeNull) {
|
if (element.safe && shouldNullifySafeCallType) {
|
||||||
selectorTypeInfo = selectorTypeInfo.replaceType(TypeUtils.makeNullable(selectorType))
|
selectorTypeInfo = selectorTypeInfo.replaceType(TypeUtils.makeNullable(selectorType))
|
||||||
}
|
}
|
||||||
// TODO : this is suspicious: remove this code?
|
// TODO : this is suspicious: remove this code?
|
||||||
|
|||||||
+1
-1
@@ -5,4 +5,4 @@ fun test(s: String) = s?.length
|
|||||||
// 0 IFNULL
|
// 0 IFNULL
|
||||||
// 0 IFNONNULL
|
// 0 IFNONNULL
|
||||||
// 0 intValue
|
// 0 intValue
|
||||||
// 0 valueOf
|
// 1 valueOf
|
||||||
|
|||||||
+20
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -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
|
||||||
|
}
|
||||||
Generated
+6
@@ -27506,6 +27506,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
|
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
|
@Test
|
||||||
@TestMetadata("simple.kt")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ enum class LanguageFeature(
|
|||||||
UnrestrictedBuilderInference(KOTLIN_1_6),
|
UnrestrictedBuilderInference(KOTLIN_1_6),
|
||||||
ProperTypeInferenceConstraintsProcessing(KOTLIN_1_6, kind = BUG_FIX),
|
ProperTypeInferenceConstraintsProcessing(KOTLIN_1_6, kind = BUG_FIX),
|
||||||
ClassTypeParameterAnnotations(KOTLIN_1_6),
|
ClassTypeParameterAnnotations(KOTLIN_1_6),
|
||||||
|
SafeCallsAreAlwaysNullable(KOTLIN_1_6),
|
||||||
|
|
||||||
// Temporarily disabled, see KT-27084/KT-22379
|
// Temporarily disabled, see KT-27084/KT-22379
|
||||||
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
|
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
|
||||||
|
|||||||
+6
@@ -27416,6 +27416,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.kt");
|
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
|
@Test
|
||||||
@TestMetadata("simple.kt")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user