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 017package org.jetbrains.jet.lang.types.expressions; 018 019import com.intellij.openapi.util.Ref; 020import com.intellij.psi.tree.IElementType; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.psi.*; 024import org.jetbrains.jet.lang.resolve.BindingContext; 025import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext; 026import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; 027import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; 028import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; 029import org.jetbrains.jet.lang.types.ErrorUtils; 030import org.jetbrains.jet.lang.types.JetType; 031import org.jetbrains.jet.lang.types.JetTypeInfo; 032import org.jetbrains.jet.lang.types.TypeUtils; 033import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 034import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 035import org.jetbrains.jet.lexer.JetTokens; 036 037import static org.jetbrains.jet.lang.diagnostics.Errors.*; 038import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST; 039 040public class DataFlowUtils { 041 private DataFlowUtils() { 042 } 043 044 @NotNull 045 public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) { 046 if (condition == null) return context.dataFlowInfo; 047 final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null); 048 condition.accept(new JetVisitorVoid() { 049 @Override 050 public void visitIsExpression(JetIsExpression expression) { 051 if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) { 052 result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression)); 053 } 054 } 055 056 @Override 057 public void visitBinaryExpression(JetBinaryExpression expression) { 058 IElementType operationToken = expression.getOperationToken(); 059 if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) { 060 DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context); 061 JetExpression expressionRight = expression.getRight(); 062 if (expressionRight != null) { 063 DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context); 064 boolean and = operationToken == JetTokens.ANDAND; 065 if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue 066 dataFlowInfo = dataFlowInfo.and(rightInfo); 067 } 068 else { 069 dataFlowInfo = dataFlowInfo.or(rightInfo); 070 } 071 } 072 result.set(dataFlowInfo); 073 } 074 else { 075 JetExpression left = expression.getLeft(); 076 if (left == null) return; 077 JetExpression right = expression.getRight(); 078 if (right == null) return; 079 080 JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); 081 if (lhsType == null) return; 082 JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); 083 if (rhsType == null) return; 084 085 BindingContext bindingContext = context.trace.getBindingContext(); 086 DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext); 087 DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext); 088 089 Boolean equals = null; 090 if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { 091 equals = true; 092 } 093 else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { 094 equals = false; 095 } 096 if (equals != null) { 097 if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue 098 result.set(context.dataFlowInfo.equate(leftValue, rightValue)); 099 } 100 else { 101 result.set(context.dataFlowInfo.disequate(leftValue, rightValue)); 102 } 103 104 } 105 } 106 } 107 108 @Override 109 public void visitUnaryExpression(JetUnaryExpression expression) { 110 IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType(); 111 if (operationTokenType == JetTokens.EXCL) { 112 JetExpression baseExpression = expression.getBaseExpression(); 113 if (baseExpression != null) { 114 result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context)); 115 } 116 } 117 } 118 119 @Override 120 public void visitParenthesizedExpression(JetParenthesizedExpression expression) { 121 JetExpression body = expression.getExpression(); 122 if (body != null) { 123 body.accept(this); 124 } 125 } 126 }); 127 if (result.get() == null) { 128 return context.dataFlowInfo; 129 } 130 return context.dataFlowInfo.and(result.get()); 131 } 132 133 @NotNull 134 public static JetTypeInfo checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) { 135 return JetTypeInfo.create(checkType(expressionType, expression, context), dataFlowInfo); 136 } 137 138 @Nullable 139 public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) { 140 if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE) { 141 context.trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, context.expectedType); 142 } 143 144 if (expressionType == null || context.expectedType == null || context.expectedType == TypeUtils.NO_EXPECTED_TYPE || 145 JetTypeChecker.INSTANCE.isSubtypeOf(expressionType, context.expectedType)) { 146 return expressionType; 147 } 148 149 DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext()); 150 for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) { 151 if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) { 152 if (dataFlowValue.isStableIdentifier()) { 153 context.trace.record(AUTOCAST, expression, possibleType); 154 } 155 else { 156 context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); 157 } 158 return possibleType; 159 } 160 } 161 context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType)); 162 return expressionType; 163 } 164 165 @NotNull 166 public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) { 167 return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo); 168 } 169 170 @Nullable 171 public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) { 172 if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE && !KotlinBuiltIns.getInstance().isUnit(context.expectedType) && !ErrorUtils.isErrorType(context.expectedType)) { 173 context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType)); 174 return null; 175 } 176 return KotlinBuiltIns.getInstance().getUnitType(); 177 } 178 179 @NotNull 180 public static JetTypeInfo checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement, DataFlowInfo dataFlowInfo) { 181 return JetTypeInfo.create(checkImplicitCast(expressionType, expression, context, isStatement), dataFlowInfo); 182 } 183 184 @Nullable 185 public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement) { 186 if (expressionType != null && context.expectedType == TypeUtils.NO_EXPECTED_TYPE && !isStatement && 187 (KotlinBuiltIns.getInstance().isUnit(expressionType) || KotlinBuiltIns.getInstance().isAny(expressionType))) { 188 context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType)); 189 190 } 191 return expressionType; 192 } 193 194 @NotNull 195 public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) { 196 facade.checkStatementType(expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)); 197 context.trace.report(EXPRESSION_EXPECTED.on(expression, expression)); 198 return JetTypeInfo.create(null, context.dataFlowInfo); 199 } 200}