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) {
@@ -0,0 +1,11 @@
enum class MyEnum {
A, B, C
}
fun foo(x: MyEnum): Int {
return when (x) {
is MyEnum.A -> 1
is MyEnum.B -> 2
is MyEnum.C -> 3
}
}
@@ -0,0 +1,47 @@
package
internal fun foo(/*0*/ x: MyEnum): kotlin.Int
internal final enum class MyEnum : kotlin.Enum<MyEnum> {
public enum entry A : MyEnum {
private constructor A()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public enum entry B : MyEnum {
private constructor B()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public enum entry C : MyEnum {
private constructor C()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
private constructor MyEnum()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyEnum
public final /*synthesized*/ fun values(): kotlin.Array<MyEnum>
}
@@ -0,0 +1,11 @@
enum class MyEnum {
A, B, C
}
fun foo(x: MyEnum): Int {
return when (x) {
MyEnum.A -> 1
is MyEnum.B -> 2
is MyEnum.C -> 3
}
}
@@ -0,0 +1,47 @@
package
internal fun foo(/*0*/ x: MyEnum): kotlin.Int
internal final enum class MyEnum : kotlin.Enum<MyEnum> {
public enum entry A : MyEnum {
private constructor A()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public enum entry B : MyEnum {
private constructor B()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public enum entry C : MyEnum {
private constructor C()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
private constructor MyEnum()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyEnum
public final /*synthesized*/ fun values(): kotlin.Array<MyEnum>
}
@@ -14040,6 +14040,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("ExhaustiveEnumIs.kt")
public void testExhaustiveEnumIs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/ExhaustiveEnumIs.kt");
doTest(fileName);
}
@TestMetadata("ExhaustiveEnumMixed.kt")
public void testExhaustiveEnumMixed() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/ExhaustiveEnumMixed.kt");
doTest(fileName);
}
@TestMetadata("ExhaustiveInitialization.kt")
public void testExhaustiveInitialization() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/ExhaustiveInitialization.kt");