001    /*
002     * Copyright 2010-2013 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.jet.lang.types.expressions;
018    
019    import com.intellij.openapi.util.Ref;
020    import com.intellij.openapi.util.text.StringUtil;
021    import com.intellij.psi.tree.IElementType;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
025    import org.jetbrains.jet.lang.evaluate.EvaluatePackage;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.jet.lang.resolve.BindingContext;
028    import org.jetbrains.jet.lang.resolve.BindingTrace;
029    import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
030    import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo;
031    import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValue;
032    import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValueFactory;
033    import org.jetbrains.jet.lang.resolve.calls.smartcasts.SmartCastUtils;
034    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
035    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantChecker;
036    import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
037    import org.jetbrains.jet.lang.types.*;
038    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
039    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
040    import org.jetbrains.jet.lexer.JetTokens;
041    
042    import java.util.Set;
043    
044    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
045    import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
046    import static org.jetbrains.jet.lang.types.TypeUtils.*;
047    
048    public class DataFlowUtils {
049        private DataFlowUtils() {
050        }
051    
052        @NotNull
053        public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) {
054            if (condition == null) return context.dataFlowInfo;
055            final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
056            condition.accept(new JetVisitorVoid() {
057                @Override
058                public void visitIsExpression(@NotNull JetIsExpression expression) {
059                    if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
060                        result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
061                    }
062                }
063    
064                @Override
065                public void visitBinaryExpression(@NotNull JetBinaryExpression expression) {
066                    IElementType operationToken = expression.getOperationToken();
067                    if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
068                        DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
069                        JetExpression expressionRight = expression.getRight();
070                        if (expressionRight != null) {
071                            DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context);
072                            boolean and = operationToken == JetTokens.ANDAND;
073                            if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
074                                dataFlowInfo = dataFlowInfo.and(rightInfo);
075                            }
076                            else {
077                                dataFlowInfo = dataFlowInfo.or(rightInfo);
078                            }
079                        }
080                        result.set(dataFlowInfo);
081                    }
082                    else  {
083                        JetExpression left = expression.getLeft();
084                        if (left == null) return;
085                        JetExpression right = expression.getRight();
086                        if (right == null) return;
087    
088                        JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
089                        if (lhsType == null) return;
090                        JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
091                        if (rhsType == null) return;
092    
093                        BindingContext bindingContext = context.trace.getBindingContext();
094                        DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, bindingContext);
095                        DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, bindingContext);
096    
097                        Boolean equals = null;
098                        if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
099                            equals = true;
100                        }
101                        else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
102                            equals = false;
103                        }
104                        if (equals != null) {
105                            if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
106                                result.set(context.dataFlowInfo.equate(leftValue, rightValue));
107                            }
108                            else {
109                                result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
110                            }
111    
112                        }
113                    }
114                }
115    
116                @Override
117                public void visitUnaryExpression(@NotNull JetUnaryExpression expression) {
118                    IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
119                    if (operationTokenType == JetTokens.EXCL) {
120                        JetExpression baseExpression = expression.getBaseExpression();
121                        if (baseExpression != null) {
122                            result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
123                        }
124                    }
125                }
126    
127                @Override
128                public void visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression) {
129                    JetExpression body = expression.getExpression();
130                    if (body != null) {
131                        body.accept(this);
132                    }
133                }
134            });
135            if (result.get() == null) {
136                return context.dataFlowInfo;
137            }
138            return context.dataFlowInfo.and(result.get());
139        }
140    
141        @NotNull
142        public static JetTypeInfo checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
143            return JetTypeInfo.create(checkType(expressionType, expression, context), dataFlowInfo);
144        }
145    
146        @NotNull
147        public static JetTypeInfo checkType(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
148            JetType type = checkType(typeInfo.getType(), expression, context);
149            if (type == typeInfo.getType()) {
150                return typeInfo;
151            }
152            return JetTypeInfo.create(type, typeInfo.getDataFlowInfo());
153        }
154    
155        @Nullable
156        public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
157            return checkType(expressionType, expression, context.expectedType, context.dataFlowInfo, context.trace);
158        }
159    
160        @Nullable
161        public static JetType checkType(@Nullable final JetType expressionType, @NotNull JetExpression expressionToCheck,
162                @NotNull JetType expectedType, @NotNull final DataFlowInfo dataFlowInfo, @NotNull final BindingTrace trace
163        ) {
164            final JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
165            recordExpectedType(trace, expression, expectedType);
166    
167            if (expressionType == null) return null;
168    
169            if (noExpectedType(expectedType) || !expectedType.getConstructor().isDenotable() ||
170                JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)) {
171    
172                if (!noExpectedType(expectedType)) {
173                    Approximation.Info approximationInfo = TypesPackage.getApproximationTo(expressionType, expectedType,
174                            new Approximation.DataFlowExtras() {
175                                private DataFlowValue getDataFlowValue() {
176                                    return DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
177                                }
178    
179                                @Override
180                                public boolean getCanBeNull() {
181                                    return dataFlowInfo.getNullability(getDataFlowValue()).canBeNull();
182                                }
183    
184                                @NotNull
185                                @Override
186                                public Set<JetType> getPossibleTypes() {
187                                    return dataFlowInfo.getPossibleTypes(getDataFlowValue());
188                                }
189    
190                                @NotNull
191                                @Override
192                                public String getPresentableText() {
193                                    return StringUtil.trimMiddle(expression.getText(), 50);
194                                }
195                            }
196                    );
197                    if (approximationInfo != null) {
198                        trace.record(BindingContext.EXPRESSION_RESULT_APPROXIMATION, expression, approximationInfo);
199                    }
200                }
201    
202                return expressionType;
203            }
204    
205            if (expression instanceof JetConstantExpression) {
206                CompileTimeConstant<?> value = ConstantExpressionEvaluator.OBJECT$.evaluate(expression, trace, expectedType);
207                if (value instanceof IntegerValueTypeConstant) {
208                    value = EvaluatePackage.createCompileTimeConstantWithType((IntegerValueTypeConstant) value, expectedType);
209                }
210                new CompileTimeConstantChecker(trace, true).checkConstantExpressionType(value, (JetConstantExpression) expression, expectedType);
211                return expressionType;
212            }
213    
214            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
215    
216            for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
217                if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
218                    SmartCastUtils.recordCastOrError(expression, possibleType, trace, dataFlowValue.isStableIdentifier(), false);
219                    return possibleType;
220                }
221            }
222            trace.report(TYPE_MISMATCH.on(expression, expectedType, expressionType));
223            return expressionType;
224        }
225    
226        public static void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
227            if (expectedType != NO_EXPECTED_TYPE) {
228                JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? KotlinBuiltIns.getInstance().getUnitType() : expectedType;
229                trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
230            }
231        }
232    
233        @NotNull
234        public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
235            return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo);
236        }
237    
238        @Nullable
239        public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
240            if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.getInstance().isUnit(context.expectedType) && !context.expectedType.isError()) {
241                context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
242                return null;
243            }
244            return KotlinBuiltIns.getInstance().getUnitType();
245        }
246    
247        @NotNull
248        public static JetTypeInfo checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement, DataFlowInfo dataFlowInfo) {
249            return JetTypeInfo.create(checkImplicitCast(expressionType, expression, context, isStatement), dataFlowInfo);
250        }
251    
252        @Nullable
253        public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement) {
254            if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
255                    && (KotlinBuiltIns.getInstance().isUnit(expressionType) || KotlinBuiltIns.getInstance().isAnyOrNullableAny(expressionType))) {
256                context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
257            }
258            return expressionType;
259        }
260    
261        @NotNull
262        public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
263            facade.checkStatementType(
264                    expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
265            context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
266            return JetTypeInfo.create(null, context.dataFlowInfo);
267        }
268    }