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