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.context.ResolutionContext;
030    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
031    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
032    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
033    import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils;
034    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
035    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantChecker;
036    import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
037    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
038    import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage;
039    import org.jetbrains.kotlin.types.JetType;
040    import org.jetbrains.kotlin.types.TypeUtils;
041    import org.jetbrains.kotlin.types.TypesPackage;
042    import org.jetbrains.kotlin.types.checker.JetTypeChecker;
043    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
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 DataFlowUtils {
052        private DataFlowUtils() {
053        }
054    
055        @NotNull
056        public static DataFlowInfo extractDataFlowInfoFromCondition(
057                @Nullable JetExpression condition,
058                final boolean conditionValue,
059                final ExpressionTypingContext context
060        ) {
061            if (condition == null) return context.dataFlowInfo;
062            final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
063            condition.accept(new JetVisitorVoid() {
064                @Override
065                public void visitIsExpression(@NotNull JetIsExpression expression) {
066                    if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
067                        result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
068                    }
069                }
070    
071                @Override
072                public void visitBinaryExpression(@NotNull JetBinaryExpression expression) {
073                    IElementType operationToken = expression.getOperationToken();
074                    if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
075                        DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
076                        JetExpression expressionRight = expression.getRight();
077                        if (expressionRight != null) {
078                            DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context);
079                            boolean and = operationToken == JetTokens.ANDAND;
080                            if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
081                                dataFlowInfo = dataFlowInfo.and(rightInfo);
082                            }
083                            else {
084                                dataFlowInfo = dataFlowInfo.or(rightInfo);
085                            }
086                        }
087                        result.set(dataFlowInfo);
088                    }
089                    else  {
090                        JetExpression left = expression.getLeft();
091                        if (left == null) return;
092                        JetExpression right = expression.getRight();
093                        if (right == null) return;
094    
095                        JetType lhsType = context.trace.getBindingContext().getType(left);
096                        if (lhsType == null) return;
097                        JetType rhsType = context.trace.getBindingContext().getType(right);
098                        if (rhsType == null) return;
099    
100                        DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, context);
101                        DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, context);
102    
103                        Boolean equals = null;
104                        if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
105                            equals = true;
106                        }
107                        else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
108                            equals = false;
109                        }
110                        if (equals != null) {
111                            if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
112                                result.set(context.dataFlowInfo.equate(leftValue, rightValue));
113                            }
114                            else {
115                                result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
116                            }
117    
118                        }
119                    }
120                }
121    
122                @Override
123                public void visitUnaryExpression(@NotNull JetUnaryExpression expression) {
124                    IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
125                    if (operationTokenType == JetTokens.EXCL) {
126                        JetExpression baseExpression = expression.getBaseExpression();
127                        if (baseExpression != null) {
128                            result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
129                        }
130                    }
131                }
132    
133                @Override
134                public void visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression) {
135                    JetExpression body = expression.getExpression();
136                    if (body != null) {
137                        body.accept(this);
138                    }
139                }
140            });
141            if (result.get() == null) {
142                return context.dataFlowInfo;
143            }
144            return context.dataFlowInfo.and(result.get());
145        }
146    
147        @Nullable
148        public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
149            return checkType(expressionType, expression, context, null);
150        }
151    
152        @NotNull
153        public static JetTypeInfo checkType(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
154            return typeInfo.replaceType(checkType(typeInfo.getType(), expression, context));
155        }
156    
157        @Nullable
158        public static JetType checkType(
159                @Nullable JetType expressionType,
160                @NotNull JetExpression expressionToCheck,
161                @NotNull ResolutionContext c,
162                @Nullable Ref<Boolean> hasError
163        ) {
164            if (hasError != null) hasError.set(false);
165    
166            JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
167            recordExpectedType(c.trace, expression, c.expectedType);
168    
169            if (expressionType == null) return null;
170    
171            c.additionalTypeChecker.checkType(expression, expressionType, c);
172    
173            if (noExpectedType(c.expectedType) || !c.expectedType.getConstructor().isDenotable() ||
174                JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, c.expectedType)) {
175                return expressionType;
176            }
177    
178            if (expression instanceof JetConstantExpression) {
179                CompileTimeConstant<?> value = ConstantExpressionEvaluator.evaluate(expression, c.trace, c.expectedType);
180                if (value instanceof IntegerValueTypeConstant) {
181                    value = EvaluatePackage.createCompileTimeConstantWithType((IntegerValueTypeConstant) value, c.expectedType);
182                }
183                boolean error = new CompileTimeConstantChecker(c.trace, true)
184                        .checkConstantExpressionType(value, (JetConstantExpression) expression, c.expectedType);
185                if (hasError != null) hasError.set(error);
186                return expressionType;
187            }
188    
189            if (expression instanceof JetWhenExpression) {
190                // No need in additional check because type mismatch is already reported for entries
191                return expressionType;
192            }
193    
194            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
195    
196            for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
197                if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
198                    SmartCastUtils.recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isPredictable(), false);
199                    return possibleType;
200                }
201            }
202            c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType));
203            if (hasError != null) hasError.set(true);
204            return expressionType;
205        }
206    
207        public static void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
208            if (expectedType != NO_EXPECTED_TYPE) {
209                JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? KotlinBuiltIns.getInstance().getUnitType() : expectedType;
210                trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
211            }
212        }
213    
214        @Nullable
215        public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
216            if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.isUnit(context.expectedType) && !context.expectedType.isError()) {
217                context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
218                return null;
219            }
220            return KotlinBuiltIns.getInstance().getUnitType();
221        }
222    
223        @Nullable
224        public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
225            if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
226                    && (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType))
227                    && !TypesPackage.isDynamic(expressionType)) {
228                context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
229            }
230            return expressionType;
231        }
232    
233        @NotNull
234        public static JetTypeInfo checkImplicitCast(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
235            return typeInfo.replaceType(checkImplicitCast(typeInfo.getType(), expression, context, isStatement));
236        }
237    
238        @NotNull
239        public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
240            facade.checkStatementType(
241                    expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
242            context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
243            return TypeInfoFactoryPackage.noTypeInfo(context);
244        }
245    
246        @NotNull
247        public static Collection<JetType> getAllPossibleTypes(
248                @NotNull JetExpression expression,
249                @NotNull DataFlowInfo dataFlowInfo,
250                @NotNull JetType type,
251                @NotNull ResolutionContext c
252        ) {
253            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
254            Collection<JetType> possibleTypes = Sets.newHashSet(type);
255            if (dataFlowValue.isPredictable()) {
256                possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
257            }
258            return possibleTypes;
259        }
260    }