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.psi.tree.IElementType;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.psi.*;
024    import org.jetbrains.jet.lang.resolve.BindingContext;
025    import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
026    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
027    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
028    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
029    import org.jetbrains.jet.lang.types.ErrorUtils;
030    import org.jetbrains.jet.lang.types.JetType;
031    import org.jetbrains.jet.lang.types.JetTypeInfo;
032    import org.jetbrains.jet.lang.types.TypeUtils;
033    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
034    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035    import org.jetbrains.jet.lexer.JetTokens;
036    
037    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
038    import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
039    
040    public 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    }