001    /*
002     * Copyright 2010-2015 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    
017    package org.jetbrains.kotlin.cfg;
018    
019    import com.intellij.psi.PsiElement;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
024    import org.jetbrains.kotlin.descriptors.ClassKind;
025    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026    import org.jetbrains.kotlin.descriptors.Modality;
027    import org.jetbrains.kotlin.diagnostics.Errors;
028    import org.jetbrains.kotlin.lexer.KtToken;
029    import org.jetbrains.kotlin.lexer.KtTokens;
030    import org.jetbrains.kotlin.psi.*;
031    import org.jetbrains.kotlin.resolve.BindingContext;
032    import org.jetbrains.kotlin.resolve.BindingTrace;
033    import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils;
034    import org.jetbrains.kotlin.resolve.DescriptorUtils;
035    import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
036    import org.jetbrains.kotlin.types.FlexibleTypesKt;
037    import org.jetbrains.kotlin.types.KotlinType;
038    import org.jetbrains.kotlin.types.TypeUtils;
039    
040    import java.util.HashSet;
041    import java.util.Set;
042    
043    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass;
044    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
045    
046    public final class WhenChecker {
047        private WhenChecker() {
048        }
049    
050        public static boolean mustHaveElse(@NotNull KtWhenExpression expression, @NotNull BindingTrace trace) {
051            KotlinType expectedType = trace.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expression);
052            boolean isUnit = expectedType != null && KotlinBuiltIns.isUnit(expectedType);
053            // Some "statements" are actually expressions returned from lambdas, their expected types are non-null
054            boolean isStatement = BindingContextUtilsKt.isUsedAsStatement(expression, trace.getBindingContext()) && expectedType == null;
055    
056            return !isUnit && !isStatement && !isWhenExhaustive(expression, trace);
057        }
058    
059        public static boolean isWhenByEnum(@NotNull KtWhenExpression expression, @NotNull BindingContext context) {
060            return getClassDescriptorOfTypeIfEnum(whenSubjectType(expression, context)) != null;
061        }
062    
063        @Nullable
064        public static ClassDescriptor getClassDescriptorOfTypeIfEnum(@Nullable KotlinType type) {
065            if (type == null) return null;
066            ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
067            if (classDescriptor == null) return null;
068            if (classDescriptor.getKind() != ClassKind.ENUM_CLASS || classDescriptor.getModality().isOverridable()) return null;
069    
070            return classDescriptor;
071        }
072    
073        @Nullable
074        private static KotlinType whenSubjectType(@NotNull KtWhenExpression expression, @NotNull BindingContext context) {
075            KtExpression subjectExpression = expression.getSubjectExpression();
076            return subjectExpression == null ? null : context.getType(subjectExpression);
077        }
078    
079        private static boolean isWhenOnBooleanExhaustive(@NotNull KtWhenExpression expression, @NotNull BindingTrace trace) {
080            // It's assumed (and not checked) that expression is of the boolean type
081            boolean containsFalse = false;
082            boolean containsTrue = false;
083            for (KtWhenEntry whenEntry: expression.getEntries()) {
084                for (KtWhenCondition whenCondition : whenEntry.getConditions()) {
085                    if (whenCondition instanceof KtWhenConditionWithExpression) {
086                        KtExpression whenExpression = ((KtWhenConditionWithExpression) whenCondition).getExpression();
087                        if (CompileTimeConstantUtils.canBeReducedToBooleanConstant(whenExpression, trace, true)) containsTrue = true;
088                        if (CompileTimeConstantUtils.canBeReducedToBooleanConstant(whenExpression, trace, false)) containsFalse = true;
089                    }
090                }
091            }
092            return containsFalse && containsTrue;
093        }
094    
095        public static boolean isWhenOnEnumExhaustive(
096                @NotNull KtWhenExpression expression,
097                @NotNull BindingTrace trace,
098                @NotNull ClassDescriptor enumClassDescriptor
099        ) {
100            assert isEnumClass(enumClassDescriptor) :
101                    "isWhenOnEnumExhaustive should be called with an enum class descriptor";
102            Set<ClassDescriptor> entryDescriptors = new HashSet<ClassDescriptor>();
103            for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(enumClassDescriptor.getUnsubstitutedInnerClassesScope())) {
104                if (isEnumEntry(descriptor)) {
105                    entryDescriptors.add((ClassDescriptor) descriptor);
106                }
107            }
108            return !entryDescriptors.isEmpty() && containsAllClassCases(expression, entryDescriptors, trace);
109        }
110    
111        private static void collectNestedSubclasses(
112                @NotNull ClassDescriptor baseDescriptor,
113                @NotNull ClassDescriptor currentDescriptor,
114                @NotNull Set<ClassDescriptor> subclasses
115        ) {
116            for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(currentDescriptor.getUnsubstitutedInnerClassesScope())) {
117                if (descriptor instanceof ClassDescriptor) {
118                    ClassDescriptor memberClassDescriptor = (ClassDescriptor) descriptor;
119                    if (DescriptorUtils.isDirectSubclass(memberClassDescriptor, baseDescriptor)) {
120                        subclasses.add(memberClassDescriptor);
121                    }
122                    collectNestedSubclasses(baseDescriptor, memberClassDescriptor, subclasses);
123                }
124            }
125        }
126    
127        private static boolean isWhenOnSealedClassExhaustive(
128                @NotNull KtWhenExpression expression,
129                @NotNull BindingTrace trace,
130                @NotNull ClassDescriptor classDescriptor
131        ) {
132            assert classDescriptor.getModality() == Modality.SEALED :
133                    "isWhenOnSealedClassExhaustive should be called with a sealed class descriptor";
134            Set<ClassDescriptor> memberClassDescriptors = new HashSet<ClassDescriptor>();
135            collectNestedSubclasses(classDescriptor, classDescriptor, memberClassDescriptors);
136            // When on a sealed class without derived members is considered non-exhaustive (see test WhenOnEmptySealed)
137            return !memberClassDescriptors.isEmpty() && containsAllClassCases(expression, memberClassDescriptors, trace);
138        }
139    
140        /**
141         * It's assumed that function is called for a final type. In this case the only possible smart cast is to not nullable type.
142         * @return true if type is nullable, and cannot be smart casted
143         */
144        private static boolean isNullableTypeWithoutPossibleSmartCast(
145                @Nullable KtExpression expression,
146                @NotNull KotlinType type,
147                @NotNull BindingContext context
148        ) {
149            if (expression == null) return false; // Normally should not happen
150            if (!TypeUtils.isNullableType(type)) return false;
151            // We cannot read data flow information here due to lack of inputs (module descriptor is necessary)
152            if (context.get(BindingContext.SMARTCAST, expression) != null) {
153                // We have smart cast from enum or boolean to something
154                // Not very nice but we *can* decide it was smart cast to not-null
155                // because both enum and boolean are final
156                return false;
157            }
158            return true;
159        }
160    
161        public static boolean isWhenExhaustive(@NotNull KtWhenExpression expression, @NotNull BindingTrace trace) {
162            KotlinType type = whenSubjectType(expression, trace.getBindingContext());
163            if (type == null) return false;
164            ClassDescriptor enumClassDescriptor = getClassDescriptorOfTypeIfEnum(type);
165    
166            boolean exhaustive;
167            if (enumClassDescriptor == null) {
168                if (KotlinBuiltIns.isBoolean(TypeUtils.makeNotNullable(type))) {
169                    exhaustive = isWhenOnBooleanExhaustive(expression, trace);
170                }
171                else {
172                    ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
173                    exhaustive = (classDescriptor != null
174                                  && classDescriptor.getModality() == Modality.SEALED
175                                  && isWhenOnSealedClassExhaustive(expression, trace, classDescriptor));
176                }
177            }
178            else {
179                exhaustive = isWhenOnEnumExhaustive(expression, trace, enumClassDescriptor);
180            }
181            if (exhaustive) {
182                if (// Flexible (nullable) enum types are also counted as exhaustive
183                    (enumClassDescriptor != null && FlexibleTypesKt.isFlexible(type))
184                    || containsNullCase(expression, trace)
185                    || !isNullableTypeWithoutPossibleSmartCast(expression.getSubjectExpression(), type, trace.getBindingContext())) {
186    
187                    trace.record(BindingContext.EXHAUSTIVE_WHEN, expression);
188                    return true;
189                }
190            }
191            return false;
192        }
193    
194        private static boolean containsAllClassCases(
195                @NotNull KtWhenExpression whenExpression,
196                @NotNull Set<ClassDescriptor> memberDescriptors,
197                @NotNull BindingTrace trace
198        ) {
199            Set<ClassDescriptor> checkedDescriptors = new HashSet<ClassDescriptor>();
200            for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
201                for (KtWhenCondition condition : whenEntry.getConditions()) {
202                    boolean negated = false;
203                    ClassDescriptor checkedDescriptor = null;
204                    if (condition instanceof KtWhenConditionIsPattern) {
205                        KtWhenConditionIsPattern conditionIsPattern = (KtWhenConditionIsPattern) condition;
206                        KotlinType checkedType = trace.get(BindingContext.TYPE, conditionIsPattern.getTypeReference());
207                        if (checkedType != null) {
208                            checkedDescriptor = TypeUtils.getClassDescriptor(checkedType);
209                        }
210                        negated = conditionIsPattern.isNegated();
211                    }
212                    else if (condition instanceof KtWhenConditionWithExpression) {
213                        KtWhenConditionWithExpression conditionWithExpression = (KtWhenConditionWithExpression) condition;
214                        if (conditionWithExpression.getExpression() != null) {
215                            KtSimpleNameExpression reference = getReference(conditionWithExpression.getExpression());
216                            if (reference != null) {
217                                DeclarationDescriptor target = trace.get(BindingContext.REFERENCE_TARGET, reference);
218                                if (target instanceof ClassDescriptor) {
219                                    checkedDescriptor = (ClassDescriptor) target;
220                                }
221                            }
222                        }
223                    }
224    
225                    // Checks are important only for nested subclasses of the sealed class
226                    // In additional, check without "is" is important only for objects
227                    if (checkedDescriptor == null
228                        || !memberDescriptors.contains(checkedDescriptor)
229                        || (condition instanceof KtWhenConditionWithExpression
230                            && !DescriptorUtils.isObject(checkedDescriptor)
231                            && !DescriptorUtils.isEnumEntry(checkedDescriptor))) {
232                        continue;
233                    }
234                    if (negated) {
235                        if (checkedDescriptors.contains(checkedDescriptor)) return true; // all members are already there
236                        checkedDescriptors.addAll(memberDescriptors);
237                        checkedDescriptors.remove(checkedDescriptor);
238                    }
239                    else {
240                        checkedDescriptors.add(checkedDescriptor);
241                    }
242                }
243            }
244            return checkedDescriptors.containsAll(memberDescriptors);
245        }
246    
247        public static boolean containsNullCase(@NotNull KtWhenExpression expression, @NotNull BindingTrace trace) {
248            for (KtWhenEntry entry : expression.getEntries()) {
249                for (KtWhenCondition condition : entry.getConditions()) {
250                    if (condition instanceof KtWhenConditionWithExpression) {
251                        KtWhenConditionWithExpression conditionWithExpression = (KtWhenConditionWithExpression) condition;
252                        if (conditionWithExpression.getExpression() != null) {
253                            KotlinType type = trace.getBindingContext().getType(conditionWithExpression.getExpression());
254                            if (type != null && KotlinBuiltIns.isNothingOrNullableNothing(type)) {
255                                return true;
256                            }
257                        }
258                    }
259                }
260            }
261            return false;
262        }
263    
264        @Nullable
265        private static KtSimpleNameExpression getReference(@Nullable KtExpression expression) {
266            if (expression == null) {
267                return null;
268            }
269            if (expression instanceof KtSimpleNameExpression) {
270                return (KtSimpleNameExpression) expression;
271            }
272            if (expression instanceof KtQualifiedExpression) {
273                return getReference(((KtQualifiedExpression) expression).getSelectorExpression());
274            }
275            return null;
276        }
277    
278        public static void checkDeprecatedWhenSyntax(@NotNull BindingTrace trace, @NotNull KtWhenExpression expression) {
279            if (expression.getSubjectExpression() != null) return;
280    
281            for (KtWhenEntry entry : expression.getEntries()) {
282                if (entry.isElse()) continue;
283                for (PsiElement child = entry.getFirstChild(); child != null; child = child.getNextSibling()) {
284                    if (child.getNode().getElementType() == KtTokens.COMMA) {
285                        trace.report(Errors.COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT.on(child));
286                    }
287                    if (child.getNode().getElementType() == KtTokens.ARROW) break;
288                }
289            }
290        }
291    
292    }