Nullable Nothing is exactly null
This commit is contained in:
+3
@@ -9,7 +9,9 @@ import org.jetbrains.jet.lang.psi.*;
|
|||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.JetModuleUtil;
|
import org.jetbrains.jet.lang.resolve.JetModuleUtil;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
|
import org.jetbrains.jet.lang.types.JetTypeChecker;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ public class DataFlowValueFactory {
|
|||||||
JetConstantExpression constantExpression = (JetConstantExpression) expression;
|
JetConstantExpression constantExpression = (JetConstantExpression) expression;
|
||||||
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
||||||
}
|
}
|
||||||
|
if (JetTypeChecker.INSTANCE.equalTypes(type, JetStandardClasses.getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
|
||||||
Pair<Object, Boolean> result = getIdForStableIdentifier(expression, bindingContext, false);
|
Pair<Object, Boolean> result = getIdForStableIdentifier(expression, bindingContext, false);
|
||||||
return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type));
|
return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ public class JetStandardClasses {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<JetType> iterator() {
|
public Iterator<JetType> iterator() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException("Don't enumerate supertypes of Nothing");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException("Supertypes of Nothing do not constitute a valid collection");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
JetScope.EMPTY,
|
JetScope.EMPTY,
|
||||||
@@ -278,8 +278,13 @@ public class JetStandardClasses {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNothing(@NotNull JetType type) {
|
public static boolean isNothing(@NotNull JetType type) {
|
||||||
return !(type instanceof NamespaceType) &&
|
return isNothingOrNullableNothing(type)
|
||||||
type.getConstructor() == NOTHING_CLASS.getTypeConstructor();
|
&& !type.isNullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNothingOrNullableNothing(@NotNull JetType type) {
|
||||||
|
return !(type instanceof NamespaceType)
|
||||||
|
&& type.getConstructor() == NOTHING_CLASS.getTypeConstructor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isUnit(@NotNull JetType type) {
|
public static boolean isUnit(@NotNull JetType type) {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class JetTypeChecker {
|
|||||||
JetType type = iterator.next();
|
JetType type = iterator.next();
|
||||||
assert type != null;
|
assert type != null;
|
||||||
// TODO : This admits 'Nothing?'. Review
|
// TODO : This admits 'Nothing?'. Review
|
||||||
if (JetStandardClasses.isNothing(type)) {
|
if (JetStandardClasses.isNothingOrNullableNothing(type)) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
nullable |= type.isNullable();
|
nullable |= type.isNullable();
|
||||||
@@ -564,7 +564,7 @@ public class JetTypeChecker {
|
|||||||
if (!supertype.isNullable() && subtype.isNullable()) {
|
if (!supertype.isNullable() && subtype.isNullable()) {
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
if (JetStandardClasses.isNothing(subtype)) {
|
if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
|
||||||
return StatusAction.DONE_WITH_CURRENT_TYPE;
|
return StatusAction.DONE_WITH_CURRENT_TYPE;
|
||||||
}
|
}
|
||||||
return StatusAction.PROCEED;
|
return StatusAction.PROCEED;
|
||||||
|
|||||||
+21
-21
@@ -49,7 +49,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.trace.record(REFERENCE_TARGET, expression, property);
|
context.trace.record(REFERENCE_TARGET, expression, property);
|
||||||
return checkType(property.getOutType(), expression, context);
|
return DataFlowUtils.checkType(property.getOutType(), expression, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -74,14 +74,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (result == null) {
|
if (result == null) {
|
||||||
return ErrorUtils.createErrorType("No class object in " + expression.getReferencedName());
|
return ErrorUtils.createErrorType("No class object in " + expression.getReferencedName());
|
||||||
}
|
}
|
||||||
return checkType(result, expression, context);
|
return DataFlowUtils.checkType(result, expression, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JetType[] result = new JetType[1];
|
JetType[] result = new JetType[1];
|
||||||
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
|
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
|
||||||
if (furtherNameLookup(expression, referencedName, result, context.replaceBindingTrace(temporaryTrace))) {
|
if (furtherNameLookup(expression, referencedName, result, context.replaceBindingTrace(temporaryTrace))) {
|
||||||
temporaryTrace.commit();
|
temporaryTrace.commit();
|
||||||
return checkType(result[0], expression, context);
|
return DataFlowUtils.checkType(result[0], expression, context);
|
||||||
}
|
}
|
||||||
// To report NO_CLASS_OBJECT when no namespace found
|
// To report NO_CLASS_OBJECT when no namespace found
|
||||||
if (classifier != null) {
|
if (classifier != null) {
|
||||||
@@ -122,7 +122,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (innerExpression == null) {
|
if (innerExpression == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return checkType(facade.getType(innerExpression, context.replaceScope(context.scope)), expression, context);
|
return DataFlowUtils.checkType(facade.getType(innerExpression, context.replaceScope(context.scope)), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -162,7 +162,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.trace.record(BindingContext.COMPILE_TIME_VALUE, expression, value);
|
context.trace.record(BindingContext.COMPILE_TIME_VALUE, expression, value);
|
||||||
return checkType(value.getType(standardLibrary), expression, context);
|
return DataFlowUtils.checkType(value.getType(standardLibrary), expression, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
else {
|
else {
|
||||||
facade.getType(expression.getLeft(), context.replaceExpectedType(NO_EXPECTED_TYPE));
|
facade.getType(expression.getLeft(), context.replaceExpectedType(NO_EXPECTED_TYPE));
|
||||||
}
|
}
|
||||||
return checkType(result, expression, context);
|
return DataFlowUtils.checkType(result, expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkBinaryWithTypeRHS(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context, @NotNull JetType targetType, @NotNull JetType expectedType, TemporaryBindingTrace temporaryTrace) {
|
private boolean checkBinaryWithTypeRHS(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context, @NotNull JetType targetType, @NotNull JetType expectedType, TemporaryBindingTrace temporaryTrace) {
|
||||||
@@ -259,7 +259,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO : labels
|
// TODO : labels
|
||||||
return checkType(JetStandardClasses.getTupleType(types), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getTupleType(types), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -269,7 +269,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
List<JetType> result = Lists.newArrayListWithCapacity(arguments.size());
|
List<JetType> result = Lists.newArrayListWithCapacity(arguments.size());
|
||||||
for (int i = 0, argumentTypesSize = argumentTypes.size(); i < argumentTypesSize; i++) {
|
for (int i = 0, argumentTypesSize = argumentTypes.size(); i < argumentTypesSize; i++) {
|
||||||
result.add(checkType(argumentTypes.get(i), arguments.get(i), context.replaceExpectedType(expectedArgumentTypes.get(i).getType())));
|
result.add(DataFlowUtils.checkType(argumentTypes.get(i), arguments.get(i), context.replaceExpectedType(expectedArgumentTypes.get(i).getType())));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -330,7 +330,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return checkType(result, expression, context);
|
return DataFlowUtils.checkType(result, expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -386,7 +386,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (result != null) {
|
if (result != null) {
|
||||||
context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, result);
|
context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, result);
|
||||||
}
|
}
|
||||||
return checkType(result, expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(result, expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateConstantValues(JetQualifiedExpression expression, ExpressionTypingContext context, JetSimpleNameExpression selectorExpression) {
|
private void propagateConstantValues(JetQualifiedExpression expression, ExpressionTypingContext context, JetSimpleNameExpression selectorExpression) {
|
||||||
@@ -432,14 +432,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
VariableDescriptor variableDescriptor = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression);
|
VariableDescriptor variableDescriptor = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression);
|
||||||
if (variableDescriptor != null) {
|
if (variableDescriptor != null) {
|
||||||
temporaryTrace.commit();
|
temporaryTrace.commit();
|
||||||
return checkType(variableDescriptor.getOutType(), nameExpression, context);
|
return DataFlowUtils.checkType(variableDescriptor.getOutType(), nameExpression, context);
|
||||||
}
|
}
|
||||||
ExpressionTypingContext newContext = receiver.exists() ? context.replaceScope(receiver.getType().getMemberScope()) : context;
|
ExpressionTypingContext newContext = receiver.exists() ? context.replaceScope(receiver.getType().getMemberScope()) : context;
|
||||||
JetType jetType = lookupNamespaceOrClassObject(nameExpression, nameExpression.getReferencedName(), newContext);
|
JetType jetType = lookupNamespaceOrClassObject(nameExpression, nameExpression.getReferencedName(), newContext);
|
||||||
if (jetType == null) {
|
if (jetType == null) {
|
||||||
context.trace.report(UNRESOLVED_REFERENCE.on(nameExpression));
|
context.trace.report(UNRESOLVED_REFERENCE.on(nameExpression));
|
||||||
}
|
}
|
||||||
return checkType(jetType, nameExpression, context);
|
return DataFlowUtils.checkType(jetType, nameExpression, context);
|
||||||
}
|
}
|
||||||
else if (selectorExpression instanceof JetQualifiedExpression) {
|
else if (selectorExpression instanceof JetQualifiedExpression) {
|
||||||
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
|
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
|
||||||
@@ -460,7 +460,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public JetType visitCallExpression(JetCallExpression expression, ExpressionTypingContext context) {
|
public JetType visitCallExpression(JetCallExpression expression, ExpressionTypingContext context) {
|
||||||
JetType expressionType = context.resolveCall(NO_RECEIVER, null, expression);
|
JetType expressionType = context.resolveCall(NO_RECEIVER, null, expression);
|
||||||
return checkType(expressionType, expression, context);
|
return DataFlowUtils.checkType(expressionType, expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -473,7 +473,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
referencedName = referencedName == null ? " <?>" : referencedName;
|
referencedName = referencedName == null ? " <?>" : referencedName;
|
||||||
context.labelResolver.enterLabeledElement(referencedName.substring(1), baseExpression);
|
context.labelResolver.enterLabeledElement(referencedName.substring(1), baseExpression);
|
||||||
// TODO : Some processing for the label?
|
// TODO : Some processing for the label?
|
||||||
JetType type = checkType(facade.getType(baseExpression, context.replaceExpectedReturnType(context.expectedType)), expression, context);
|
JetType type = DataFlowUtils.checkType(facade.getType(baseExpression, context.replaceExpectedReturnType(context.expectedType)), expression, context);
|
||||||
context.labelResolver.exitLabeledElement(baseExpression);
|
context.labelResolver.exitLabeledElement(baseExpression);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -516,7 +516,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
result = returnType;
|
result = returnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkType(result, expression, context);
|
return DataFlowUtils.checkType(result, expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -599,7 +599,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) {
|
else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) {
|
||||||
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
|
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
|
||||||
WritableScopeImpl leftScope = newWritableScopeImpl(context).setDebugName("Left scope of && or ||");
|
WritableScopeImpl leftScope = newWritableScopeImpl(context).setDebugName("Left scope of && or ||");
|
||||||
DataFlowInfo flowInfoLeft = extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, leftScope, context); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition
|
DataFlowInfo flowInfoLeft = DataFlowUtils.extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, leftScope, context); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition
|
||||||
WritableScopeImpl rightScope = operationType == JetTokens.ANDAND ? leftScope : newWritableScopeImpl(context).setDebugName("Right scope of && or ||");
|
WritableScopeImpl rightScope = operationType == JetTokens.ANDAND ? leftScope : newWritableScopeImpl(context).setDebugName("Right scope of && or ||");
|
||||||
JetType rightType = right == null ? null : facade.getType(right, context.replaceDataFlowInfo(flowInfoLeft).replaceScope(rightScope));
|
JetType rightType = right == null ? null : facade.getType(right, context.replaceDataFlowInfo(flowInfoLeft).replaceScope(rightScope));
|
||||||
if (leftType != null && !isBoolean(context.semanticServices, leftType)) {
|
if (leftType != null && !isBoolean(context.semanticServices, leftType)) {
|
||||||
@@ -618,7 +618,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
|
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
|
||||||
}
|
}
|
||||||
if (rightType != null) {
|
if (rightType != null) {
|
||||||
checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
|
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
|
||||||
return TypeUtils.makeNullableAsSpecified(context.semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable());
|
return TypeUtils.makeNullableAsSpecified(context.semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +626,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
else {
|
else {
|
||||||
context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
|
context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
|
||||||
}
|
}
|
||||||
return checkType(result, expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(result, expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @NotNull JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) {
|
public void checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @NotNull JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) {
|
||||||
@@ -685,7 +685,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
"get",
|
"get",
|
||||||
receiver);
|
receiver);
|
||||||
if (functionDescriptor != null) {
|
if (functionDescriptor != null) {
|
||||||
return checkType(functionDescriptor.getReturnType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(functionDescriptor.getReturnType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -714,7 +714,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public JetType visitRootNamespaceExpression(JetRootNamespaceExpression expression, ExpressionTypingContext context) {
|
public JetType visitRootNamespaceExpression(JetRootNamespaceExpression expression, ExpressionTypingContext context) {
|
||||||
if (context.namespacesAllowed) {
|
if (context.namespacesAllowed) {
|
||||||
return ExpressionTypingUtils.checkType(JetModuleUtil.getRootNamespaceType(expression), expression, context);
|
return DataFlowUtils.checkType(JetModuleUtil.getRootNamespaceType(expression), expression, context);
|
||||||
}
|
}
|
||||||
context.trace.report(NAMESPACE_IS_NOT_AN_EXPRESSION.on(expression));
|
context.trace.report(NAMESPACE_IS_NOT_AN_EXPRESSION.on(expression));
|
||||||
return null;
|
return null;
|
||||||
@@ -764,7 +764,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (value[0] != CompileTimeConstantResolver.OUT_OF_RANGE) {
|
if (value[0] != CompileTimeConstantResolver.OUT_OF_RANGE) {
|
||||||
context.trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new StringValue(builder.toString()));
|
context.trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new StringValue(builder.toString()));
|
||||||
}
|
}
|
||||||
return checkType(context.semanticServices.getStandardLibrary().getStringType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(context.semanticServices.getStandardLibrary().getStringType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+3
-3
@@ -55,7 +55,7 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
ObservableBindingTrace traceAdapter = new ObservableBindingTrace(context.trace);
|
ObservableBindingTrace traceAdapter = new ObservableBindingTrace(context.trace);
|
||||||
traceAdapter.addHandler(CLASS, handler);
|
traceAdapter.addHandler(CLASS, handler);
|
||||||
TopDownAnalyzer.processObject(context.semanticServices, traceAdapter, context.scope, context.scope.getContainingDeclaration(), expression.getObjectDeclaration());
|
TopDownAnalyzer.processObject(context.semanticServices, traceAdapter, context.scope, context.scope.getContainingDeclaration(), expression.getObjectDeclaration());
|
||||||
return ExpressionTypingUtils.checkType(result[0], expression, context);
|
return DataFlowUtils.checkType(result[0], expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,11 +152,11 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetType expectedReturnType = JetStandardClasses.getReturnType(expectedType);
|
JetType expectedReturnType = JetStandardClasses.getReturnType(expectedType);
|
||||||
if (JetStandardClasses.isUnit(expectedReturnType)) {
|
if (JetStandardClasses.isUnit(expectedReturnType)) {
|
||||||
functionDescriptor.setReturnType(expectedReturnType);
|
functionDescriptor.setReturnType(expectedReturnType);
|
||||||
return ExpressionTypingUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return ExpressionTypingUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -65,8 +65,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetExpression thenBranch = expression.getThen();
|
JetExpression thenBranch = expression.getThen();
|
||||||
|
|
||||||
WritableScopeImpl thenScope = newWritableScopeImpl(context).setDebugName("Then scope");
|
WritableScopeImpl thenScope = newWritableScopeImpl(context).setDebugName("Then scope");
|
||||||
DataFlowInfo thenInfo = extractDataFlowInfoFromCondition(condition, true, thenScope, context);
|
DataFlowInfo thenInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, thenScope, context);
|
||||||
DataFlowInfo elseInfo = extractDataFlowInfoFromCondition(condition, false, null, context);
|
DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context);
|
||||||
|
|
||||||
if (elseBranch == null) {
|
if (elseBranch == null) {
|
||||||
if (thenBranch != null) {
|
if (thenBranch != null) {
|
||||||
@@ -75,7 +75,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
facade.setResultingDataFlowInfo(elseInfo);
|
facade.setResultingDataFlowInfo(elseInfo);
|
||||||
// resultScope = elseScope;
|
// resultScope = elseScope;
|
||||||
}
|
}
|
||||||
return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
facade.setResultingDataFlowInfo(thenInfo);
|
facade.setResultingDataFlowInfo(thenInfo);
|
||||||
// resultScope = thenScope;
|
// resultScope = thenScope;
|
||||||
}
|
}
|
||||||
return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
JetType thenType = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, contextWithExpectedType);
|
JetType thenType = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, contextWithExpectedType);
|
||||||
JetType elseType = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, contextWithExpectedType);
|
JetType elseType = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, contextWithExpectedType);
|
||||||
@@ -121,13 +121,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetExpression body = expression.getBody();
|
JetExpression body = expression.getBody();
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in while's condition");
|
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in while's condition");
|
||||||
DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context);
|
DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context);
|
||||||
getTypeWithNewScopeAndDataFlowInfo(scopeToExtend, body, conditionInfo, context);
|
getTypeWithNewScopeAndDataFlowInfo(scopeToExtend, body, conditionInfo, context);
|
||||||
}
|
}
|
||||||
if (!containsBreak(expression, context)) {
|
if (!containsBreak(expression, context)) {
|
||||||
facade.setResultingDataFlowInfo(extractDataFlowInfoFromCondition(condition, false, null, context));
|
facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context));
|
||||||
}
|
}
|
||||||
return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsBreak(final JetLoopExpression loopExpression, final ExpressionTypingContext context) {
|
private boolean containsBreak(final JetLoopExpression loopExpression, final ExpressionTypingContext context) {
|
||||||
@@ -178,9 +178,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetExpression condition = expression.getCondition();
|
JetExpression condition = expression.getCondition();
|
||||||
checkCondition(conditionScope, condition, context);
|
checkCondition(conditionScope, condition, context);
|
||||||
if (!containsBreak(expression, context)) {
|
if (!containsBreak(expression, context)) {
|
||||||
facade.setResultingDataFlowInfo(extractDataFlowInfoFromCondition(condition, false, null, context));
|
facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context));
|
||||||
}
|
}
|
||||||
return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -225,7 +225,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
facade.getType(body, context.replaceScope(loopScope));
|
facade.getType(body, context.replaceScope(loopScope));
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -372,7 +372,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetType type = facade.getType(thrownExpression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(context.scope));
|
JetType type = facade.getType(thrownExpression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(context.scope));
|
||||||
// TODO : check that it inherits Throwable
|
// TODO : check that it inherits Throwable
|
||||||
}
|
}
|
||||||
return checkType(JetStandardClasses.getNothingType(), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -395,19 +395,19 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.report(RETURN_TYPE_MISMATCH.on(expression, context.expectedReturnType));
|
context.trace.report(RETURN_TYPE_MISMATCH.on(expression, context.expectedReturnType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return checkType(JetStandardClasses.getNothingType(), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JetType visitBreakExpression(JetBreakExpression expression, ExpressionTypingContext context) {
|
public JetType visitBreakExpression(JetBreakExpression expression, ExpressionTypingContext context) {
|
||||||
context.labelResolver.recordLabel(expression, context);
|
context.labelResolver.recordLabel(expression, context);
|
||||||
return checkType(JetStandardClasses.getNothingType(), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JetType visitContinueExpression(JetContinueExpression expression, ExpressionTypingContext context) {
|
public JetType visitContinueExpression(JetContinueExpression expression, ExpressionTypingContext context) {
|
||||||
context.labelResolver.recordLabel(expression, context);
|
context.labelResolver.recordLabel(expression, context);
|
||||||
return checkType(JetStandardClasses.getNothingType(), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package org.jetbrains.jet.lang.types.expressions;
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.Ref;
|
||||||
|
import com.intellij.psi.tree.IElementType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
||||||
|
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||||
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
|
import org.jetbrains.jet.lang.types.JetTypeChecker;
|
||||||
|
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||||
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE;
|
||||||
|
import static org.jetbrains.jet.lang.diagnostics.Errors.TYPE_MISMATCH;
|
||||||
|
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author abreslav
|
||||||
|
*/
|
||||||
|
public class DataFlowUtils {
|
||||||
|
@NotNull
|
||||||
|
public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend, final ExpressionTypingContext context) {
|
||||||
|
if (condition == null) return context.dataFlowInfo;
|
||||||
|
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(context.dataFlowInfo);
|
||||||
|
condition.accept(new JetVisitorVoid() {
|
||||||
|
@Override
|
||||||
|
public void visitIsExpression(JetIsExpression expression) {
|
||||||
|
if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
|
||||||
|
JetPattern pattern = expression.getPattern();
|
||||||
|
result.set(context.patternsToDataFlowInfo.get(pattern));
|
||||||
|
if (scopeToExtend != null) {
|
||||||
|
List<VariableDescriptor> descriptors = context.patternsToBoundVariableLists.get(pattern);
|
||||||
|
if (descriptors != null) {
|
||||||
|
for (VariableDescriptor variableDescriptor : descriptors) {
|
||||||
|
scopeToExtend.addVariableDescriptor(variableDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBinaryExpression(JetBinaryExpression expression) {
|
||||||
|
IElementType operationToken = expression.getOperationToken();
|
||||||
|
if (operationToken == JetTokens.ANDAND || operationToken == JetTokens.OROR) {
|
||||||
|
WritableScope actualScopeToExtend;
|
||||||
|
if (operationToken == JetTokens.ANDAND) {
|
||||||
|
actualScopeToExtend = conditionValue ? scopeToExtend : null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
actualScopeToExtend = conditionValue ? null : scopeToExtend;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, actualScopeToExtend, context);
|
||||||
|
JetExpression expressionRight = expression.getRight();
|
||||||
|
if (expressionRight != null) {
|
||||||
|
DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, actualScopeToExtend, context);
|
||||||
|
DataFlowInfo.CompositionOperator operator;
|
||||||
|
if (operationToken == JetTokens.ANDAND) {
|
||||||
|
operator = conditionValue ? DataFlowInfo.AND : DataFlowInfo.OR;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
operator = conditionValue ? DataFlowInfo.OR : DataFlowInfo.AND;
|
||||||
|
}
|
||||||
|
dataFlowInfo = operator.compose(dataFlowInfo, rightInfo);
|
||||||
|
}
|
||||||
|
result.set(dataFlowInfo);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JetExpression left = expression.getLeft();
|
||||||
|
JetExpression right = expression.getRight();
|
||||||
|
if (right == null) return;
|
||||||
|
|
||||||
|
JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
|
||||||
|
if (lhsType == null) return;
|
||||||
|
JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
||||||
|
if (rhsType == null) return;
|
||||||
|
|
||||||
|
BindingContext bindingContext = context.trace.getBindingContext();
|
||||||
|
DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext);
|
||||||
|
DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext);
|
||||||
|
|
||||||
|
Boolean equals = null;
|
||||||
|
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
||||||
|
equals = true;
|
||||||
|
}
|
||||||
|
else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
|
||||||
|
equals = false;
|
||||||
|
}
|
||||||
|
if (equals != null) {
|
||||||
|
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
||||||
|
result.set(context.dataFlowInfo.equate(leftValue, rightValue));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitUnaryExpression(JetUnaryExpression expression) {
|
||||||
|
IElementType operationTokenType = expression.getOperationSign().getReferencedNameElementType();
|
||||||
|
if (operationTokenType == JetTokens.EXCL) {
|
||||||
|
JetExpression baseExpression = expression.getBaseExpression();
|
||||||
|
if (baseExpression != null) {
|
||||||
|
result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
|
||||||
|
JetExpression body = expression.getExpression();
|
||||||
|
if (body != null) {
|
||||||
|
body.accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (result.get() == null) {
|
||||||
|
return context.dataFlowInfo;
|
||||||
|
}
|
||||||
|
return result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context) {
|
||||||
|
if (expressionType == null || context.expectedType == null || context.expectedType == TypeUtils.NO_EXPECTED_TYPE ||
|
||||||
|
context.semanticServices.getTypeChecker().isSubtypeOf(expressionType, context.expectedType)) {
|
||||||
|
return expressionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext());
|
||||||
|
for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||||
|
if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) {
|
||||||
|
if (dataFlowValue.isStableIdentifier()) {
|
||||||
|
context.trace.record(AUTOCAST, expression, possibleType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
|
||||||
|
}
|
||||||
|
return possibleType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType));
|
||||||
|
return expressionType;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -128,7 +128,7 @@ public class ExpressionTypingServices {
|
|||||||
/*package*/ JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, @NotNull CoercionStrategy coercionStrategyForLastExpression, ExpressionTypingContext context) {
|
/*package*/ JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, @NotNull CoercionStrategy coercionStrategyForLastExpression, ExpressionTypingContext context) {
|
||||||
List<JetElement> block = expression.getStatements();
|
List<JetElement> block = expression.getStatements();
|
||||||
if (block.isEmpty()) {
|
if (block.isEmpty()) {
|
||||||
return ExpressionTypingUtils.checkType(JetStandardClasses.getUnitType(), expression, context);
|
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
|
DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
|
||||||
|
|||||||
+3
-196
@@ -1,6 +1,5 @@
|
|||||||
package org.jetbrains.jet.lang.types.expressions;
|
package org.jetbrains.jet.lang.types.expressions;
|
||||||
|
|
||||||
import com.intellij.openapi.util.Ref;
|
|
||||||
import com.intellij.psi.tree.IElementType;
|
import com.intellij.psi.tree.IElementType;
|
||||||
import com.intellij.psi.tree.TokenSet;
|
import com.intellij.psi.tree.TokenSet;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -10,25 +9,16 @@ import org.jetbrains.jet.lang.JetSemanticServices;
|
|||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||||
import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler;
|
import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||||
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.JetTypeChecker;
|
|
||||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
|
||||||
|
|
||||||
import java.util.List;
|
import static org.jetbrains.jet.lang.diagnostics.Errors.RESULT_TYPE_MISMATCH;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.MUST_BE_WRAPPED_IN_A_REF;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.MUST_BE_WRAPPED_IN_A_REF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,166 +90,6 @@ public class ExpressionTypingUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend, final ExpressionTypingContext context) {
|
|
||||||
if (condition == null) return context.dataFlowInfo;
|
|
||||||
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(context.dataFlowInfo);
|
|
||||||
condition.accept(new JetVisitorVoid() {
|
|
||||||
@Override
|
|
||||||
public void visitIsExpression(JetIsExpression expression) {
|
|
||||||
if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
|
|
||||||
JetPattern pattern = expression.getPattern();
|
|
||||||
result.set(context.patternsToDataFlowInfo.get(pattern));
|
|
||||||
if (scopeToExtend != null) {
|
|
||||||
List<VariableDescriptor> descriptors = context.patternsToBoundVariableLists.get(pattern);
|
|
||||||
if (descriptors != null) {
|
|
||||||
for (VariableDescriptor variableDescriptor : descriptors) {
|
|
||||||
scopeToExtend.addVariableDescriptor(variableDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitBinaryExpression(JetBinaryExpression expression) {
|
|
||||||
IElementType operationToken = expression.getOperationToken();
|
|
||||||
if (operationToken == JetTokens.ANDAND || operationToken == JetTokens.OROR) {
|
|
||||||
WritableScope actualScopeToExtend;
|
|
||||||
if (operationToken == JetTokens.ANDAND) {
|
|
||||||
actualScopeToExtend = conditionValue ? scopeToExtend : null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
actualScopeToExtend = conditionValue ? null : scopeToExtend;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, actualScopeToExtend, context);
|
|
||||||
JetExpression expressionRight = expression.getRight();
|
|
||||||
if (expressionRight != null) {
|
|
||||||
DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, actualScopeToExtend, context);
|
|
||||||
DataFlowInfo.CompositionOperator operator;
|
|
||||||
if (operationToken == JetTokens.ANDAND) {
|
|
||||||
operator = conditionValue ? DataFlowInfo.AND : DataFlowInfo.OR;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
operator = conditionValue ? DataFlowInfo.OR : DataFlowInfo.AND;
|
|
||||||
}
|
|
||||||
dataFlowInfo = operator.compose(dataFlowInfo, rightInfo);
|
|
||||||
}
|
|
||||||
result.set(dataFlowInfo);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JetExpression left = expression.getLeft();
|
|
||||||
JetExpression right = expression.getRight();
|
|
||||||
if (right == null) return;
|
|
||||||
|
|
||||||
JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
|
|
||||||
if (lhsType == null) return;
|
|
||||||
JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
|
||||||
if (rhsType == null) return;
|
|
||||||
|
|
||||||
BindingContext bindingContext = context.trace.getBindingContext();
|
|
||||||
DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext);
|
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext);
|
|
||||||
|
|
||||||
Boolean equals = null;
|
|
||||||
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
|
||||||
equals = true;
|
|
||||||
}
|
|
||||||
else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
|
|
||||||
equals = false;
|
|
||||||
}
|
|
||||||
if (equals != null) {
|
|
||||||
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
|
||||||
result.set(context.dataFlowInfo.equate(leftValue, rightValue));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (!(left instanceof JetSimpleNameExpression)) {
|
|
||||||
// JetExpression tmp = left;
|
|
||||||
// left = right;
|
|
||||||
// right = tmp;
|
|
||||||
//
|
|
||||||
// if (!(left instanceof JetSimpleNameExpression)) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), left);
|
|
||||||
// if (variableDescriptor == null) return;
|
|
||||||
//
|
|
||||||
// // TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc
|
|
||||||
//
|
|
||||||
// // Comparison to a non-null expression
|
|
||||||
// JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
|
||||||
// if (rhsType != null && !rhsType.isNullable()) {
|
|
||||||
// extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VariableDescriptor rightVariable = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), right);
|
|
||||||
// if (rightVariable != null) {
|
|
||||||
// JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
|
|
||||||
// if (lhsType != null && !lhsType.isNullable()) {
|
|
||||||
// extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Comparison to 'null'
|
|
||||||
// if (!(right instanceof JetConstantExpression)) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// JetConstantExpression constantExpression = (JetConstantExpression) right;
|
|
||||||
// if (constantExpression.getNode().getElementType() != JetNodeTypes.NULL) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extendDataFlowWithNullComparison(operationToken, variableDescriptor, conditionValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// private void extendDataFlowWithNullComparison(IElementType operationToken, @NotNull VariableDescriptor variableDescriptor, boolean equalsToNull) {
|
|
||||||
// boolean equality;
|
|
||||||
// if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
|
||||||
//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, !equalsToNull);
|
|
||||||
// equality = true;
|
|
||||||
// }
|
|
||||||
// else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
|
|
||||||
//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, equalsToNull);
|
|
||||||
// equality = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitUnaryExpression(JetUnaryExpression expression) {
|
|
||||||
IElementType operationTokenType = expression.getOperationSign().getReferencedNameElementType();
|
|
||||||
if (operationTokenType == JetTokens.EXCL) {
|
|
||||||
JetExpression baseExpression = expression.getBaseExpression();
|
|
||||||
if (baseExpression != null) {
|
|
||||||
result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
|
|
||||||
JetExpression body = expression.getExpression();
|
|
||||||
if (body != null) {
|
|
||||||
body.accept(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (result.get() == null) {
|
|
||||||
return context.dataFlowInfo;
|
|
||||||
}
|
|
||||||
return result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isTypeFlexible(@Nullable JetExpression expression) {
|
public static boolean isTypeFlexible(@Nullable JetExpression expression) {
|
||||||
if (expression == null) return false;
|
if (expression == null) return false;
|
||||||
|
|
||||||
@@ -269,29 +99,6 @@ public class ExpressionTypingUtils {
|
|||||||
).contains(expression.getNode().getElementType());
|
).contains(expression.getNode().getElementType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context) {
|
|
||||||
if (expressionType == null || context.expectedType == null || context.expectedType == TypeUtils.NO_EXPECTED_TYPE ||
|
|
||||||
context.semanticServices.getTypeChecker().isSubtypeOf(expressionType, context.expectedType)) {
|
|
||||||
return expressionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext());
|
|
||||||
for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
|
||||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) {
|
|
||||||
if (dataFlowValue.isStableIdentifier()) {
|
|
||||||
context.trace.record(AUTOCAST, expression, possibleType);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
|
|
||||||
}
|
|
||||||
return possibleType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType));
|
|
||||||
return expressionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkWrappingInRef(JetExpression expression, ExpressionTypingContext context) {
|
public static void checkWrappingInRef(JetExpression expression, ExpressionTypingContext context) {
|
||||||
if (!(expression instanceof JetSimpleNameExpression)) return;
|
if (!(expression instanceof JetSimpleNameExpression)) return;
|
||||||
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) expression;
|
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) expression;
|
||||||
|
|||||||
+1
-1
@@ -164,7 +164,7 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp
|
|||||||
"set", receiver);
|
"set", receiver);
|
||||||
if (functionDescriptor == null) return null;
|
if (functionDescriptor == null) return null;
|
||||||
context.trace.record(REFERENCE_TARGET, operationSign, functionDescriptor);
|
context.trace.record(REFERENCE_TARGET, operationSign, functionDescriptor);
|
||||||
return ExpressionTypingUtils.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context);
|
return DataFlowUtils.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo);
|
context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo);
|
||||||
context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables());
|
context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables());
|
||||||
}
|
}
|
||||||
return ExpressionTypingUtils.checkType(context.semanticServices.getStandardLibrary().getBooleanType(), expression, contextWithExpectedType);
|
return DataFlowUtils.checkType(context.semanticServices.getStandardLibrary().getBooleanType(), expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
fun test() {
|
||||||
|
val out : Int? = null
|
||||||
|
val x : Nothing? = null
|
||||||
|
if (out != x)
|
||||||
|
out.plus(1)
|
||||||
|
if (out == x) return
|
||||||
|
out.plus(1)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user