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.types.expressions;
018    
019    import com.google.common.collect.Sets;
020    import com.intellij.openapi.util.Ref;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024    import org.jetbrains.kotlin.cfg.WhenChecker;
025    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
026    import org.jetbrains.kotlin.diagnostics.Errors;
027    import org.jetbrains.kotlin.psi.*;
028    import org.jetbrains.kotlin.resolve.BindingContext;
029    import org.jetbrains.kotlin.resolve.DescriptorUtils;
030    import org.jetbrains.kotlin.resolve.PossiblyBareType;
031    import org.jetbrains.kotlin.resolve.TypeResolutionContext;
032    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
033    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
034    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
035    import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
036    import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
037    import org.jetbrains.kotlin.resolve.scopes.utils.UtilsPackage;
038    import org.jetbrains.kotlin.types.*;
039    import org.jetbrains.kotlin.types.checker.JetTypeChecker;
040    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
041    
042    import java.util.Collections;
043    import java.util.Set;
044    
045    import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean;
046    import static org.jetbrains.kotlin.diagnostics.Errors.*;
047    import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
048    import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
049    import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl;
050    
051    public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
052        protected PatternMatchingTypingVisitor(@NotNull ExpressionTypingInternals facade) {
053            super(facade);
054        }
055    
056        @Override
057        public JetTypeInfo visitIsExpression(@NotNull JetIsExpression expression, ExpressionTypingContext contextWithExpectedType) {
058            ExpressionTypingContext context = contextWithExpectedType
059                                                .replaceExpectedType(NO_EXPECTED_TYPE)
060                                                .replaceContextDependency(INDEPENDENT);
061            JetExpression leftHandSide = expression.getLeftHandSide();
062            JetTypeInfo typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope));
063            JetType knownType = typeInfo.getType();
064            if (expression.getTypeReference() != null) {
065                DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context);
066                DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo;
067                DataFlowInfo newDataFlowInfo = conditionInfo.and(typeInfo.getDataFlowInfo());
068                context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo);
069            }
070            return components.dataFlowAnalyzer.checkType(typeInfo.replaceType(components.builtIns.getBooleanType()), expression, contextWithExpectedType);
071        }
072    
073        @Override
074        public JetTypeInfo visitWhenExpression(@NotNull JetWhenExpression expression, ExpressionTypingContext context) {
075            return visitWhenExpression(expression, context, false);
076        }
077    
078        public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
079            components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType);
080    
081            ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
082            // TODO :change scope according to the bound value in the when header
083            JetExpression subjectExpression = expression.getSubjectExpression();
084    
085            JetType subjectType;
086            boolean loopBreakContinuePossible = false;
087            if (subjectExpression == null) {
088                subjectType = ErrorUtils.createErrorType("Unknown type");
089            }
090            else {
091                JetTypeInfo typeInfo = facade.safeGetTypeInfo(subjectExpression, context);
092                loopBreakContinuePossible = typeInfo.getJumpOutPossible();
093                subjectType = typeInfo.getType();
094                assert subjectType != null;
095                if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace)) {
096                    ExpressionTypingContext subjectContext = context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType));
097                    components.dataFlowAnalyzer.checkPossibleCast(subjectType, JetPsiUtil.safeDeparenthesize(subjectExpression, false), subjectContext);
098                }
099                context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
100            }
101            DataFlowValue subjectDataFlowValue = subjectExpression != null
102                    ? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context)
103                    : DataFlowValue.NULL;
104    
105            // TODO : exhaustive patterns
106    
107            Set<JetType> expressionTypes = Sets.newHashSet();
108            DataFlowInfo commonDataFlowInfo = null;
109            DataFlowInfo elseDataFlowInfo = context.dataFlowInfo;
110            for (JetWhenEntry whenEntry : expression.getEntries()) {
111                DataFlowInfos infosForCondition = getDataFlowInfosForEntryCondition(
112                        whenEntry, context.replaceDataFlowInfo(elseDataFlowInfo), subjectExpression, subjectType, subjectDataFlowValue);
113                elseDataFlowInfo = elseDataFlowInfo.and(infosForCondition.elseInfo);
114    
115                JetExpression bodyExpression = whenEntry.getExpression();
116                if (bodyExpression != null) {
117                    LexicalWritableScope scopeToExtend = newWritableScopeImpl(context, "Scope extended in when entry");
118                    ExpressionTypingContext newContext = contextWithExpectedType
119                            .replaceScope(scopeToExtend).replaceDataFlowInfo(infosForCondition.thenInfo).replaceContextDependency(INDEPENDENT);
120                    CoercionStrategy coercionStrategy = isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION;
121                    JetTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
122                            scopeToExtend, Collections.singletonList(bodyExpression), coercionStrategy, newContext);
123                    loopBreakContinuePossible |= typeInfo.getJumpOutPossible();
124                    JetType type = typeInfo.getType();
125                    if (type != null) {
126                        expressionTypes.add(type);
127                    }
128                    if (commonDataFlowInfo == null) {
129                        commonDataFlowInfo = typeInfo.getDataFlowInfo();
130                    }
131                    else {
132                        commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.getDataFlowInfo());
133                    }
134                }
135            }
136    
137            if (commonDataFlowInfo == null) {
138                commonDataFlowInfo = context.dataFlowInfo;
139            }
140            else if (expression.getElseExpression() == null && !WhenChecker.isWhenExhaustive(expression, context.trace)) {
141                // Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
142                // because data flow can bypass all when branches in this case
143                commonDataFlowInfo = commonDataFlowInfo.or(context.dataFlowInfo);
144            }
145    
146            return TypeInfoFactoryPackage.createTypeInfo(expressionTypes.isEmpty() ? null : components.dataFlowAnalyzer.checkType(
147                                                                 components.dataFlowAnalyzer.checkImplicitCast(
148                                                                         CommonSupertypes.commonSupertype(expressionTypes), expression,
149                                                                         contextWithExpectedType, isStatement),
150                                                                 expression, contextWithExpectedType),
151                                                         commonDataFlowInfo,
152                                                         loopBreakContinuePossible,
153                                                         contextWithExpectedType.dataFlowInfo);
154        }
155    
156        @NotNull
157        private DataFlowInfos getDataFlowInfosForEntryCondition(
158                @NotNull JetWhenEntry whenEntry,
159                @NotNull ExpressionTypingContext context,
160                @Nullable JetExpression subjectExpression,
161                @NotNull JetType subjectType,
162                @NotNull DataFlowValue subjectDataFlowValue
163        ) {
164            if (whenEntry.isElse()) {
165                return new DataFlowInfos(context.dataFlowInfo);
166            }
167    
168            DataFlowInfos infos = null;
169            ExpressionTypingContext contextForCondition = context;
170            for (JetWhenCondition condition : whenEntry.getConditions()) {
171                DataFlowInfos conditionInfos = checkWhenCondition(subjectExpression, subjectType, condition,
172                                                                  contextForCondition, subjectDataFlowValue);
173                if (infos != null) {
174                    infos = new DataFlowInfos(infos.thenInfo.or(conditionInfos.thenInfo), infos.elseInfo.and(conditionInfos.elseInfo));
175                }
176                else {
177                    infos = conditionInfos;
178                }
179                contextForCondition = contextForCondition.replaceDataFlowInfo(conditionInfos.elseInfo);
180            }
181            return infos != null ? infos : new DataFlowInfos(context.dataFlowInfo);
182        }
183    
184        private DataFlowInfos checkWhenCondition(
185                @Nullable final JetExpression subjectExpression,
186                final JetType subjectType,
187                JetWhenCondition condition,
188                final ExpressionTypingContext context,
189                final DataFlowValue subjectDataFlowValue
190        ) {
191            final Ref<DataFlowInfos> newDataFlowInfo = new Ref<DataFlowInfos>(noChange(context));
192            condition.accept(new JetVisitorVoid() {
193                @Override
194                public void visitWhenConditionInRange(@NotNull JetWhenConditionInRange condition) {
195                    JetExpression rangeExpression = condition.getRangeExpression();
196                    if (rangeExpression == null) return;
197                    if (subjectExpression == null) {
198                        context.trace.report(EXPECTED_CONDITION.on(condition));
199                        DataFlowInfo dataFlowInfo = facade.getTypeInfo(rangeExpression, context).getDataFlowInfo();
200                        newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
201                        return;
202                    }
203                    ValueArgument argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression);
204                    JetTypeInfo typeInfo = facade.checkInExpression(condition, condition.getOperationReference(),
205                                                                    argumentForSubject, rangeExpression, context);
206                    DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
207                    newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
208                    JetType type = typeInfo.getType();
209                    if (type == null || !isBoolean(type)) {
210                        context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition));
211                    }
212                }
213    
214                @Override
215                public void visitWhenConditionIsPattern(@NotNull JetWhenConditionIsPattern condition) {
216                    if (subjectExpression == null) {
217                        context.trace.report(EXPECTED_CONDITION.on(condition));
218                    }
219                    if (condition.getTypeReference() != null) {
220                        DataFlowInfos result = checkTypeForIs(context, subjectType, condition.getTypeReference(), subjectDataFlowValue);
221                        if (condition.isNegated()) {
222                            newDataFlowInfo.set(new DataFlowInfos(result.elseInfo, result.thenInfo));
223                        }
224                        else {
225                            newDataFlowInfo.set(result);
226                        }
227                    }
228                }
229    
230                @Override
231                public void visitWhenConditionWithExpression(@NotNull JetWhenConditionWithExpression condition) {
232                    JetExpression expression = condition.getExpression();
233                    if (expression != null) {
234                        newDataFlowInfo.set(checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
235                                                                            subjectDataFlowValue));
236                    }
237                }
238    
239                @Override
240                public void visitJetElement(@NotNull JetElement element) {
241                    context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
242                }
243            });
244            return newDataFlowInfo.get();
245        }
246    
247        private static class DataFlowInfos {
248            private final DataFlowInfo thenInfo;
249            private final DataFlowInfo elseInfo;
250    
251            private DataFlowInfos(DataFlowInfo thenInfo, DataFlowInfo elseInfo) {
252                this.thenInfo = thenInfo;
253                this.elseInfo = elseInfo;
254            }
255    
256            private DataFlowInfos(DataFlowInfo info) {
257                this(info, info);
258            }
259        }
260    
261        private DataFlowInfos checkTypeForExpressionCondition(
262                ExpressionTypingContext context,
263                JetExpression expression,
264                JetType subjectType,
265                boolean conditionExpected,
266                DataFlowValue subjectDataFlowValue
267        ) {
268            if (expression == null) {
269                return noChange(context);
270            }
271            JetTypeInfo typeInfo = facade.getTypeInfo(expression, context);
272            JetType type = typeInfo.getType();
273            if (type == null) {
274                return noChange(context);
275            }
276            context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
277            if (conditionExpected) {
278                JetType booleanType = components.builtIns.getBooleanType();
279                if (!JetTypeChecker.DEFAULT.equalTypes(booleanType, type)) {
280                    context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(expression, type));
281                }
282                else {
283                    DataFlowInfo ifInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, true, context);
284                    DataFlowInfo elseInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, false, context);
285                    return new DataFlowInfos(ifInfo, elseInfo);
286                }
287                return noChange(context);
288            }
289            checkTypeCompatibility(context, type, subjectType, expression);
290            DataFlowValue expressionDataFlowValue =
291                    DataFlowValueFactory.createDataFlowValue(expression, type, context);
292            DataFlowInfos result = noChange(context);
293            result = new DataFlowInfos(
294                    result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
295                    result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue)
296            );
297            return result;
298        }
299    
300        private DataFlowInfos checkTypeForIs(
301                ExpressionTypingContext context,
302                JetType subjectType,
303                JetTypeReference typeReferenceAfterIs,
304                DataFlowValue subjectDataFlowValue
305        ) {
306            if (typeReferenceAfterIs == null) {
307                return noChange(context);
308            }
309            TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true);
310            PossiblyBareType possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs);
311            JetType targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace, components.builtIns);
312    
313            if (TypesPackage.isDynamic(targetType)) {
314                context.trace.report(DYNAMIC_NOT_ALLOWED.on(typeReferenceAfterIs));
315            }
316            ClassDescriptor targetDescriptor = TypeUtils.getClassDescriptor(targetType);
317            if (targetDescriptor != null && DescriptorUtils.isEnumEntry(targetDescriptor)) {
318                context.trace.report(IS_ENUM_ENTRY.on(typeReferenceAfterIs));
319            }
320    
321            if (!subjectType.isMarkedNullable() && targetType.isMarkedNullable()) {
322                JetTypeElement element = typeReferenceAfterIs.getTypeElement();
323                assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
324                JetNullableType nullableType = (JetNullableType) element;
325                context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType));
326            }
327            checkTypeCompatibility(context, targetType, subjectType, typeReferenceAfterIs);
328            if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, JetTypeChecker.DEFAULT)) {
329                context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType));
330            }
331            return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo);
332        }
333    
334        private static DataFlowInfos noChange(ExpressionTypingContext context) {
335            return new DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo);
336        }
337    
338        /*
339         * (a: SubjectType) is Type
340         */
341        private void checkTypeCompatibility(
342                @NotNull ExpressionTypingContext context,
343                @Nullable JetType type,
344                @NotNull JetType subjectType,
345                @NotNull JetElement reportErrorOn
346        ) {
347            // TODO : Take smart casts into account?
348            if (type == null) {
349                return;
350            }
351            if (components.typeIntersector.isIntersectionEmpty(type, subjectType)) {
352                context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType));
353                return;
354            }
355    
356            // check if the pattern is essentially a 'null' expression
357            if (KotlinBuiltIns.isNullableNothing(type) && !TypeUtils.isNullableType(subjectType)) {
358                context.trace.report(SENSELESS_NULL_IN_WHEN.on(reportErrorOn));
359            }
360        }
361    }