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