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.openapi.util.text.StringUtil;
021 import com.intellij.psi.tree.IElementType;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
025 import org.jetbrains.jet.lang.evaluate.EvaluatePackage;
026 import org.jetbrains.jet.lang.psi.*;
027 import org.jetbrains.jet.lang.resolve.BindingContext;
028 import org.jetbrains.jet.lang.resolve.BindingTrace;
029 import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
030 import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo;
031 import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValue;
032 import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValueFactory;
033 import org.jetbrains.jet.lang.resolve.calls.smartcasts.SmartCastUtils;
034 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
035 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantChecker;
036 import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
037 import org.jetbrains.jet.lang.types.*;
038 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
039 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
040 import org.jetbrains.jet.lexer.JetTokens;
041
042 import java.util.Set;
043
044 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
045 import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
046 import static org.jetbrains.jet.lang.types.TypeUtils.*;
047
048 public class DataFlowUtils {
049 private DataFlowUtils() {
050 }
051
052 @NotNull
053 public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) {
054 if (condition == null) return context.dataFlowInfo;
055 final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
056 condition.accept(new JetVisitorVoid() {
057 @Override
058 public void visitIsExpression(@NotNull JetIsExpression expression) {
059 if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
060 result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
061 }
062 }
063
064 @Override
065 public void visitBinaryExpression(@NotNull JetBinaryExpression expression) {
066 IElementType operationToken = expression.getOperationToken();
067 if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
068 DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
069 JetExpression expressionRight = expression.getRight();
070 if (expressionRight != null) {
071 DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context);
072 boolean and = operationToken == JetTokens.ANDAND;
073 if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
074 dataFlowInfo = dataFlowInfo.and(rightInfo);
075 }
076 else {
077 dataFlowInfo = dataFlowInfo.or(rightInfo);
078 }
079 }
080 result.set(dataFlowInfo);
081 }
082 else {
083 JetExpression left = expression.getLeft();
084 if (left == null) return;
085 JetExpression right = expression.getRight();
086 if (right == null) return;
087
088 JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
089 if (lhsType == null) return;
090 JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
091 if (rhsType == null) return;
092
093 BindingContext bindingContext = context.trace.getBindingContext();
094 DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, bindingContext);
095 DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, bindingContext);
096
097 Boolean equals = null;
098 if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
099 equals = true;
100 }
101 else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
102 equals = false;
103 }
104 if (equals != null) {
105 if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
106 result.set(context.dataFlowInfo.equate(leftValue, rightValue));
107 }
108 else {
109 result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
110 }
111
112 }
113 }
114 }
115
116 @Override
117 public void visitUnaryExpression(@NotNull JetUnaryExpression expression) {
118 IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
119 if (operationTokenType == JetTokens.EXCL) {
120 JetExpression baseExpression = expression.getBaseExpression();
121 if (baseExpression != null) {
122 result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
123 }
124 }
125 }
126
127 @Override
128 public void visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression) {
129 JetExpression body = expression.getExpression();
130 if (body != null) {
131 body.accept(this);
132 }
133 }
134 });
135 if (result.get() == null) {
136 return context.dataFlowInfo;
137 }
138 return context.dataFlowInfo.and(result.get());
139 }
140
141 @NotNull
142 public static JetTypeInfo checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
143 return JetTypeInfo.create(checkType(expressionType, expression, context), dataFlowInfo);
144 }
145
146 @NotNull
147 public static JetTypeInfo checkType(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
148 JetType type = checkType(typeInfo.getType(), expression, context);
149 if (type == typeInfo.getType()) {
150 return typeInfo;
151 }
152 return JetTypeInfo.create(type, typeInfo.getDataFlowInfo());
153 }
154
155 @Nullable
156 public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
157 return checkType(expressionType, expression, context.expectedType, context.dataFlowInfo, context.trace);
158 }
159
160 @Nullable
161 public static JetType checkType(@Nullable final JetType expressionType, @NotNull JetExpression expressionToCheck,
162 @NotNull JetType expectedType, @NotNull final DataFlowInfo dataFlowInfo, @NotNull final BindingTrace trace
163 ) {
164 final JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
165 recordExpectedType(trace, expression, expectedType);
166
167 if (expressionType == null) return null;
168
169 if (noExpectedType(expectedType) || !expectedType.getConstructor().isDenotable() ||
170 JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)) {
171
172 if (!noExpectedType(expectedType)) {
173 Approximation.Info approximationInfo = TypesPackage.getApproximationTo(expressionType, expectedType,
174 new Approximation.DataFlowExtras() {
175 private DataFlowValue getDataFlowValue() {
176 return DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
177 }
178
179 @Override
180 public boolean getCanBeNull() {
181 return dataFlowInfo.getNullability(getDataFlowValue()).canBeNull();
182 }
183
184 @NotNull
185 @Override
186 public Set<JetType> getPossibleTypes() {
187 return dataFlowInfo.getPossibleTypes(getDataFlowValue());
188 }
189
190 @NotNull
191 @Override
192 public String getPresentableText() {
193 return StringUtil.trimMiddle(expression.getText(), 50);
194 }
195 }
196 );
197 if (approximationInfo != null) {
198 trace.record(BindingContext.EXPRESSION_RESULT_APPROXIMATION, expression, approximationInfo);
199 }
200 }
201
202 return expressionType;
203 }
204
205 if (expression instanceof JetConstantExpression) {
206 CompileTimeConstant<?> value = ConstantExpressionEvaluator.OBJECT$.evaluate(expression, trace, expectedType);
207 if (value instanceof IntegerValueTypeConstant) {
208 value = EvaluatePackage.createCompileTimeConstantWithType((IntegerValueTypeConstant) value, expectedType);
209 }
210 new CompileTimeConstantChecker(trace, true).checkConstantExpressionType(value, (JetConstantExpression) expression, expectedType);
211 return expressionType;
212 }
213
214 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
215
216 for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
217 if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
218 SmartCastUtils.recordCastOrError(expression, possibleType, trace, dataFlowValue.isStableIdentifier(), false);
219 return possibleType;
220 }
221 }
222 trace.report(TYPE_MISMATCH.on(expression, expectedType, expressionType));
223 return expressionType;
224 }
225
226 public static void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
227 if (expectedType != NO_EXPECTED_TYPE) {
228 JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? KotlinBuiltIns.getInstance().getUnitType() : expectedType;
229 trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
230 }
231 }
232
233 @NotNull
234 public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
235 return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo);
236 }
237
238 @Nullable
239 public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
240 if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.getInstance().isUnit(context.expectedType) && !context.expectedType.isError()) {
241 context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
242 return null;
243 }
244 return KotlinBuiltIns.getInstance().getUnitType();
245 }
246
247 @NotNull
248 public static JetTypeInfo checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement, DataFlowInfo dataFlowInfo) {
249 return JetTypeInfo.create(checkImplicitCast(expressionType, expression, context, isStatement), dataFlowInfo);
250 }
251
252 @Nullable
253 public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement) {
254 if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
255 && (KotlinBuiltIns.getInstance().isUnit(expressionType) || KotlinBuiltIns.getInstance().isAnyOrNullableAny(expressionType))) {
256 context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
257 }
258 return expressionType;
259 }
260
261 @NotNull
262 public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
263 facade.checkStatementType(
264 expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
265 context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
266 return JetTypeInfo.create(null, context.dataFlowInfo);
267 }
268 }