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.diagnostics.Diagnostic;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.resolve.BindingContext;
026    import org.jetbrains.jet.lang.resolve.BindingTrace;
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.resolve.calls.context.ResolutionContext;
031    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantResolver;
032    import org.jetbrains.jet.lang.types.JetType;
033    import org.jetbrains.jet.lang.types.JetTypeInfo;
034    import org.jetbrains.jet.lang.types.TypeUtils;
035    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
036    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037    import org.jetbrains.jet.lexer.JetTokens;
038    
039    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
040    import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
041    import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
042    import static org.jetbrains.jet.lang.types.TypeUtils.*;
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(@NotNull 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(@NotNull 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.createDataFlowValue(left, lhsType, bindingContext);
091                        DataFlowValue rightValue = DataFlowValueFactory.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(@NotNull 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(@NotNull 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 expressionToCheck,
158                @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace
159        ) {
160            JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
161            recordExpectedType(trace, expression, expectedType);
162    
163            if (expressionType == null || noExpectedType(expectedType) ||
164                JetTypeChecker.INSTANCE.isSubtypeOf(expressionType, expectedType)) {
165                return expressionType;
166            }
167    
168            if (expression instanceof JetConstantExpression) {
169                Diagnostic diagnostic =
170                        new CompileTimeConstantResolver().checkConstantExpressionType((JetConstantExpression) expression, expectedType);
171                if (diagnostic != null) {
172                    trace.report(diagnostic);
173                }
174                return expressionType;
175            }
176    
177            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
178            for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
179                if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, expectedType)) {
180                    if (dataFlowValue.isStableIdentifier()) {
181                        trace.record(AUTOCAST, expression, possibleType);
182                    }
183                    else {
184                        trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
185                    }
186                    return possibleType;
187                }
188            }
189            trace.report(TYPE_MISMATCH.on(expression, expectedType, expressionType));
190            return expressionType;
191        }
192    
193        public static void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
194            if (expectedType != NO_EXPECTED_TYPE) {
195                JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? KotlinBuiltIns.getInstance().getUnitType() : expectedType;
196                trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
197            }
198        }
199    
200        @NotNull
201        public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
202            return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo);
203        }
204    
205        @Nullable
206        public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
207            if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.getInstance().isUnit(context.expectedType) && !context.expectedType.isError()) {
208                context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
209                return null;
210            }
211            return KotlinBuiltIns.getInstance().getUnitType();
212        }
213    
214        @NotNull
215        public static JetTypeInfo checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement, DataFlowInfo dataFlowInfo) {
216            return JetTypeInfo.create(checkImplicitCast(expressionType, expression, context, isStatement), dataFlowInfo);
217        }
218    
219        @Nullable
220        public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement) {
221            if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
222                    && (KotlinBuiltIns.getInstance().isUnit(expressionType) || KotlinBuiltIns.getInstance().isAny(expressionType))) {
223                context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
224            }
225            return expressionType;
226        }
227    
228        @NotNull
229        public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
230            facade.checkStatementType(
231                    expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
232            context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
233            return JetTypeInfo.create(null, context.dataFlowInfo);
234        }
235    }