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 }