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.lexer.JetTokens;
026    import org.jetbrains.kotlin.psi.*;
027    import org.jetbrains.kotlin.resolve.BindingContext;
028    import org.jetbrains.kotlin.resolve.BindingTrace;
029    import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker;
030    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
031    import org.jetbrains.kotlin.resolve.calls.smartcasts.*;
032    import org.jetbrains.kotlin.resolve.constants.*;
033    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
034    import org.jetbrains.kotlin.types.JetType;
035    import org.jetbrains.kotlin.types.TypeUtils;
036    import org.jetbrains.kotlin.types.TypesPackage;
037    import org.jetbrains.kotlin.types.checker.JetTypeChecker;
038    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
039    
040    import java.util.Collection;
041    
042    import static org.jetbrains.kotlin.diagnostics.Errors.*;
043    import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
044    import static org.jetbrains.kotlin.types.TypeUtils.*;
045    
046    public class DataFlowAnalyzer {
047    
048        private final Iterable<AdditionalTypeChecker> additionalTypeCheckers;
049        private final ConstantExpressionEvaluator constantExpressionEvaluator;
050        private final KotlinBuiltIns builtIns;
051        private final SmartCastManager smartCastManager;
052    
053        public DataFlowAnalyzer(
054                @NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
055                @NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
056                @NotNull KotlinBuiltIns builtIns,
057                @NotNull SmartCastManager smartCastManager
058        ) {
059            this.additionalTypeCheckers = additionalTypeCheckers;
060            this.constantExpressionEvaluator = constantExpressionEvaluator;
061            this.builtIns = builtIns;
062            this.smartCastManager = smartCastManager;
063        }
064    
065        @NotNull
066        public DataFlowInfo extractDataFlowInfoFromCondition(
067                @Nullable JetExpression condition,
068                final boolean conditionValue,
069                final ExpressionTypingContext context
070        ) {
071            if (condition == null) return context.dataFlowInfo;
072            final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
073            condition.accept(new JetVisitorVoid() {
074                @Override
075                public void visitIsExpression(@NotNull JetIsExpression expression) {
076                    if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
077                        result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
078                    }
079                }
080    
081                @Override
082                public void visitBinaryExpression(@NotNull JetBinaryExpression expression) {
083                    IElementType operationToken = expression.getOperationToken();
084                    if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
085                        DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
086                        JetExpression expressionRight = expression.getRight();
087                        if (expressionRight != null) {
088                            DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context);
089                            boolean and = operationToken == JetTokens.ANDAND;
090                            if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
091                                dataFlowInfo = dataFlowInfo.and(rightInfo);
092                            }
093                            else {
094                                dataFlowInfo = dataFlowInfo.or(rightInfo);
095                            }
096                        }
097                        result.set(dataFlowInfo);
098                    }
099                    else  {
100                        JetExpression left = expression.getLeft();
101                        if (left == null) return;
102                        JetExpression right = expression.getRight();
103                        if (right == null) return;
104    
105                        JetType lhsType = context.trace.getBindingContext().getType(left);
106                        if (lhsType == null) return;
107                        JetType rhsType = context.trace.getBindingContext().getType(right);
108                        if (rhsType == null) return;
109    
110                        DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, context);
111                        DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, context);
112    
113                        Boolean equals = null;
114                        if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
115                            equals = true;
116                        }
117                        else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
118                            equals = false;
119                        }
120                        if (equals != null) {
121                            if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
122                                result.set(context.dataFlowInfo.equate(leftValue, rightValue));
123                            }
124                            else {
125                                result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
126                            }
127    
128                        }
129                    }
130                }
131    
132                @Override
133                public void visitUnaryExpression(@NotNull JetUnaryExpression expression) {
134                    IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
135                    if (operationTokenType == JetTokens.EXCL) {
136                        JetExpression baseExpression = expression.getBaseExpression();
137                        if (baseExpression != null) {
138                            result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
139                        }
140                    }
141                }
142    
143                @Override
144                public void visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression) {
145                    JetExpression body = expression.getExpression();
146                    if (body != null) {
147                        body.accept(this);
148                    }
149                }
150            });
151            if (result.get() == null) {
152                return context.dataFlowInfo;
153            }
154            return context.dataFlowInfo.and(result.get());
155        }
156    
157        @Nullable
158        public JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
159            return checkType(expressionType, expression, context, null);
160        }
161    
162        @NotNull
163        public JetTypeInfo checkType(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
164            return typeInfo.replaceType(checkType(typeInfo.getType(), expression, context));
165        }
166    
167        @NotNull
168        private JetType checkTypeInternal(
169                @NotNull JetType expressionType,
170                @NotNull JetExpression expression,
171                @NotNull ResolutionContext c,
172                @NotNull Ref<Boolean> hasError
173        ) {
174            if (noExpectedType(c.expectedType) || !c.expectedType.getConstructor().isDenotable() ||
175                JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, c.expectedType)) {
176                return expressionType;
177            }
178    
179            if (expression instanceof JetConstantExpression) {
180                ConstantValue<?> constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, c.trace, c.expectedType);
181                boolean error = new CompileTimeConstantChecker(c.trace, true)
182                        .checkConstantExpressionType(constantValue, (JetConstantExpression) expression, c.expectedType);
183                if (hasError != null) hasError.set(error);
184                return expressionType;
185            }
186    
187            if (expression instanceof JetWhenExpression) {
188                // No need in additional check because type mismatch is already reported for entries
189                return expressionType;
190            }
191    
192            JetType possibleType = checkPossibleCast(expressionType, expression, c);
193            if (possibleType != null) return possibleType;
194    
195            c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType));
196            if (hasError != null) hasError.set(true);
197            return expressionType;
198        }
199    
200        @Nullable
201        public JetType checkType(
202                @Nullable JetType expressionType,
203                @NotNull JetExpression expressionToCheck,
204                @NotNull ResolutionContext c,
205                @Nullable Ref<Boolean> hasError
206        ) {
207            if (hasError == null) {
208                hasError = Ref.create(false);
209            }
210            else {
211                hasError.set(false);
212            }
213    
214            JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
215            recordExpectedType(c.trace, expression, c.expectedType);
216    
217            if (expressionType == null) return null;
218    
219            JetType result = checkTypeInternal(expressionType, expression, c, hasError);
220            if (Boolean.FALSE.equals(hasError.get())) {
221                for (AdditionalTypeChecker checker : additionalTypeCheckers) {
222                    checker.checkType(expression, expressionType, result, c);
223                }
224            }
225    
226            return result;
227        }
228    
229        @Nullable
230        public JetType checkPossibleCast(
231                @NotNull JetType expressionType,
232                @NotNull JetExpression expression,
233                @NotNull ResolutionContext c
234        ) {
235            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
236    
237            SmartCastResult result = smartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, false);
238            return result != null ? result.getResultType() : null;
239        }
240    
241        public void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
242            if (expectedType != NO_EXPECTED_TYPE) {
243                JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? builtIns.getUnitType() : expectedType;
244                trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
245            }
246        }
247    
248        @Nullable
249        public JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
250            if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.isUnit(context.expectedType) && !context.expectedType.isError()) {
251                context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
252                return null;
253            }
254            return builtIns.getUnitType();
255        }
256    
257        @Nullable
258        public JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
259            if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
260                    && (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType))
261                    && !TypesPackage.isDynamic(expressionType)) {
262                context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
263            }
264            return expressionType;
265        }
266    
267        @NotNull
268        public JetTypeInfo checkImplicitCast(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
269            return typeInfo.replaceType(checkImplicitCast(typeInfo.getType(), expression, context, isStatement));
270        }
271    
272        @NotNull
273        public JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
274            facade.checkStatementType(
275                    expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
276            context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
277            return TypeInfoFactoryPackage.noTypeInfo(context);
278        }
279    
280        @NotNull
281        public Collection<JetType> getAllPossibleTypes(
282                @NotNull JetExpression expression,
283                @NotNull DataFlowInfo dataFlowInfo,
284                @NotNull JetType type,
285                @NotNull ResolutionContext c
286        ) {
287            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
288            Collection<JetType> possibleTypes = Sets.newHashSet(type);
289            if (dataFlowValue.isPredictable()) {
290                possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
291            }
292            return possibleTypes;
293        }
294    
295        @NotNull
296        public JetTypeInfo createCheckedTypeInfo(
297                @Nullable JetType type,
298                @NotNull ResolutionContext<?> context,
299                @NotNull JetExpression expression
300        ) {
301            return checkType(TypeInfoFactoryPackage.createTypeInfo(type, context), expression, context);
302        }
303    
304        @NotNull
305        public JetTypeInfo createCompileTimeConstantTypeInfo(
306                @NotNull CompileTimeConstant<?> value,
307                @NotNull JetExpression expression,
308                @NotNull ExpressionTypingContext context
309        ) {
310            JetType expressionType;
311            if (value instanceof IntegerValueTypeConstant) {
312                IntegerValueTypeConstant integerValueTypeConstant = (IntegerValueTypeConstant) value;
313                if (context.contextDependency == INDEPENDENT) {
314                    expressionType = integerValueTypeConstant.getType(context.expectedType);
315                    constantExpressionEvaluator.updateNumberType(expressionType, expression, context.statementFilter, context.trace);
316                }
317                else {
318                    expressionType = integerValueTypeConstant.getUnknownIntegerType();
319                }
320            }
321            else {
322                expressionType = ((TypedCompileTimeConstant<?>) value).getType();
323            }
324    
325            return createCheckedTypeInfo(expressionType, context, expression);
326        }
327    }