Exhaustive when over enum may now use "is" instead of comparison + a pair of tests
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user