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 com.intellij.psi.tree.IElementType;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt;
027    import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
028    import org.jetbrains.kotlin.lexer.KtTokens;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.resolve.BindingContext;
031    import org.jetbrains.kotlin.resolve.BindingTrace;
032    import org.jetbrains.kotlin.resolve.DescriptorUtils;
033    import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker;
034    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
035    import org.jetbrains.kotlin.resolve.calls.smartcasts.*;
036    import org.jetbrains.kotlin.resolve.constants.*;
037    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
038    import org.jetbrains.kotlin.types.KotlinType;
039    import org.jetbrains.kotlin.types.TypeConstructor;
040    import org.jetbrains.kotlin.types.TypeUtils;
041    import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
042    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
043    import org.jetbrains.kotlin.util.OperatorNameConventions;
044    
045    import java.util.Collection;
046    
047    import static org.jetbrains.kotlin.diagnostics.Errors.*;
048    import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
049    import static org.jetbrains.kotlin.types.TypeUtils.*;
050    
051    public class DataFlowAnalyzer {
052    
053        private final Iterable<AdditionalTypeChecker> additionalTypeCheckers;
054        private final ConstantExpressionEvaluator constantExpressionEvaluator;
055        private final KotlinBuiltIns builtIns;
056        private final SmartCastManager smartCastManager;
057        private final ExpressionTypingFacade facade;
058    
059        public DataFlowAnalyzer(
060                @NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
061                @NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
062                @NotNull KotlinBuiltIns builtIns,
063                @NotNull SmartCastManager smartCastManager,
064                @NotNull ExpressionTypingFacade facade
065        ) {
066            this.additionalTypeCheckers = additionalTypeCheckers;
067            this.constantExpressionEvaluator = constantExpressionEvaluator;
068            this.builtIns = builtIns;
069            this.smartCastManager = smartCastManager;
070            this.facade = facade;
071        }
072    
073        // NB: use this method only for functions from 'Any'
074        @Nullable
075        private static FunctionDescriptor getOverriddenDescriptorFromClass(@NotNull FunctionDescriptor descriptor) {
076            if (descriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) return descriptor;
077            Collection<? extends FunctionDescriptor> overriddenDescriptors = descriptor.getOverriddenDescriptors();
078            if (overriddenDescriptors.isEmpty()) return descriptor;
079            for (FunctionDescriptor overridden : overriddenDescriptors) {
080                DeclarationDescriptor containingDeclaration = overridden.getContainingDeclaration();
081                if (DescriptorUtils.isClass(containingDeclaration) || DescriptorUtils.isObject(containingDeclaration)) {
082                    // Exactly one class should exist in the list
083                    return getOverriddenDescriptorFromClass(overridden);
084                }
085            }
086            return null;
087        }
088    
089        private static boolean typeHasOverriddenEquals(@NotNull KotlinType type, @NotNull KtElement lookupElement) {
090            Collection<SimpleFunctionDescriptor> members = type.getMemberScope().getContributedFunctions(
091                    OperatorNameConventions.EQUALS, new KotlinLookupLocation(lookupElement));
092            for (FunctionDescriptor member : members) {
093                KotlinType returnType = member.getReturnType();
094                if (returnType == null || !KotlinBuiltIns.isBoolean(returnType)) continue;
095                if (member.getValueParameters().size() != 1) continue;
096                KotlinType parameterType = member.getValueParameters().iterator().next().getType();
097                if (!KotlinBuiltIns.isNullableAny(parameterType)) continue;
098                FunctionDescriptor fromSuperClass = getOverriddenDescriptorFromClass(member);
099                if (fromSuperClass == null) return false;
100                ClassifierDescriptor superClassDescriptor = (ClassifierDescriptor) fromSuperClass.getContainingDeclaration();
101                // We should have override fun in class other than Any (to prove unknown behaviour)
102                return !KotlinBuiltIns.isAnyOrNullableAny(superClassDescriptor.getDefaultType());
103            }
104            return false;
105        }
106    
107        // Returns true if we can prove that 'type' has equals method from 'Any' base type
108        public static boolean typeHasEqualsFromAny(@NotNull KotlinType type, @NotNull KtElement lookupElement) {
109            TypeConstructor constructor = type.getConstructor();
110            // Subtypes can override equals for non-final types
111            if (!constructor.isFinal()) return false;
112            // check whether 'equals' is overriden
113            return !typeHasOverriddenEquals(type, lookupElement);
114        }
115    
116        @NotNull
117        public DataFlowInfo extractDataFlowInfoFromCondition(
118                @Nullable final KtExpression condition,
119                final boolean conditionValue,
120                final ExpressionTypingContext context
121        ) {
122            if (condition == null) return context.dataFlowInfo;
123            final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
124            condition.accept(new KtVisitorVoid() {
125                @Override
126                public void visitIsExpression(@NotNull KtIsExpression expression) {
127                    if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
128                        result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
129                    }
130                }
131    
132                @Override
133                public void visitBinaryExpression(@NotNull KtBinaryExpression expression) {
134                    IElementType operationToken = expression.getOperationToken();
135                    if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
136                        DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
137                        KtExpression expressionRight = expression.getRight();
138                        if (expressionRight != null) {
139                            boolean and = operationToken == KtTokens.ANDAND;
140                            DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(
141                                    expressionRight, conditionValue,
142                                    and == conditionValue ? context.replaceDataFlowInfo(dataFlowInfo) : context
143                            );
144                            if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
145                                dataFlowInfo = dataFlowInfo.and(rightInfo);
146                            }
147                            else {
148                                dataFlowInfo = dataFlowInfo.or(rightInfo);
149                            }
150                        }
151                        result.set(dataFlowInfo);
152                    }
153                    else  {
154                        DataFlowInfo expressionFlowInfo = facade.getTypeInfo(expression, context).getDataFlowInfo();
155                        KtExpression left = expression.getLeft();
156                        if (left == null) return;
157                        KtExpression right = expression.getRight();
158                        if (right == null) return;
159    
160                        KotlinType lhsType = context.trace.getBindingContext().getType(left);
161                        if (lhsType == null) return;
162                        KotlinType rhsType = context.trace.getBindingContext().getType(right);
163                        if (rhsType == null) return;
164    
165                        DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, context);
166                        DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, context);
167    
168                        Boolean equals = null;
169                        if (operationToken == KtTokens.EQEQ || operationToken == KtTokens.EQEQEQ) {
170                            equals = true;
171                        }
172                        else if (operationToken == KtTokens.EXCLEQ || operationToken == KtTokens.EXCLEQEQEQ) {
173                            equals = false;
174                        }
175                        if (equals != null) {
176                            if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
177                                boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ ||
178                                                     typeHasEqualsFromAny(lhsType, condition);
179                                result.set(context.dataFlowInfo.equate(leftValue, rightValue, byIdentity).and(expressionFlowInfo));
180                            }
181                            else {
182                                result.set(context.dataFlowInfo.disequate(leftValue, rightValue).and(expressionFlowInfo));
183                            }
184                        }
185                        else {
186                            result.set(expressionFlowInfo);
187                        }
188                    }
189                }
190    
191                @Override
192                public void visitUnaryExpression(@NotNull KtUnaryExpression expression) {
193                    IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
194                    if (operationTokenType == KtTokens.EXCL) {
195                        KtExpression baseExpression = expression.getBaseExpression();
196                        if (baseExpression != null) {
197                            result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
198                        }
199                    }
200                    else {
201                        visitExpression(expression);
202                    }
203                }
204    
205                @Override
206                public void visitExpression(@NotNull KtExpression expression) {
207                    // In fact, everything is taken from trace here
208                    result.set(facade.getTypeInfo(expression, context).getDataFlowInfo());
209                }
210    
211                @Override
212                public void visitParenthesizedExpression(@NotNull KtParenthesizedExpression expression) {
213                    KtExpression body = expression.getExpression();
214                    if (body != null) {
215                        body.accept(this);
216                    }
217                }
218            });
219            if (result.get() == null) {
220                return context.dataFlowInfo;
221            }
222            return context.dataFlowInfo.and(result.get());
223        }
224    
225        @Nullable
226        public KotlinType checkType(@Nullable KotlinType expressionType, @NotNull KtExpression expression, @NotNull ResolutionContext context) {
227            return checkType(expressionType, expression, context, null);
228        }
229    
230        @NotNull
231        public KotlinTypeInfo checkType(@NotNull KotlinTypeInfo typeInfo, @NotNull KtExpression expression, @NotNull ResolutionContext context) {
232            return typeInfo.replaceType(checkType(typeInfo.getType(), expression, context));
233        }
234    
235        @NotNull
236        private KotlinType checkTypeInternal(
237                @NotNull KotlinType expressionType,
238                @NotNull KtExpression expression,
239                @NotNull ResolutionContext c,
240                @NotNull Ref<Boolean> hasError
241        ) {
242            if (noExpectedType(c.expectedType) || !c.expectedType.getConstructor().isDenotable() ||
243                KotlinTypeChecker.DEFAULT.isSubtypeOf(expressionType, c.expectedType)) {
244                return expressionType;
245            }
246    
247            if (expression instanceof KtConstantExpression) {
248                ConstantValue<?> constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, c.trace, c.expectedType);
249                boolean error = new CompileTimeConstantChecker(c, builtIns, true)
250                        .checkConstantExpressionType(constantValue, (KtConstantExpression) expression, c.expectedType);
251                hasError.set(error);
252                return expressionType;
253            }
254    
255            if (expression instanceof KtWhenExpression) {
256                // No need in additional check because type mismatch is already reported for entries
257                return expressionType;
258            }
259    
260            SmartCastResult castResult = checkPossibleCast(expressionType, expression, c);
261            if (castResult != null) return castResult.getResultType();
262    
263            if (!DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection(c, expression, c.expectedType, expressionType)) {
264                c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType));
265            }
266            hasError.set(true);
267            return expressionType;
268        }
269    
270        @Nullable
271        public KotlinType checkType(
272                @Nullable KotlinType expressionType,
273                @NotNull KtExpression expressionToCheck,
274                @NotNull ResolutionContext c,
275                @Nullable Ref<Boolean> hasError
276        ) {
277            if (hasError == null) {
278                hasError = Ref.create(false);
279            }
280            else {
281                hasError.set(false);
282            }
283    
284            KtExpression expression = KtPsiUtil.safeDeparenthesize(expressionToCheck);
285            recordExpectedType(c.trace, expression, c.expectedType);
286    
287            if (expressionType == null) return null;
288    
289            KotlinType result = checkTypeInternal(expressionType, expression, c, hasError);
290            if (Boolean.FALSE.equals(hasError.get())) {
291                for (AdditionalTypeChecker checker : additionalTypeCheckers) {
292                    checker.checkType(expression, expressionType, result, c);
293                }
294            }
295    
296            return result;
297        }
298    
299        @Nullable
300        public static SmartCastResult checkPossibleCast(
301                @NotNull KotlinType expressionType,
302                @NotNull KtExpression expression,
303                @NotNull ResolutionContext c
304        ) {
305            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
306    
307            return SmartCastManager.Companion.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false);
308        }
309    
310        public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) {
311            if (expectedType != NO_EXPECTED_TYPE) {
312                KotlinType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? builtIns.getUnitType() : expectedType;
313                trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
314            }
315        }
316    
317        @Nullable
318        public KotlinType checkStatementType(@NotNull KtExpression expression, @NotNull ResolutionContext context) {
319            if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.isUnit(context.expectedType) && !context.expectedType.isError()) {
320                context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
321                return null;
322            }
323            return builtIns.getUnitType();
324        }
325    
326        @NotNull
327        public static KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
328            facade.checkStatementType(
329                    expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
330            context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
331            return TypeInfoFactoryKt.noTypeInfo(context);
332        }
333    
334        @NotNull
335        public static Collection<KotlinType> getAllPossibleTypes(
336                @NotNull KtExpression expression,
337                @NotNull DataFlowInfo dataFlowInfo,
338                @NotNull KotlinType type,
339                @NotNull ResolutionContext c
340        ) {
341            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
342            Collection<KotlinType> possibleTypes = Sets.newHashSet(type);
343            possibleTypes.addAll(dataFlowInfo.getPredictableTypes(dataFlowValue));
344            return possibleTypes;
345        }
346    
347        @NotNull
348        public KotlinTypeInfo createCheckedTypeInfo(
349                @Nullable KotlinType type,
350                @NotNull ResolutionContext<?> context,
351                @NotNull KtExpression expression
352        ) {
353            return checkType(TypeInfoFactoryKt.createTypeInfo(type, context), expression, context);
354        }
355    
356        @NotNull
357        public KotlinTypeInfo createCompileTimeConstantTypeInfo(
358                @NotNull CompileTimeConstant<?> value,
359                @NotNull KtExpression expression,
360                @NotNull ExpressionTypingContext context
361        ) {
362            KotlinType expressionType;
363            if (value instanceof IntegerValueTypeConstant) {
364                IntegerValueTypeConstant integerValueTypeConstant = (IntegerValueTypeConstant) value;
365                if (context.contextDependency == INDEPENDENT) {
366                    expressionType = integerValueTypeConstant.getType(context.expectedType);
367                    constantExpressionEvaluator.updateNumberType(expressionType, expression, context.statementFilter, context.trace);
368                }
369                else {
370                    expressionType = integerValueTypeConstant.getUnknownIntegerType();
371                }
372            }
373            else {
374                expressionType = ((TypedCompileTimeConstant<?>) value).getType();
375            }
376    
377            return createCheckedTypeInfo(expressionType, context, expression);
378        }
379    }