From 44b07d8dfa1a34503c0fd4afd5ebe5706928f97c Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 11 Jan 2016 18:32:27 +0300 Subject: [PATCH] Refactoring: context instead of trace in WhenChecker and around --- .../JavaNullabilityWarningsChecker.kt | 6 +- .../cfg/ControlFlowInformationProvider.java | 5 +- .../kotlin/cfg/ControlFlowProcessor.java | 3 +- .../org/jetbrains/kotlin/cfg/WhenChecker.kt | 69 ++++++++++--------- .../resolve/CompileTimeConstantUtils.java | 4 +- .../PatternMatchingTypingVisitor.java | 2 +- .../SimplifyBooleanWithConstantsIntention.kt | 4 +- 7 files changed, 46 insertions(+), 47 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityWarningsChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityWarningsChecker.kt index 2fd2a39e97a..72ae703860f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityWarningsChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityWarningsChecker.kt @@ -104,9 +104,9 @@ class JavaNullabilityWarningsChecker : AdditionalTypeChecker { val type = expression.subjectExpression?.let { c.trace.getType(it) } ?: return if (type.isFlexible() && TypeUtils.isNullableType(type.flexibility().upperBound) && !type.annotations.isMarkedNotNull()) { val enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(type) ?: return - - if (WhenChecker.getEnumMissingCases(expression, c.trace, enumClassDescriptor).isEmpty() - && !WhenChecker.containsNullCase(expression, c.trace)) { + val context = c.trace.bindingContext + if (WhenChecker.getEnumMissingCases(expression, context, enumClassDescriptor).isEmpty() + && !WhenChecker.containsNullCase(expression, context)) { c.trace.report(ErrorsJvm.WHEN_ENUM_CAN_BE_NULL_IN_JAVA.on(expression.getSubjectExpression())) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index e796ea64cd4..18bf40d9649 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -743,7 +743,8 @@ public class ControlFlowInformationProvider { KtWhenExpression whenExpression = (KtWhenExpression) element; if (whenExpression.getElseExpression() != null) continue; - List necessaryCases = WhenChecker.getNecessaryCases(whenExpression, trace); + BindingContext context = trace.getBindingContext(); + List necessaryCases = WhenChecker.getNecessaryCases(whenExpression, context); if (!necessaryCases.isEmpty()) { trace.report(NO_ELSE_IN_WHEN.on(whenExpression, necessaryCases)); } @@ -752,7 +753,7 @@ public class ControlFlowInformationProvider { trace.getType(whenExpression.getSubjectExpression())); if (enumClassDescriptor != null) { List missingCases = WhenChecker.getEnumMissingCases( - whenExpression, trace, enumClassDescriptor + whenExpression, context, enumClassDescriptor ); if (!missingCases.isEmpty()) { trace.report(NON_EXHAUSTIVE_WHEN.on(whenExpression, missingCases)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.java index a7a7906dab4..37c3c31d5b5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.java @@ -802,8 +802,7 @@ public class ControlFlowProcessor { generateInstructions(condition); } mark(expression); - boolean conditionIsTrueConstant = CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace, true); - if (!conditionIsTrueConstant) { + if (!CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace.getBindingContext(), true)) { builder.jumpOnFalse(loopInfo.getExitPoint(), expression, builder.getBoundValue(condition)); } else { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt index bfa903c4de5..038d188d3cd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt @@ -52,12 +52,12 @@ val List.hasUnknown: Boolean private interface WhenExhaustivenessChecker { fun getMissingCases( expression: KtWhenExpression, - trace: BindingTrace, + context: BindingContext, subjectDescriptor: ClassDescriptor?, nullable: Boolean ): List - fun isApplicable(subjectType: KotlinType, trace: BindingTrace): Boolean = false + fun isApplicable(subjectType: KotlinType): Boolean = false } private object NullMissingCase : WhenMissingCase { @@ -65,17 +65,17 @@ private object NullMissingCase : WhenMissingCase { } private object WhenOnNullableExhaustivenessChecker : WhenExhaustivenessChecker { - override fun getMissingCases(expression: KtWhenExpression, trace: BindingTrace, subjectDescriptor: ClassDescriptor?, nullable: Boolean) = - if (nullable) getNullCaseIfMissing(expression, trace) else listOf() + override fun getMissingCases(expression: KtWhenExpression, context: BindingContext, subjectDescriptor: ClassDescriptor?, nullable: Boolean) = + if (nullable) getNullCaseIfMissing(expression, context) else listOf() - override fun isApplicable(subjectType: KotlinType, trace: BindingTrace): Boolean = TypeUtils.isNullableType(subjectType) + override fun isApplicable(subjectType: KotlinType): Boolean = TypeUtils.isNullableType(subjectType) - private fun getNullCaseIfMissing(expression: KtWhenExpression, trace: BindingTrace): List { + private fun getNullCaseIfMissing(expression: KtWhenExpression, context: BindingContext): List { for (entry in expression.entries) { for (condition in entry.conditions) { if (condition is KtWhenConditionWithExpression) { condition.expression?.let { - val type = trace.bindingContext.getType(it) + val type = context.getType(it) if (type != null && KotlinBuiltIns.isNullableNothing(type)) { return listOf() } @@ -94,7 +94,7 @@ private class BooleanMissingCase(val b: Boolean) : WhenMissingCase { private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker { override fun getMissingCases( expression: KtWhenExpression, - trace: BindingTrace, + context: BindingContext, subjectDescriptor: ClassDescriptor?, nullable: Boolean ): List { @@ -105,8 +105,8 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker { for (whenCondition in whenEntry.conditions) { if (whenCondition is KtWhenConditionWithExpression) { val whenExpression = whenCondition.expression - if (CompileTimeConstantUtils.canBeReducedToBooleanConstant(whenExpression, trace, true)) containsTrue = true - if (CompileTimeConstantUtils.canBeReducedToBooleanConstant(whenExpression, trace, false)) containsFalse = true + if (CompileTimeConstantUtils.canBeReducedToBooleanConstant(whenExpression, context, true)) containsTrue = true + if (CompileTimeConstantUtils.canBeReducedToBooleanConstant(whenExpression, context, false)) containsFalse = true } } } @@ -114,7 +114,7 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker { (if (!containsFalse) listOf(BooleanMissingCase(false)) else listOf()) } - override fun isApplicable(subjectType: KotlinType, trace: BindingTrace): Boolean { + override fun isApplicable(subjectType: KotlinType): Boolean { return KotlinBuiltIns.isBoolean(TypeUtils.makeNotNullable(subjectType)) } } @@ -134,7 +134,7 @@ private abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChec protected fun getMissingClassCases( whenExpression: KtWhenExpression, memberDescriptors: Set, - trace: BindingTrace + context: BindingContext ): List { // when on empty enum / sealed is considered non-exhaustive, see test whenOnEmptySealed if (memberDescriptors.isEmpty()) return listOf(UnknownMissingCase) @@ -144,7 +144,7 @@ private abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChec var negated = false var checkedDescriptor: ClassDescriptor? = null if (condition is KtWhenConditionIsPattern) { - val checkedType = trace.get(BindingContext.TYPE, condition.typeReference) + val checkedType = context.get(BindingContext.TYPE, condition.typeReference) if (checkedType != null) { checkedDescriptor = TypeUtils.getClassDescriptor(checkedType) } @@ -154,7 +154,7 @@ private abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChec if (condition.expression != null) { val reference = getReference(condition.expression) if (reference != null) { - val target = trace.get(BindingContext.REFERENCE_TARGET, reference) + val target = context.get(BindingContext.REFERENCE_TARGET, reference) if (target is ClassDescriptor) { checkedDescriptor = target } @@ -188,7 +188,7 @@ private abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChec private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecker() { override fun getMissingCases( expression: KtWhenExpression, - trace: BindingTrace, + context: BindingContext, subjectDescriptor: ClassDescriptor?, nullable: Boolean ): List { @@ -198,10 +198,10 @@ private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecke .filter { isEnumEntry(it) } .filterIsInstance() .toSet() - return getMissingClassCases(expression, entryDescriptors, trace) + return getMissingClassCases(expression, entryDescriptors, context) } - override fun isApplicable(subjectType: KotlinType, trace: BindingTrace): Boolean { + override fun isApplicable(subjectType: KotlinType): Boolean { return WhenChecker.getClassDescriptorOfTypeIfEnum(subjectType) != null } } @@ -209,7 +209,7 @@ private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecke private object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChecker() { override fun getMissingCases( expression: KtWhenExpression, - trace: BindingTrace, + context: BindingContext, subjectDescriptor: ClassDescriptor?, nullable: Boolean ): List { @@ -220,10 +220,10 @@ private object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChec val memberClassDescriptors = LinkedHashSet() collectNestedSubclasses(subjectDescriptor!!, subjectDescriptor, memberClassDescriptors) // When on a sealed class without derived members is considered non-exhaustive (see test WhenOnEmptySealed) - return getMissingClassCases(expression, memberClassDescriptors, trace) + return getMissingClassCases(expression, memberClassDescriptors, context) } - override fun isApplicable(subjectType: KotlinType, trace: BindingTrace): Boolean { + override fun isApplicable(subjectType: KotlinType): Boolean { return TypeUtils.getClassDescriptor(subjectType)?.modality == Modality.SEALED } @@ -251,8 +251,8 @@ object WhenChecker { WhenOnNullableExhaustivenessChecker) @JvmStatic - fun getNecessaryCases(expression: KtWhenExpression, trace: BindingTrace) = - if (expression.isUsedAsExpression(trace.bindingContext)) getMissingCases(expression, trace) + fun getNecessaryCases(expression: KtWhenExpression, context: BindingContext) = + if (expression.isUsedAsExpression(context)) getMissingCases(expression, context) else listOf() @JvmStatic @@ -274,9 +274,9 @@ object WhenChecker { @JvmStatic fun getEnumMissingCases( expression: KtWhenExpression, - trace: BindingTrace, + context: BindingContext, enumClassDescriptor: ClassDescriptor - ) = WhenOnEnumExhaustivenessChecker.getMissingCases(expression, trace, enumClassDescriptor, false) + ) = WhenOnEnumExhaustivenessChecker.getMissingCases(expression, context, enumClassDescriptor, false) /** * It's assumed that function is called for a final type. In this case the only possible smart cast is to not nullable type. @@ -285,11 +285,12 @@ object WhenChecker { private fun isNullableTypeWithoutPossibleSmartCast( expression: KtExpression?, type: KotlinType, - trace: BindingTrace): Boolean { + context: BindingContext + ): Boolean { if (expression == null) return false // Normally should not happen if (!TypeUtils.isNullableType(type)) return false // We cannot read data flow information here due to lack of inputs (module descriptor is necessary) - if (trace.get(BindingContext.SMARTCAST, expression) != null) { + if (context.get(BindingContext.SMARTCAST, expression) != null) { // We have smart cast from enum or boolean to something // Not very nice but we *can* decide it was smart cast to not-null // because both enum and boolean are final @@ -298,17 +299,17 @@ object WhenChecker { return true } - private fun getMissingCases(expression: KtWhenExpression, trace: BindingTrace): List { - val type = whenSubjectType(expression, trace.bindingContext) ?: return listOf(UnknownMissingCase) - val nullable = !type.isFlexible() && isNullableTypeWithoutPossibleSmartCast(expression.subjectExpression, type, trace) - val checkers = exhaustivenessCheckers.filter { it.isApplicable(type, trace) } + private fun getMissingCases(expression: KtWhenExpression, context: BindingContext): List { + val type = whenSubjectType(expression, context) ?: return listOf(UnknownMissingCase) + val nullable = !type.isFlexible() && isNullableTypeWithoutPossibleSmartCast(expression.subjectExpression, type, context) + val checkers = exhaustivenessCheckers.filter { it.isApplicable(type) } if (checkers.isEmpty()) return listOf(UnknownMissingCase) - return checkers.map { it.getMissingCases(expression, trace, TypeUtils.getClassDescriptor(type), nullable) }.flatten() + return checkers.map { it.getMissingCases(expression, context, TypeUtils.getClassDescriptor(type), nullable) }.flatten() } @JvmStatic fun isWhenExhaustive(expression: KtWhenExpression, trace: BindingTrace) = - if (getMissingCases(expression, trace).isEmpty()) { + if (getMissingCases(expression, trace.bindingContext).isEmpty()) { trace.record(BindingContext.EXHAUSTIVE_WHEN, expression) true } else { @@ -316,8 +317,8 @@ object WhenChecker { } @JvmStatic - fun containsNullCase(expression: KtWhenExpression, trace: BindingTrace) = - WhenOnNullableExhaustivenessChecker.getMissingCases(expression, trace, null, true).isEmpty() + fun containsNullCase(expression: KtWhenExpression, context: BindingContext) = + WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, null, true).isEmpty() @JvmStatic fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompileTimeConstantUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompileTimeConstantUtils.java index 337e0b91b6f..1212e9326bd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompileTimeConstantUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompileTimeConstantUtils.java @@ -109,14 +109,14 @@ public class CompileTimeConstantUtils { public static boolean canBeReducedToBooleanConstant( @Nullable KtExpression expression, - @NotNull BindingTrace trace, + @NotNull BindingContext context, @Nullable Boolean expectedValue ) { KtExpression effectiveExpression = KtPsiUtil.deparenthesize(expression); if (effectiveExpression == null) return false; - CompileTimeConstant compileTimeConstant = ConstantExpressionEvaluator.getConstant(effectiveExpression, trace.getBindingContext()); + CompileTimeConstant compileTimeConstant = ConstantExpressionEvaluator.getConstant(effectiveExpression, context); if (!(compileTimeConstant instanceof TypedCompileTimeConstant) || compileTimeConstant.getUsesVariableAsConstant()) return false; ConstantValue constantValue = ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java index 829daa16420..ac142972e8b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java @@ -93,7 +93,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { loopBreakContinuePossible = typeInfo.getJumpOutPossible(); subjectType = typeInfo.getType(); assert subjectType != null; - if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace)) { + if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace.getBindingContext())) { TemporaryBindingTrace trace = TemporaryBindingTrace.create(context.trace, "Temporary trace for when subject nullability"); ExpressionTypingContext subjectContext = context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace); diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyBooleanWithConstantsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyBooleanWithConstantsIntention.kt index dd5b1d3c2ae..3191421ce12 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyBooleanWithConstantsIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyBooleanWithConstantsIntention.kt @@ -124,9 +124,7 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte private fun simplifyExpression(expression: KtExpression) = expression.replaced(toSimplifiedExpression(expression)) private fun KtExpression.canBeReducedToBooleanConstant(constant: Boolean?): Boolean { - val bindingContext = this.analyze() - val trace = DelegatingBindingTrace(bindingContext, "trace for constant check") - return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, trace, constant) + return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, this.analyze(), constant) } private fun KtExpression.canBeReducedToTrue() = canBeReducedToBooleanConstant(true)