Exhaustive when over enum may now use "is" instead of comparison + a pair of tests

This commit is contained in:
Mikhail Glukhikh
2015-06-29 13:55:08 +03:00
parent 786cadb08b
commit e1d3b296e9
6 changed files with 147 additions and 45 deletions
@@ -92,16 +92,13 @@ public final class WhenChecker {
) {
assert isEnumClass(enumClassDescriptor) :
"isWhenOnEnumExhaustive should be called with an enum class descriptor";
boolean notEmpty = false;
Set<ClassDescriptor> entryDescriptors = new HashSet<ClassDescriptor>();
for (DeclarationDescriptor descriptor : enumClassDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors()) {
if (isEnumEntry(descriptor)) {
notEmpty = true;
if (!containsEnumEntryCase(expression, (ClassDescriptor) descriptor, trace)) {
return false;
}
entryDescriptors.add((ClassDescriptor) descriptor);
}
}
return notEmpty;
return !entryDescriptors.isEmpty() && containsAllClassCases(expression, entryDescriptors, trace);
}
private static void collectNestedSubclasses(
@@ -187,25 +184,6 @@ public final class WhenChecker {
return false;
}
private static boolean containsEnumEntryCase(
@NotNull JetWhenExpression whenExpression,
@NotNull ClassDescriptor enumEntry,
@NotNull BindingTrace trace
) {
assert enumEntry.getKind() == ClassKind.ENUM_ENTRY;
for (JetWhenEntry whenEntry : whenExpression.getEntries()) {
for (JetWhenCondition condition : whenEntry.getConditions()) {
if (!(condition instanceof JetWhenConditionWithExpression)) {
continue;
}
if (isCheckForEnumEntry((JetWhenConditionWithExpression) condition, enumEntry, trace)) {
return true;
}
}
}
return false;
}
private static boolean containsAllClassCases(
@NotNull JetWhenExpression whenExpression,
@NotNull Set<ClassDescriptor> memberDescriptors,
@@ -215,27 +193,35 @@ public final class WhenChecker {
for (JetWhenEntry whenEntry : whenExpression.getEntries()) {
for (JetWhenCondition condition : whenEntry.getConditions()) {
boolean negated = false;
JetType checkedType = null;
ClassDescriptor checkedDescriptor = null;
if (condition instanceof JetWhenConditionIsPattern) {
JetWhenConditionIsPattern conditionIsPattern = (JetWhenConditionIsPattern) condition;
checkedType = trace.get(BindingContext.TYPE, conditionIsPattern.getTypeReference());
JetType checkedType = trace.get(BindingContext.TYPE, conditionIsPattern.getTypeReference());
if (checkedType != null) {
checkedDescriptor = TypeUtils.getClassDescriptor(checkedType);
}
negated = conditionIsPattern.isNegated();
}
else if (condition instanceof JetWhenConditionWithExpression) {
JetWhenConditionWithExpression conditionWithExpression = (JetWhenConditionWithExpression) condition;
if (conditionWithExpression.getExpression() != null) {
checkedType = trace.getBindingContext().getType(conditionWithExpression.getExpression());
JetSimpleNameExpression reference = getReference(conditionWithExpression.getExpression());
if (reference != null) {
DeclarationDescriptor target = trace.get(BindingContext.REFERENCE_TARGET, reference);
if (target instanceof ClassDescriptor) {
checkedDescriptor = (ClassDescriptor) target;
}
}
}
}
if (checkedType == null) {
continue;
}
ClassDescriptor checkedDescriptor = TypeUtils.getClassDescriptor(checkedType);
// Checks are important only for nested subclasses of the sealed class
// In additional, check without "is" is important only for objects
if (checkedDescriptor == null
|| !memberDescriptors.contains(checkedDescriptor)
|| (condition instanceof JetWhenConditionWithExpression && !DescriptorUtils.isObject(checkedDescriptor))) {
|| (condition instanceof JetWhenConditionWithExpression
&& !DescriptorUtils.isObject(checkedDescriptor)
&& !DescriptorUtils.isEnumEntry(checkedDescriptor))) {
continue;
}
if (negated) {
@@ -268,18 +254,6 @@ public final class WhenChecker {
return false;
}
private static boolean isCheckForEnumEntry(
@NotNull JetWhenConditionWithExpression whenExpression,
@NotNull ClassDescriptor enumEntry,
@NotNull BindingTrace trace
) {
JetSimpleNameExpression reference = getReference(whenExpression.getExpression());
if (reference == null) return false;
DeclarationDescriptor target = trace.get(BindingContext.REFERENCE_TARGET, reference);
return target == enumEntry;
}
@Nullable
private static JetSimpleNameExpression getReference(@Nullable JetExpression expression) {
if (expression == null) {