From 529763b9bdbf795c1620c0e84b4c56e827739da4 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 24 Apr 2019 12:16:45 +0300 Subject: [PATCH] [Misc] Get rid of public Companion methods in SmartCastManager It's necessary for type-refinement component to be injected --- .../kotlin/resolve/calls/CandidateResolver.kt | 7 +- .../DiagnosticReporterByTrackingStrategy.kt | 10 +- .../calls/smartcasts/SmartCastManager.kt | 165 ++++++++---------- .../tower/KotlinToResolvedCallTransformer.kt | 7 +- .../types/expressions/DataFlowAnalyzer.java | 7 +- .../jetbrains/kotlin/idea/util/CallType.kt | 19 +- 6 files changed, 105 insertions(+), 110 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index f7d6cf90ed2..811275c62b9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -377,7 +377,7 @@ class CandidateResolver( val spreadElement = argument.getSpreadElement() if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) { val dataFlowValue = dataFlowValueFactory.createDataFlowValue(expression, type, context) - val smartCastResult = SmartCastManager.checkAndRecordPossibleCast( + val smartCastResult = smartCastManager.checkAndRecordPossibleCast( dataFlowValue, expectedType, expression, context, call = null, recordExpressionType = false ) @@ -544,11 +544,10 @@ class CandidateResolver( } else if (!nullableImplicitInvokeReceiver && smartCastNeeded) { // Look if smart cast has some useful nullability info - val smartCastResult = SmartCastManager.checkAndRecordPossibleCast( + val smartCastResult = smartCastManager.checkAndRecordPossibleCast( dataFlowValue, expectedReceiverParameterType, - { possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) }, expression, this, candidateCall.call, recordExpressionType = true - ) + ) { possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) } if (smartCastResult == null) { if (notNullReceiverExpected) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 990316cd636..c94747ba02d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -39,7 +39,8 @@ class DiagnosticReporterByTrackingStrategy( val context: BasicCallResolutionContext, val psiKotlinCall: PSIKotlinCall, val dataFlowValueFactory: DataFlowValueFactory, - val allDiagnostics: List + val allDiagnostics: List, + private val smartCastManager: SmartCastManager ) : DiagnosticReporter { private val trace = context.trace as TrackingBindingTrace private val tracingStrategy: TracingStrategy get() = psiKotlinCall.tracingStrategy @@ -195,7 +196,7 @@ class DiagnosticReporterByTrackingStrategy( ) val dataFlowValue = dataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context) val call = if (call.callElement is KtBinaryExpression) null else call - SmartCastManager.checkAndRecordPossibleCast( + smartCastManager.checkAndRecordPossibleCast( dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call, recordExpressionType = true ) @@ -204,7 +205,7 @@ class DiagnosticReporterByTrackingStrategy( trace.markAsReported() val receiverValue = expressionArgument.receiver.receiverValue val dataFlowValue = dataFlowValueFactory.createDataFlowValue(receiverValue, context) - SmartCastManager.checkAndRecordPossibleCast( + smartCastManager.checkAndRecordPossibleCast( dataFlowValue, smartCastDiagnostic.smartCastType, (receiverValue as? ExpressionReceiver)?.expression, context, call, recordExpressionType = true ) @@ -340,6 +341,5 @@ class DiagnosticReporterByTrackingStrategy( } - val NewConstraintError.upperKotlinType get() = upperType as KotlinType -val NewConstraintError.lowerKotlinType get() = lowerType as KotlinType \ No newline at end of file +val NewConstraintError.lowerKotlinType get() = lowerType as KotlinType diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt index 41422ed21da..4f2b73fe958 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt @@ -119,13 +119,89 @@ class SmartCastManager { else -> null } + fun checkAndRecordPossibleCast( + dataFlowValue: DataFlowValue, + expectedType: KotlinType, + expression: KtExpression?, + c: ResolutionContext<*>, + call: Call?, + recordExpressionType: Boolean, + additionalPredicate: ((KotlinType) -> Boolean)? = null + ): SmartCastResult? { + val calleeExpression = call?.calleeExpression + val expectedTypes = if (c.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) + expectedType.expandIntersectionTypeIfNecessary() + else + listOf(expectedType) + + for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue, c.languageVersionSettings)) { + if (expectedTypes.any { ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, it) } && + (additionalPredicate == null || additionalPredicate(possibleType)) + ) { + if (expression != null) { + recordCastOrError(expression, possibleType, c.trace, dataFlowValue, call, recordExpressionType) + } else if (calleeExpression != null && dataFlowValue.isStable) { + val receiver = (dataFlowValue.identifierInfo as? IdentifierInfo.Receiver)?.value + if (receiver is ImplicitReceiver) { + val oldSmartCasts = c.trace[IMPLICIT_RECEIVER_SMARTCAST, calleeExpression] + val newSmartCasts = ImplicitSmartCasts(receiver, possibleType) + if (oldSmartCasts != null) { + val oldType = oldSmartCasts.receiverTypes[receiver] + if (oldType != null && oldType != possibleType) { + throw AssertionError( + "Rewriting key $receiver for implicit smart cast on ${calleeExpression.text}: " + + "was $oldType, now $possibleType" + ) + } + } + c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, + oldSmartCasts?.let { it + newSmartCasts } ?: newSmartCasts) + + } + } + return SmartCastResult(possibleType, dataFlowValue.isStable) + } + } + + if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable) { + // Handling cases like: + // fun bar(x: Any) {} + // fun foo(x: T) { + // if (x != null) { + // bar(x) // Should be allowed with smart cast + // } + // } + // + // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T. + // But at the same time we're sure that `x` can't be null and just check for such cases manually + + // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds + // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression) + val immanentlyNotNull = !dataFlowValue.immanentNullability.canBeNull() + val nullableExpectedType = TypeUtils.makeNullable(expectedType) + + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && + (additionalPredicate == null || additionalPredicate(dataFlowValue.type)) + ) { + if (!immanentlyNotNull && expression != null) { + recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, call, recordExpressionType) + } + + return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isStable) + } + return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, call, recordExpressionType) + } + + return null + } + enum class ReceiverSmartCastResult { OK, SMARTCAST_NEEDED_OR_NOT_NULL_EXPECTED } companion object { - + // REVIEW: make it non-static too? private fun recordCastOrError( expression: KtExpression, type: KotlinType, @@ -158,92 +234,5 @@ class SmartCastManager { trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.text, dataFlowValue.kind.description)) } } - - fun checkAndRecordPossibleCast( - dataFlowValue: DataFlowValue, - expectedType: KotlinType, - expression: KtExpression?, - c: ResolutionContext<*>, - call: Call?, - recordExpressionType: Boolean - ): SmartCastResult? { - return checkAndRecordPossibleCast(dataFlowValue, expectedType, null, expression, c, call, recordExpressionType) - } - - fun checkAndRecordPossibleCast( - dataFlowValue: DataFlowValue, - expectedType: KotlinType, - additionalPredicate: ((KotlinType) -> Boolean)?, - expression: KtExpression?, - c: ResolutionContext<*>, - call: Call?, - recordExpressionType: Boolean - ): SmartCastResult? { - val calleeExpression = call?.calleeExpression - val expectedTypes = if (c.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) - expectedType.expandIntersectionTypeIfNecessary() - else - listOf(expectedType) - - for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue, c.languageVersionSettings)) { - if (expectedTypes.any { ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, it) } && - (additionalPredicate == null || additionalPredicate(possibleType)) - ) { - if (expression != null) { - recordCastOrError(expression, possibleType, c.trace, dataFlowValue, call, recordExpressionType) - } else if (calleeExpression != null && dataFlowValue.isStable) { - val receiver = (dataFlowValue.identifierInfo as? IdentifierInfo.Receiver)?.value - if (receiver is ImplicitReceiver) { - val oldSmartCasts = c.trace[IMPLICIT_RECEIVER_SMARTCAST, calleeExpression] - val newSmartCasts = ImplicitSmartCasts(receiver, possibleType) - if (oldSmartCasts != null) { - val oldType = oldSmartCasts.receiverTypes[receiver] - if (oldType != null && oldType != possibleType) { - throw AssertionError( - "Rewriting key $receiver for implicit smart cast on ${calleeExpression.text}: " + - "was $oldType, now $possibleType" - ) - } - } - c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, - oldSmartCasts?.let { it + newSmartCasts } ?: newSmartCasts) - - } - } - return SmartCastResult(possibleType, dataFlowValue.isStable) - } - } - - if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable) { - // Handling cases like: - // fun bar(x: Any) {} - // fun foo(x: T) { - // if (x != null) { - // bar(x) // Should be allowed with smart cast - // } - // } - // - // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T. - // But at the same time we're sure that `x` can't be null and just check for such cases manually - - // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds - // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression) - val immanentlyNotNull = !dataFlowValue.immanentNullability.canBeNull() - val nullableExpectedType = TypeUtils.makeNullable(expectedType) - - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && - (additionalPredicate == null || additionalPredicate(dataFlowValue.type)) - ) { - if (!immanentlyNotNull && expression != null) { - recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, call, recordExpressionType) - } - - return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isStable) - } - return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, call, recordExpressionType) - } - - return null - } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index 9ede95f61c4..1dd6625299c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.makeNullableTypeIfSaf import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant @@ -69,7 +70,8 @@ class KotlinToResolvedCallTransformer( private val moduleDescriptor: ModuleDescriptor, private val dataFlowValueFactory: DataFlowValueFactory, private val builtIns: KotlinBuiltIns, - private val typeSystemContext: TypeSystemInferenceExtensionContextDelegate + private val typeSystemContext: TypeSystemInferenceExtensionContextDelegate, + private val smartCastManager: SmartCastManager ) { companion object { private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC @@ -471,7 +473,8 @@ class KotlinToResolvedCallTransformer( newContext, completedCallAtom.atom.psiKotlinCall, context.dataFlowValueFactory, - allDiagnostics + allDiagnostics, + smartCastManager ) for (diagnostic in allDiagnostics) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index b50a3a80e34..fa601dc12cc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -61,6 +61,7 @@ public class DataFlowAnalyzer { private final LanguageVersionSettings languageVersionSettings; private final EffectSystem effectSystem; private final DataFlowValueFactory dataFlowValueFactory; + private final SmartCastManager smartCastManager; public DataFlowAnalyzer( @NotNull Iterable additionalTypeCheckers, @@ -70,7 +71,8 @@ public class DataFlowAnalyzer { @NotNull ExpressionTypingFacade facade, @NotNull LanguageVersionSettings languageVersionSettings, @NotNull EffectSystem effectSystem, - @NotNull DataFlowValueFactory factory + @NotNull DataFlowValueFactory factory, + @NotNull SmartCastManager smartCastManager ) { this.additionalTypeCheckers = additionalTypeCheckers; this.constantExpressionEvaluator = constantExpressionEvaluator; @@ -80,6 +82,7 @@ public class DataFlowAnalyzer { this.languageVersionSettings = languageVersionSettings; this.effectSystem = effectSystem; this.dataFlowValueFactory = factory; + this.smartCastManager = smartCastManager; } // NB: use this method only for functions from 'Any' @@ -349,7 +352,7 @@ public class DataFlowAnalyzer { ) { DataFlowValue dataFlowValue = dataFlowValueFactory.createDataFlowValue(expression, expressionType, c); - return SmartCastManager.Companion.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false); + return smartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false, null); } public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) { diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt index 1accf0255d1..3d5954be548 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt @@ -133,8 +133,7 @@ sealed class CallTypeAndReceiver(CallType.DOT, receiver) class SAFE(receiver: KtExpression) : CallTypeAndReceiver(CallType.SAFE, receiver) class SUPER_MEMBERS(receiver: KtSuperExpression) : CallTypeAndReceiver( - CallType.SUPER_MEMBERS, - receiver + CallType.SUPER_MEMBERS, receiver ) class INFIX(receiver: KtExpression) : CallTypeAndReceiver(CallType.INFIX, receiver) @@ -275,8 +274,8 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex( val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext) receiverValueTypes( receiverValue, lhs.dataFlowInfo, bindingContext, - moduleDescriptor, stableSmartCastsOnly, languageVersionSettings, - resolutionFacade.frontendService() + moduleDescriptor, stableSmartCastsOnly, + resolutionFacade ).map { ReceiverType(it, 0) } } } @@ -339,8 +338,8 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex( fun addReceiverType(receiverValue: ReceiverValue, implicit: Boolean) { val types = receiverValueTypes( - receiverValue, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly, languageVersionSettings, - resolutionFacade.frontendService() + receiverValue, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly, + resolutionFacade ) types.mapTo(result) { type -> ReceiverType(type, receiverIndex, receiverValue.takeIf { implicit }) } @@ -362,12 +361,14 @@ private fun receiverValueTypes( bindingContext: BindingContext, moduleDescriptor: ModuleDescriptor, stableSmartCastsOnly: Boolean, - languageVersionSettings: LanguageVersionSettings, - dataFlowValueFactory: DataFlowValueFactory + resolutionFacade: ResolutionFacade ): List { + val languageVersionSettings = resolutionFacade.frontendService() + val dataFlowValueFactory = resolutionFacade.frontendService() + val smartCastManager = resolutionFacade.frontendService() val dataFlowValue = dataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor) return if (dataFlowValue.isStable || !stableSmartCastsOnly) { // we don't include smart cast receiver types for "unstable" receiver value to mark members grayed - SmartCastManager().getSmartCastVariantsWithLessSpecificExcluded( + smartCastManager.getSmartCastVariantsWithLessSpecificExcluded( receiverValue, bindingContext, moduleDescriptor,