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