KT-1185 Support full enumeration check for 'when'

This commit is contained in:
svtk
2012-01-30 13:39:44 +04:00
parent 59aa2f8c71
commit 1d71e820bb
12 changed files with 193 additions and 19 deletions
@@ -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
}