diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt index bca6af9793e..310d2514b5d 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt @@ -30,7 +30,6 @@ import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue @@ -89,9 +88,13 @@ private val KtExpression.textForRuntimeAssertionInfo class RuntimeAssertionsDataFlowExtras( private val c: ResolutionContext<*>, - private val dataFlowValue: DataFlowValue, + private val expressionType: KotlinType, private val expression: KtExpression ) : RuntimeAssertionInfo.DataFlowExtras { + private val dataFlowValue by lazy(LazyThreadSafetyMode.PUBLICATION) { + DataFlowValueFactory.createDataFlowValue(expression, expressionType, c) + } + override val canBeNull: Boolean get() = c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull() override val presentableText: String @@ -105,7 +108,7 @@ object RuntimeAssertionsTypeChecker : AdditionalTypeChecker { val assertionInfo = RuntimeAssertionInfo.create( c.expectedType, expressionType, - RuntimeAssertionsDataFlowExtras(c, DataFlowValueFactory.createDataFlowValue(expression, expressionType, c), expression) + RuntimeAssertionsDataFlowExtras(c, expressionType, expression) ) if (assertionInfo != null) { @@ -128,12 +131,11 @@ object RuntimeAssertionsOnExtensionReceiverCallChecker : CallChecker { val expressionReceiverValue = receiverValue.safeAs() ?: return val receiverExpression = expressionReceiverValue.expression val c = context.resolutionContext - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverExpression, receiverValue.type, c) val assertionInfo = RuntimeAssertionInfo.create( receiverParameter.type, receiverValue.type, - RuntimeAssertionsDataFlowExtras(c, dataFlowValue, receiverExpression) + RuntimeAssertionsDataFlowExtras(c, receiverValue.type, receiverExpression) ) if (assertionInfo != null) { @@ -237,4 +239,4 @@ object RuntimeAssertionsOnDeclarationBodyChecker { else -> upper.immediateSupertypes().all { it.unwrap().canContainNull() } } } -} \ No newline at end of file +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt index d5fd3f5c23d..e4590b99eb2 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt @@ -44,7 +44,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker { doCheckType( expressionType, c.expectedType, - DataFlowValueFactory.createDataFlowValue(expression, expressionType, c), + { DataFlowValueFactory.createDataFlowValue(expression, expressionType, c) } , c.dataFlowInfo ) { expectedMustNotBeNull, actualMayBeNull -> c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedMustNotBeNull, actualMayBeNull)) @@ -57,16 +57,15 @@ class JavaNullabilityChecker : AdditionalTypeChecker { val subjectExpression = expression.subjectExpression ?: return val type = c.trace.getType(subjectExpression) ?: return if (type.isFlexible() && TypeUtils.isNullableType(type.asFlexibleType().upperBound)) { - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, type, c) - val dataFlowInfo = c.trace[BindingContext.EXPRESSION_TYPE_INFO, subjectExpression]?.dataFlowInfo - if (dataFlowInfo != null && !dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()) { - return - } - val enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(type) ?: return val context = c.trace.bindingContext if (WhenChecker.getEnumMissingCases(expression, context, enumClassDescriptor).isEmpty() && !WhenChecker.containsNullCase(expression, context)) { + val subjectDataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, type, c) + val dataFlowInfo = c.trace[BindingContext.EXPRESSION_TYPE_INFO, subjectExpression]?.dataFlowInfo + if (dataFlowInfo != null && !dataFlowInfo.getStableNullability(subjectDataFlowValue).canBeNull()) { + return + } c.trace.report(ErrorsJvm.WHEN_ENUM_CAN_BE_NULL_IN_JAVA.on(expression.subjectExpression!!)) } @@ -77,7 +76,9 @@ class JavaNullabilityChecker : AdditionalTypeChecker { val baseExpression = expression.baseExpression ?: return val baseExpressionType = c.trace.getType(baseExpression) ?: return doIfNotNull( - DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c), c + baseExpressionType, + { DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c) }, + c ) { c.trace.report(Errors.UNNECESSARY_NOT_NULL_ASSERTION.on(expression.operationReference, baseExpressionType)) } @@ -93,7 +94,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker { expression, expression.left!!, expression.right!!, c, { c.trace.getType(it) }, { value -> - doIfNotNull(value, c) { Nullability.NOT_NULL } ?: Nullability.UNKNOWN + doIfNotNull(value.type, { value }, c) { Nullability.NOT_NULL } ?: Nullability.UNKNOWN } ) } @@ -103,9 +104,12 @@ class JavaNullabilityChecker : AdditionalTypeChecker { } override fun checkReceiver(receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue, safeAccess: Boolean, c: CallResolutionContext<*>) { - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, c) + val dataFlowValue by lazy(LazyThreadSafetyMode.NONE) { + DataFlowValueFactory.createDataFlowValue(receiverArgument, c) + } + if (safeAccess) { - doIfNotNull(dataFlowValue, c) { + doIfNotNull(receiverArgument.type, { dataFlowValue }, c) { c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.callOperationNode!!.psi, receiverArgument.type)) } @@ -115,7 +119,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker { doCheckType( receiverArgument.type, receiverParameter.type, - dataFlowValue, + { dataFlowValue }, c.dataFlowInfo ) { expectedMustNotBeNull, actualMayBeNull -> @@ -129,7 +133,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker { private fun doCheckType( expressionType: KotlinType, expectedType: KotlinType, - dataFlowValue: DataFlowValue, + dataFlowValue: () -> DataFlowValue, dataFlowInfo: DataFlowInfo, reportWarning: (expectedMustNotBeNull: ErrorsJvm.NullabilityInformationSource, actualMayBeNull: ErrorsJvm.NullabilityInformationSource) -> Unit ) { @@ -138,26 +142,28 @@ class JavaNullabilityChecker : AdditionalTypeChecker { } val expectedMustNotBeNull = expectedType.mustNotBeNull() - if (dataFlowInfo.getStableNullability(dataFlowValue) == Nullability.NOT_NULL) { - return - } - val actualMayBeNull = expressionType.mayBeNull() if (expectedMustNotBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN && actualMayBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN) { // a type mismatch error will be reported elsewhere return } - if (expectedMustNotBeNull != null && actualMayBeNull != null) { + if (expectedMustNotBeNull != null && actualMayBeNull != null && + dataFlowInfo.getStableNullability(dataFlowValue()) != Nullability.NOT_NULL) { reportWarning(expectedMustNotBeNull, actualMayBeNull) } } - private fun doIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, body: () -> T): T? = - if (c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull() && - dataFlowValue.type.mustNotBeNull() == ErrorsJvm.NullabilityInformationSource.JAVA) - body() - else null + private fun doIfNotNull( + type: KotlinType, + dataFlowValue: () -> DataFlowValue, + c: ResolutionContext<*>, + body: () -> T + ) = if (type.mustNotBeNull() == ErrorsJvm.NullabilityInformationSource.JAVA && + c.dataFlowInfo.getStableNullability(dataFlowValue()).canBeNull()) + body() + else + null private fun KotlinType.mustNotBeNull(): ErrorsJvm.NullabilityInformationSource? = when { !isError && !isFlexible() && !TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN