Postpone creating DataFlowValue until they're really needed
Unfortnutately DataFlowValueFactory::createDataFlowValue isn't very cheap, so it's better to avoid these calls when it's possible
This commit is contained in:
@@ -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.checkers.CallCheckerContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
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.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
@@ -89,9 +88,13 @@ private val KtExpression.textForRuntimeAssertionInfo
|
|||||||
|
|
||||||
class RuntimeAssertionsDataFlowExtras(
|
class RuntimeAssertionsDataFlowExtras(
|
||||||
private val c: ResolutionContext<*>,
|
private val c: ResolutionContext<*>,
|
||||||
private val dataFlowValue: DataFlowValue,
|
private val expressionType: KotlinType,
|
||||||
private val expression: KtExpression
|
private val expression: KtExpression
|
||||||
) : RuntimeAssertionInfo.DataFlowExtras {
|
) : RuntimeAssertionInfo.DataFlowExtras {
|
||||||
|
private val dataFlowValue by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c)
|
||||||
|
}
|
||||||
|
|
||||||
override val canBeNull: Boolean
|
override val canBeNull: Boolean
|
||||||
get() = c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
|
get() = c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
|
||||||
override val presentableText: String
|
override val presentableText: String
|
||||||
@@ -105,7 +108,7 @@ object RuntimeAssertionsTypeChecker : AdditionalTypeChecker {
|
|||||||
val assertionInfo = RuntimeAssertionInfo.create(
|
val assertionInfo = RuntimeAssertionInfo.create(
|
||||||
c.expectedType,
|
c.expectedType,
|
||||||
expressionType,
|
expressionType,
|
||||||
RuntimeAssertionsDataFlowExtras(c, DataFlowValueFactory.createDataFlowValue(expression, expressionType, c), expression)
|
RuntimeAssertionsDataFlowExtras(c, expressionType, expression)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (assertionInfo != null) {
|
if (assertionInfo != null) {
|
||||||
@@ -128,12 +131,11 @@ object RuntimeAssertionsOnExtensionReceiverCallChecker : CallChecker {
|
|||||||
val expressionReceiverValue = receiverValue.safeAs<ExpressionReceiver>() ?: return
|
val expressionReceiverValue = receiverValue.safeAs<ExpressionReceiver>() ?: return
|
||||||
val receiverExpression = expressionReceiverValue.expression
|
val receiverExpression = expressionReceiverValue.expression
|
||||||
val c = context.resolutionContext
|
val c = context.resolutionContext
|
||||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverExpression, receiverValue.type, c)
|
|
||||||
|
|
||||||
val assertionInfo = RuntimeAssertionInfo.create(
|
val assertionInfo = RuntimeAssertionInfo.create(
|
||||||
receiverParameter.type,
|
receiverParameter.type,
|
||||||
receiverValue.type,
|
receiverValue.type,
|
||||||
RuntimeAssertionsDataFlowExtras(c, dataFlowValue, receiverExpression)
|
RuntimeAssertionsDataFlowExtras(c, receiverValue.type, receiverExpression)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (assertionInfo != null) {
|
if (assertionInfo != null) {
|
||||||
|
|||||||
+29
-23
@@ -44,7 +44,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
|
|||||||
doCheckType(
|
doCheckType(
|
||||||
expressionType,
|
expressionType,
|
||||||
c.expectedType,
|
c.expectedType,
|
||||||
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c),
|
{ DataFlowValueFactory.createDataFlowValue(expression, expressionType, c) } ,
|
||||||
c.dataFlowInfo
|
c.dataFlowInfo
|
||||||
) { expectedMustNotBeNull, actualMayBeNull ->
|
) { expectedMustNotBeNull, actualMayBeNull ->
|
||||||
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, 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 subjectExpression = expression.subjectExpression ?: return
|
||||||
val type = c.trace.getType(subjectExpression) ?: return
|
val type = c.trace.getType(subjectExpression) ?: return
|
||||||
if (type.isFlexible() && TypeUtils.isNullableType(type.asFlexibleType().upperBound)) {
|
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 enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(type) ?: return
|
||||||
val context = c.trace.bindingContext
|
val context = c.trace.bindingContext
|
||||||
if (WhenChecker.getEnumMissingCases(expression, context, enumClassDescriptor).isEmpty()
|
if (WhenChecker.getEnumMissingCases(expression, context, enumClassDescriptor).isEmpty()
|
||||||
&& !WhenChecker.containsNullCase(expression, context)) {
|
&& !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!!))
|
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 baseExpression = expression.baseExpression ?: return
|
||||||
val baseExpressionType = c.trace.getType(baseExpression) ?: return
|
val baseExpressionType = c.trace.getType(baseExpression) ?: return
|
||||||
doIfNotNull(
|
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))
|
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,
|
expression, expression.left!!, expression.right!!, c,
|
||||||
{ c.trace.getType(it) },
|
{ c.trace.getType(it) },
|
||||||
{ value ->
|
{ 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<*>) {
|
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) {
|
if (safeAccess) {
|
||||||
doIfNotNull(dataFlowValue, c) {
|
doIfNotNull(receiverArgument.type, { dataFlowValue }, c) {
|
||||||
c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.callOperationNode!!.psi, receiverArgument.type))
|
c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.callOperationNode!!.psi, receiverArgument.type))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +119,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
|
|||||||
doCheckType(
|
doCheckType(
|
||||||
receiverArgument.type,
|
receiverArgument.type,
|
||||||
receiverParameter.type,
|
receiverParameter.type,
|
||||||
dataFlowValue,
|
{ dataFlowValue },
|
||||||
c.dataFlowInfo
|
c.dataFlowInfo
|
||||||
) { expectedMustNotBeNull,
|
) { expectedMustNotBeNull,
|
||||||
actualMayBeNull ->
|
actualMayBeNull ->
|
||||||
@@ -129,7 +133,7 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
|
|||||||
private fun doCheckType(
|
private fun doCheckType(
|
||||||
expressionType: KotlinType,
|
expressionType: KotlinType,
|
||||||
expectedType: KotlinType,
|
expectedType: KotlinType,
|
||||||
dataFlowValue: DataFlowValue,
|
dataFlowValue: () -> DataFlowValue,
|
||||||
dataFlowInfo: DataFlowInfo,
|
dataFlowInfo: DataFlowInfo,
|
||||||
reportWarning: (expectedMustNotBeNull: ErrorsJvm.NullabilityInformationSource, actualMayBeNull: ErrorsJvm.NullabilityInformationSource) -> Unit
|
reportWarning: (expectedMustNotBeNull: ErrorsJvm.NullabilityInformationSource, actualMayBeNull: ErrorsJvm.NullabilityInformationSource) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -138,26 +142,28 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val expectedMustNotBeNull = expectedType.mustNotBeNull()
|
val expectedMustNotBeNull = expectedType.mustNotBeNull()
|
||||||
if (dataFlowInfo.getStableNullability(dataFlowValue) == Nullability.NOT_NULL) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val actualMayBeNull = expressionType.mayBeNull()
|
val actualMayBeNull = expressionType.mayBeNull()
|
||||||
if (expectedMustNotBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN && actualMayBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN) {
|
if (expectedMustNotBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN && actualMayBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN) {
|
||||||
// a type mismatch error will be reported elsewhere
|
// a type mismatch error will be reported elsewhere
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectedMustNotBeNull != null && actualMayBeNull != null) {
|
if (expectedMustNotBeNull != null && actualMayBeNull != null &&
|
||||||
|
dataFlowInfo.getStableNullability(dataFlowValue()) != Nullability.NOT_NULL) {
|
||||||
reportWarning(expectedMustNotBeNull, actualMayBeNull)
|
reportWarning(expectedMustNotBeNull, actualMayBeNull)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : Any> doIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, body: () -> T): T? =
|
private fun <T : Any> doIfNotNull(
|
||||||
if (c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull() &&
|
type: KotlinType,
|
||||||
dataFlowValue.type.mustNotBeNull() == ErrorsJvm.NullabilityInformationSource.JAVA)
|
dataFlowValue: () -> DataFlowValue,
|
||||||
body()
|
c: ResolutionContext<*>,
|
||||||
else null
|
body: () -> T
|
||||||
|
) = if (type.mustNotBeNull() == ErrorsJvm.NullabilityInformationSource.JAVA &&
|
||||||
|
c.dataFlowInfo.getStableNullability(dataFlowValue()).canBeNull())
|
||||||
|
body()
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
private fun KotlinType.mustNotBeNull(): ErrorsJvm.NullabilityInformationSource? = when {
|
private fun KotlinType.mustNotBeNull(): ErrorsJvm.NullabilityInformationSource? = when {
|
||||||
!isError && !isFlexible() && !TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
|
!isError && !isFlexible() && !TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
|
||||||
|
|||||||
Reference in New Issue
Block a user