KT-1185 Support full enumeration check for 'when'
This commit is contained in:
+7
-4
@@ -10,11 +10,8 @@ import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.TypeSubstitutor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
@@ -66,6 +63,12 @@ public class JavaClassMembersScope extends JavaClassOrPackageScope {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<DeclarationDescriptor> getAllDescriptors() {
|
||||
|
||||
@@ -762,8 +762,6 @@ public class JetControlFlowProcessor {
|
||||
|
||||
@Override
|
||||
public void visitWhenExpression(JetWhenExpression expression) {
|
||||
// TODO : no more than one else
|
||||
// TODO : else must be the last
|
||||
JetExpression subjectExpression = expression.getSubjectExpression();
|
||||
if (subjectExpression != null) {
|
||||
value(subjectExpression, inCondition);
|
||||
@@ -815,7 +813,8 @@ public class JetControlFlowProcessor {
|
||||
}
|
||||
}
|
||||
builder.bindLabel(doneLabel);
|
||||
if (!hasElseOrIrrefutableBranch) {
|
||||
boolean isWhenExhaust = WhenChecker.isWhenExhaust(expression, trace);
|
||||
if (!hasElseOrIrrefutableBranch && !isWhenExhaust) {
|
||||
trace.report(NO_ELSE_IN_WHEN.on(expression));
|
||||
}
|
||||
builder.stopAllowDead();
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.jetbrains.jet.lang.cfg;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassKind;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.Modality;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeProjection;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author svtk
|
||||
*/
|
||||
public class WhenChecker {
|
||||
public static boolean isWhenExhaust(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) {
|
||||
JetExpression subjectExpression = expression.getSubjectExpression();
|
||||
if (subjectExpression == null) return false;
|
||||
JetType type = trace.get(BindingContext.EXPRESSION_TYPE, subjectExpression);
|
||||
if (type == null) return false;
|
||||
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
if (!(declarationDescriptor instanceof ClassDescriptor)) return false;
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
|
||||
if (classDescriptor.getKind() != ClassKind.ENUM_CLASS || classDescriptor.getModality().isOverridable()) return false;
|
||||
ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor();
|
||||
assert classObjectDescriptor != null;
|
||||
JetScope memberScope = classObjectDescriptor.getMemberScope(Collections.<TypeProjection>emptyList());
|
||||
Set<ClassDescriptor> objectDescriptors = memberScope.getObjectDescriptors();
|
||||
boolean isExhaust = true;
|
||||
boolean notEmpty = false;
|
||||
for (ClassDescriptor descriptor : objectDescriptors) {
|
||||
if (descriptor.getKind() == ClassKind.ENUM_ENTRY) {
|
||||
notEmpty = true;
|
||||
if (!containsEnumEntryCase(expression, descriptor, trace)) {
|
||||
isExhaust = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isExhaust && notEmpty;
|
||||
}
|
||||
|
||||
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) {
|
||||
JetExpressionPattern pattern = ((JetWhenConditionWithExpression) condition).getPattern();
|
||||
if (pattern == null) continue;
|
||||
JetExpression patternExpression = pattern.getExpression();
|
||||
JetType type = trace.get(BindingContext.EXPRESSION_TYPE, patternExpression);
|
||||
if (type == null) continue;
|
||||
if (type.getConstructor().getDeclarationDescriptor() == enumEntry) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,12 @@ public abstract class AbstractScopeAdapter implements JetScope {
|
||||
return getWorkerScope().getObjectDescriptor(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
return getWorkerScope().getObjectDescriptors();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<VariableDescriptor> getProperties(@NotNull String name) {
|
||||
|
||||
@@ -5,10 +5,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
@@ -41,6 +38,16 @@ public class ChainedScope implements JetScope {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
Set<ClassDescriptor> objectDescriptors = Sets.newHashSet();
|
||||
for (JetScope scope : scopeChain) {
|
||||
objectDescriptors.addAll(scope.getObjectDescriptors());
|
||||
}
|
||||
return objectDescriptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamespaceDescriptor getNamespace(@NotNull String name) {
|
||||
for (JetScope jetScope : scopeChain) {
|
||||
|
||||
@@ -32,6 +32,9 @@ public interface JetScope {
|
||||
@Nullable
|
||||
ClassDescriptor getObjectDescriptor(@NotNull String name);
|
||||
|
||||
@NotNull
|
||||
Set<ClassDescriptor> getObjectDescriptors();
|
||||
|
||||
@Nullable
|
||||
NamespaceDescriptor getNamespace(@NotNull String name);
|
||||
|
||||
|
||||
@@ -4,10 +4,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
@@ -23,6 +20,12 @@ public abstract class JetScopeImpl implements JetScope {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<VariableDescriptor> getProperties(@NotNull String name) {
|
||||
|
||||
@@ -81,6 +81,12 @@ public class SubstitutingScope implements JetScope {
|
||||
return substitute(workerScope.getObjectDescriptor(name));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
return substitute(workerScope.getObjectDescriptors());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<FunctionDescriptor> getFunctions(@NotNull String name) {
|
||||
|
||||
@@ -130,7 +130,7 @@ public class WritableScopeImpl extends WritableScopeWithImports {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, ClassDescriptor> getObjectDescriptors() {
|
||||
private Map<String, ClassDescriptor> getObjectDescriptorsMap() {
|
||||
if (objectDescriptors == null) {
|
||||
objectDescriptors = Maps.newHashMap();
|
||||
}
|
||||
@@ -299,7 +299,7 @@ public class WritableScopeImpl extends WritableScopeWithImports {
|
||||
public void addObjectDescriptor(@NotNull ClassDescriptor objectDescriptor) {
|
||||
checkMayWrite();
|
||||
|
||||
getObjectDescriptors().put(objectDescriptor.getName(), objectDescriptor);
|
||||
getObjectDescriptorsMap().put(objectDescriptor.getName(), objectDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -373,7 +373,13 @@ public class WritableScopeImpl extends WritableScopeWithImports {
|
||||
|
||||
@Override
|
||||
public ClassDescriptor getObjectDescriptor(@NotNull String name) {
|
||||
return getObjectDescriptors().get(name);
|
||||
return getObjectDescriptorsMap().get(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
return Sets.newHashSet(getObjectDescriptorsMap().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -137,6 +137,18 @@ public class WriteThroughScope extends WritableScopeWithImports {
|
||||
return super.getObjectDescriptor(name); // Imports
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
checkMayRead();
|
||||
Set<ClassDescriptor> objectDescriptors = Sets.newHashSet();
|
||||
|
||||
objectDescriptors.addAll(super.getObjectDescriptors());
|
||||
objectDescriptors.addAll(getWorkerScope().getObjectDescriptors());
|
||||
objectDescriptors.addAll(writableWorker.getObjectDescriptors());
|
||||
return objectDescriptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLabeledDeclaration(@NotNull DeclarationDescriptor descriptor) {
|
||||
checkMayWrite();
|
||||
|
||||
@@ -4,8 +4,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -33,6 +33,12 @@ public class ErrorUtils {
|
||||
return ERROR_CLASS;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ClassDescriptor> getObjectDescriptors() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<VariableDescriptor> getProperties(@NotNull String name) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//KT-1185 Support full enumeration check for 'when'
|
||||
|
||||
package kt1185
|
||||
|
||||
enum class Direction {
|
||||
NORTH
|
||||
SOUTH
|
||||
WEST
|
||||
EAST
|
||||
}
|
||||
|
||||
enum class Color(val rgb : Int) {
|
||||
RED : Color(0xFF0000)
|
||||
GREEN : Color(0x00FF00)
|
||||
BLUE : Color(0x0000FF)
|
||||
}
|
||||
|
||||
fun foo(d: Direction) = when(d) { //no 'else' should be requested
|
||||
Direction.NORTH -> 1
|
||||
Direction.SOUTH -> 2
|
||||
Direction.WEST -> 3
|
||||
Direction.EAST -> 4
|
||||
}
|
||||
|
||||
fun foo1(d: Direction) = <!NO_ELSE_IN_WHEN!>when<!>(d) {
|
||||
Direction.NORTH -> 1
|
||||
Direction.SOUTH -> 2
|
||||
Direction.WEST -> 3
|
||||
}
|
||||
|
||||
fun bar(c: Color) = when (c) {
|
||||
Color.RED -> 1
|
||||
Color.GREEN -> 2
|
||||
Color.BLUE -> 3
|
||||
}
|
||||
|
||||
fun bar1(c: Color) = <!NO_ELSE_IN_WHEN!>when<!> (c) {
|
||||
Color.RED -> 1
|
||||
Color.GREEN -> 2
|
||||
}
|
||||
|
||||
open enum class SomeEnum() {}
|
||||
|
||||
enum class MyEnum : SomeEnum() {
|
||||
A
|
||||
B
|
||||
}
|
||||
|
||||
fun g(me: SomeEnum) = <!NO_ELSE_IN_WHEN!>when<!> (me) {
|
||||
MyEnum.A -> 1
|
||||
MyEnum.B -> 2
|
||||
}
|
||||
|
||||
fun t2(me: MyEnum) = when (me) {
|
||||
MyEnum.A -> 1
|
||||
MyEnum.B -> 2
|
||||
}
|
||||
Reference in New Issue
Block a user