001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.lang.cfg; 018 019import org.jetbrains.annotations.NotNull; 020import org.jetbrains.annotations.Nullable; 021import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 022import org.jetbrains.jet.lang.descriptors.ClassKind; 023import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 024import org.jetbrains.jet.lang.descriptors.VariableDescriptor; 025import org.jetbrains.jet.lang.psi.*; 026import org.jetbrains.jet.lang.resolve.BindingContext; 027import org.jetbrains.jet.lang.resolve.BindingTrace; 028import org.jetbrains.jet.lang.resolve.scopes.JetScope; 029import org.jetbrains.jet.lang.types.JetType; 030import org.jetbrains.jet.lang.types.TypeProjection; 031 032import java.util.Collection; 033import java.util.Collections; 034 035public final class WhenChecker { 036 private WhenChecker() { 037 } 038 039 public static boolean isWhenExhaustive(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) { 040 JetExpression subjectExpression = expression.getSubjectExpression(); 041 if (subjectExpression == null) return false; 042 JetType type = trace.get(BindingContext.EXPRESSION_TYPE, subjectExpression); 043 if (type == null) return false; 044 DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); 045 if (!(declarationDescriptor instanceof ClassDescriptor)) return false; 046 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; 047 if (classDescriptor.getKind() != ClassKind.ENUM_CLASS || classDescriptor.getModality().isOverridable()) return false; 048 ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor(); 049 assert classObjectDescriptor != null : "Enum classes must have class object."; 050 JetScope memberScope = classObjectDescriptor.getMemberScope(Collections.<TypeProjection>emptyList()); 051 Collection<ClassDescriptor> objectDescriptors = memberScope.getObjectDescriptors(); 052 boolean isExhaust = true; 053 boolean notEmpty = false; 054 for (ClassDescriptor descriptor : objectDescriptors) { 055 if (descriptor.getKind() == ClassKind.ENUM_ENTRY) { 056 notEmpty = true; 057 if (!containsEnumEntryCase(expression, descriptor, trace)) { 058 isExhaust = false; 059 } 060 } 061 } 062 return isExhaust && notEmpty; 063 } 064 065 private static boolean containsEnumEntryCase( 066 @NotNull JetWhenExpression whenExpression, 067 @NotNull ClassDescriptor enumEntry, 068 @NotNull BindingTrace trace 069 ) { 070 assert enumEntry.getKind() == ClassKind.ENUM_ENTRY; 071 for (JetWhenEntry whenEntry : whenExpression.getEntries()) { 072 for (JetWhenCondition condition : whenEntry.getConditions()) { 073 if (!(condition instanceof JetWhenConditionWithExpression)) { 074 continue; 075 } 076 if (isCheckForEnumEntry((JetWhenConditionWithExpression) condition, enumEntry, trace)) { 077 return true; 078 } 079 } 080 } 081 return false; 082 } 083 084 private static boolean isCheckForEnumEntry( 085 @NotNull JetWhenConditionWithExpression whenExpression, 086 @NotNull ClassDescriptor enumEntry, 087 @NotNull BindingTrace trace 088 ) { 089 JetSimpleNameExpression reference = getReference(whenExpression.getExpression()); 090 if (reference == null) return false; 091 092 DeclarationDescriptor target = trace.get(BindingContext.REFERENCE_TARGET, reference); 093 if (!(target instanceof VariableDescriptor)) { 094 return false; 095 } 096 097 ClassDescriptor classDescriptor = trace.get(BindingContext.OBJECT_DECLARATION_CLASS, (VariableDescriptor) target); 098 return classDescriptor == enumEntry; 099 } 100 101 @Nullable 102 private static JetSimpleNameExpression getReference(@Nullable JetExpression expression) { 103 if (expression == null) { 104 return null; 105 } 106 if (expression instanceof JetSimpleNameExpression) { 107 return (JetSimpleNameExpression) expression; 108 } 109 if (expression instanceof JetQualifiedExpression) { 110 return getReference(((JetQualifiedExpression) expression).getSelectorExpression()); 111 } 112 return null; 113 } 114}