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 }