Refactoring: context instead of trace in WhenChecker and around
This commit is contained in:
+3
-3
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -743,7 +743,8 @@ public class ControlFlowInformationProvider {
|
||||
KtWhenExpression whenExpression = (KtWhenExpression) element;
|
||||
if (whenExpression.getElseExpression() != null) continue;
|
||||
|
||||
List<WhenMissingCase> necessaryCases = WhenChecker.getNecessaryCases(whenExpression, trace);
|
||||
BindingContext context = trace.getBindingContext();
|
||||
List<WhenMissingCase> 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<WhenMissingCase> missingCases = WhenChecker.getEnumMissingCases(
|
||||
whenExpression, trace, enumClassDescriptor
|
||||
whenExpression, context, enumClassDescriptor
|
||||
);
|
||||
if (!missingCases.isEmpty()) {
|
||||
trace.report(NON_EXHAUSTIVE_WHEN.on(whenExpression, missingCases));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -52,12 +52,12 @@ val List<WhenMissingCase>.hasUnknown: Boolean
|
||||
private interface WhenExhaustivenessChecker {
|
||||
fun getMissingCases(
|
||||
expression: KtWhenExpression,
|
||||
trace: BindingTrace,
|
||||
context: BindingContext,
|
||||
subjectDescriptor: ClassDescriptor?,
|
||||
nullable: Boolean
|
||||
): List<WhenMissingCase>
|
||||
|
||||
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<WhenMissingCase> {
|
||||
private fun getNullCaseIfMissing(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> {
|
||||
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<WhenMissingCase> {
|
||||
@@ -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<ClassDescriptor>,
|
||||
trace: BindingTrace
|
||||
context: BindingContext
|
||||
): List<WhenMissingCase> {
|
||||
// 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<WhenMissingCase> {
|
||||
@@ -198,10 +198,10 @@ private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecke
|
||||
.filter { isEnumEntry(it) }
|
||||
.filterIsInstance<ClassDescriptor>()
|
||||
.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<WhenMissingCase> {
|
||||
@@ -220,10 +220,10 @@ private object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChec
|
||||
val memberClassDescriptors = LinkedHashSet<ClassDescriptor>()
|
||||
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<WhenMissingCase> {
|
||||
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<WhenMissingCase> {
|
||||
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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-3
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user