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
017package org.jetbrains.jet.lang.types.expressions;
018
019import com.intellij.openapi.util.Ref;
020import com.intellij.psi.tree.IElementType;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.psi.*;
024import org.jetbrains.jet.lang.resolve.BindingContext;
025import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
026import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
027import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
028import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
029import org.jetbrains.jet.lang.types.ErrorUtils;
030import org.jetbrains.jet.lang.types.JetType;
031import org.jetbrains.jet.lang.types.JetTypeInfo;
032import org.jetbrains.jet.lang.types.TypeUtils;
033import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
034import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035import org.jetbrains.jet.lexer.JetTokens;
036
037import static org.jetbrains.jet.lang.diagnostics.Errors.*;
038import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
039
040public class DataFlowUtils {
041    private DataFlowUtils() {
042    }
043
044    @NotNull
045    public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) {
046        if (condition == null) return context.dataFlowInfo;
047        final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
048        condition.accept(new JetVisitorVoid() {
049            @Override
050            public void visitIsExpression(JetIsExpression expression) {
051                if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
052                    result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
053                }
054            }
055
056            @Override
057            public void visitBinaryExpression(JetBinaryExpression expression) {
058                IElementType operationToken = expression.getOperationToken();
059                if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
060                    DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
061                    JetExpression expressionRight = expression.getRight();
062                    if (expressionRight != null) {
063                        DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context);
064                        boolean and = operationToken == JetTokens.ANDAND;
065                        if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
066                            dataFlowInfo = dataFlowInfo.and(rightInfo);
067                        }
068                        else {
069                            dataFlowInfo = dataFlowInfo.or(rightInfo);
070                        }
071                    }
072                    result.set(dataFlowInfo);
073                }
074                else  {
075                    JetExpression left = expression.getLeft();
076                    if (left == null) return;
077                    JetExpression right = expression.getRight();
078                    if (right == null) return;
079
080                    JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
081                    if (lhsType == null) return;
082                    JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
083                    if (rhsType == null) return;
084
085                    BindingContext bindingContext = context.trace.getBindingContext();
086                    DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext);
087                    DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext);
088
089                    Boolean equals = null;
090                    if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
091                        equals = true;
092                    }
093                    else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
094                        equals = false;
095                    }
096                    if (equals != null) {
097                        if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
098                            result.set(context.dataFlowInfo.equate(leftValue, rightValue));
099                        }
100                        else {
101                            result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
102                        }
103
104                    }
105                }
106            }
107
108            @Override
109            public void visitUnaryExpression(JetUnaryExpression expression) {
110                IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
111                if (operationTokenType == JetTokens.EXCL) {
112                    JetExpression baseExpression = expression.getBaseExpression();
113                    if (baseExpression != null) {
114                        result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
115                    }
116                }
117            }
118
119            @Override
120            public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
121                JetExpression body = expression.getExpression();
122                if (body != null) {
123                    body.accept(this);
124                }
125            }
126        });
127        if (result.get() == null) {
128            return context.dataFlowInfo;
129        }
130        return context.dataFlowInfo.and(result.get());
131    }
132
133    @NotNull
134    public static JetTypeInfo checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
135        return JetTypeInfo.create(checkType(expressionType, expression, context), dataFlowInfo);
136    }
137
138    @Nullable
139    public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
140        if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE) {
141            context.trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, context.expectedType);
142        }
143
144        if (expressionType == null || context.expectedType == null || context.expectedType == TypeUtils.NO_EXPECTED_TYPE ||
145            JetTypeChecker.INSTANCE.isSubtypeOf(expressionType, context.expectedType)) {
146            return expressionType;
147        }
148
149        DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext());
150        for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
151            if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) {
152                if (dataFlowValue.isStableIdentifier()) {
153                    context.trace.record(AUTOCAST, expression, possibleType);
154                }
155                else {
156                    context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
157                }
158                return possibleType;
159            }
160        }
161        context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType));
162        return expressionType;
163    }
164
165    @NotNull
166    public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
167        return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo);
168    }
169
170    @Nullable
171    public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
172        if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE && !KotlinBuiltIns.getInstance().isUnit(context.expectedType) && !ErrorUtils.isErrorType(context.expectedType)) {
173            context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
174            return null;
175        }
176        return KotlinBuiltIns.getInstance().getUnitType();
177    }
178
179    @NotNull
180    public static JetTypeInfo checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement, DataFlowInfo dataFlowInfo) {
181        return JetTypeInfo.create(checkImplicitCast(expressionType, expression, context, isStatement), dataFlowInfo);
182    }
183
184    @Nullable
185    public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement) {
186        if (expressionType != null && context.expectedType == TypeUtils.NO_EXPECTED_TYPE && !isStatement &&
187            (KotlinBuiltIns.getInstance().isUnit(expressionType) || KotlinBuiltIns.getInstance().isAny(expressionType))) {
188            context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
189
190        }
191        return expressionType;
192    }
193
194    @NotNull
195    public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
196        facade.checkStatementType(expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE));
197        context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
198        return JetTypeInfo.create(null, context.dataFlowInfo);
199    }
200}