Merge remote branch 'origin/master'

This commit is contained in:
Andrey Breslav
2011-11-28 21:44:16 +03:00
9 changed files with 176 additions and 117 deletions
@@ -25,12 +25,11 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
@Override
public boolean newlineBeforeCurrentToken() {
if (!newlinesEnabled.peek()) return false;
if (eof()) return true;
// TODO: maybe, memorize this somehow?
// TODO: maybe, memoize this somehow?
for (int i = 1; i <= getCurrentOffset(); i++) {
IElementType previousToken = rawLookup(-i);
@@ -51,8 +50,9 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
assert previousTokenEnd < getOriginalText().length();
for (int j = previousTokenStart; j < previousTokenEnd; j++) {
if (getOriginalText().charAt(j) == '\n')
if (getOriginalText().charAt(j) == '\n') {
return true;
}
}
}
@@ -651,7 +651,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return DataFlowUtils.checkType(result, expression, context);
}
protected void checkLValue(BindingTrace trace, JetExpression expression) {
public void checkLValue(BindingTrace trace, JetExpression expression) {
checkLValue(trace, expression, false);
}
@@ -685,8 +685,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
}
else if (OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)) {
result = getTypeForBinaryCall(context.scope, OperatorConventions.BINARY_OPERATION_NAMES.get(operationType), context, expression);
}
result = getTypeForBinaryCall(context.scope, OperatorConventions.BINARY_OPERATION_NAMES.get(operationType), context, expression);
}
else if (operationType == JetTokens.EQ) {
result = visitAssignment(expression, contextWithExpectedType);
}
@@ -706,74 +706,77 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
}
}
else if (OperatorConventions.EQUALS_OPERATIONS.contains(operationType)) {
String name = "equals";
if (right != null) {
ExpressionReceiver receiver = ExpressionTypingUtils.safeGetExpressionReceiver(facade, left, context.replaceScope(context.scope));
OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveExactSignature(
receiver, "equals",
Collections.singletonList(JetStandardClasses.getNullableAnyType()));
if (resolutionResults.isSuccess()) {
FunctionDescriptor equals = resolutionResults.getResult().getResultingDescriptor();
context.trace.record(REFERENCE_TARGET, operationSign, equals);
if (ensureBooleanResult(operationSign, name, equals.getReturnType(), context)) {
ensureNonemptyIntersectionOfOperandTypes(expression, context);
}
}
else {
if (resolutionResults.isAmbiguity()) {
context.trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(operationSign, resolutionResults.getResults()));
else {
JetType booleanType = context.semanticServices.getStandardLibrary().getBooleanType();
if (OperatorConventions.EQUALS_OPERATIONS.contains(operationType)) {
String name = "equals";
if (right != null) {
ExpressionReceiver receiver = ExpressionTypingUtils.safeGetExpressionReceiver(facade, left, context.replaceScope(context.scope));
OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveExactSignature(
receiver, "equals",
Collections.singletonList(JetStandardClasses.getNullableAnyType()));
if (resolutionResults.isSuccess()) {
FunctionDescriptor equals = resolutionResults.getResult().getResultingDescriptor();
context.trace.record(REFERENCE_TARGET, operationSign, equals);
if (ensureBooleanResult(operationSign, name, equals.getReturnType(), context)) {
ensureNonemptyIntersectionOfOperandTypes(expression, context);
}
}
else {
context.trace.report(EQUALS_MISSING.on(operationSign));
if (resolutionResults.isAmbiguity()) {
context.trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(operationSign, resolutionResults.getResults()));
}
else {
context.trace.report(EQUALS_MISSING.on(operationSign));
}
}
}
result = booleanType;
}
else if (operationType == JetTokens.EQEQEQ || operationType == JetTokens.EXCLEQEQEQ) {
ensureNonemptyIntersectionOfOperandTypes(expression, context);
// TODO : Check comparison pointlessness
result = booleanType;
}
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
if (right == null) {
result = ErrorUtils.createErrorType("No right argument"); // TODO
return null;
}
checkInExpression(expression, expression.getOperationReference(), expression.getLeft(), expression.getRight(), context);
result = booleanType;
}
else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
WritableScopeImpl leftScope = newWritableScopeImpl(context).setDebugName("Left scope of && or ||");
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 ||");
JetType rightType = right == null ? null : facade.getType(right, context.replaceDataFlowInfo(flowInfoLeft).replaceScope(rightScope));
if (leftType != null && !isBoolean(context.semanticServices, leftType)) {
context.trace.report(TYPE_MISMATCH.on(left, booleanType, leftType));
}
if (rightType != null && !isBoolean(context.semanticServices, rightType)) {
context.trace.report(TYPE_MISMATCH.on(right, booleanType, rightType));
}
result = booleanType;
}
else if (operationType == JetTokens.ELVIS) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
JetType rightType = right == null ? null : facade.getType(right, contextWithExpectedType.replaceScope(context.scope));
if (leftType != null) {
if (!leftType.isNullable()) {
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
}
if (rightType != null) {
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
return TypeUtils.makeNullableAsSpecified(CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable());
}
}
}
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (operationType == JetTokens.EQEQEQ || operationType == JetTokens.EXCLEQEQEQ) {
ensureNonemptyIntersectionOfOperandTypes(expression, context);
// TODO : Check comparison pointlessness
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
if (right == null) {
result = ErrorUtils.createErrorType("No right argument"); // TODO
return null;
else {
context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
}
checkInExpression(expression, expression.getOperationReference(), expression.getLeft(), expression.getRight(), context);
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
WritableScopeImpl leftScope = newWritableScopeImpl(context).setDebugName("Left scope of && or ||");
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 ||");
JetType rightType = right == null ? null : facade.getType(right, context.replaceDataFlowInfo(flowInfoLeft).replaceScope(rightScope));
if (leftType != null && !isBoolean(context.semanticServices, leftType)) {
context.trace.report(TYPE_MISMATCH.on(left, context.semanticServices.getStandardLibrary().getBooleanType(), leftType));
}
if (rightType != null && !isBoolean(context.semanticServices, rightType)) {
context.trace.report(TYPE_MISMATCH.on(right, context.semanticServices.getStandardLibrary().getBooleanType(), rightType));
}
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (operationType == JetTokens.ELVIS) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
JetType rightType = right == null ? null : facade.getType(right, contextWithExpectedType.replaceScope(context.scope));
if (leftType != null) {
if (!leftType.isNullable()) {
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
}
if (rightType != null) {
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
return TypeUtils.makeNullableAsSpecified(CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable());
}
}
}
else {
context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
}
return DataFlowUtils.checkType(result, expression, contextWithExpectedType);
}
@@ -842,7 +845,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
@Nullable
protected JetType getTypeForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) {
public JetType getTypeForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) {
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
FunctionDescriptor functionDescriptor = context.replaceScope(scope).resolveCallWithGivenNameToDescriptor(
CallMaker.makeCall(receiver, binaryExpression),
@@ -10,10 +10,11 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.TaskPrioritizers;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
@@ -21,7 +22,10 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.jet.lang.types.*;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
@@ -36,10 +40,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
super(facade);
}
private JetType getTypeWithNewScopeAndDataFlowInfo(@NotNull JetScope scope, @NotNull JetExpression expression, @NotNull DataFlowInfo newDataFlowInfo, @NotNull ExpressionTypingContext context) {
return facade.getType(expression, context.replaceScope(scope).replaceDataFlowInfo(newDataFlowInfo));
}
private void checkCondition(@NotNull JetScope scope, @Nullable JetExpression condition, ExpressionTypingContext context) {
if (condition != null) {
JetType conditionType = facade.getType(condition, context.replaceScope(scope));
@@ -63,30 +63,29 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetExpression thenBranch = expression.getThen();
WritableScopeImpl thenScope = newWritableScopeImpl(context).setDebugName("Then scope");
WritableScopeImpl elseScope = newWritableScopeImpl(context).setDebugName("Else scope");
DataFlowInfo thenInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, thenScope, context);
DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context);
if (elseBranch == null) {
if (thenBranch != null) {
JetType type = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, context);
JetType type = context.getServices().getBlockReturnedTypeWithWritableScope(thenScope, Collections.singletonList(thenBranch), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(thenInfo));
if (type != null && JetStandardClasses.isNothing(type)) {
facade.setResultingDataFlowInfo(elseInfo);
// resultScope = elseScope;
}
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
}
return null;
}
if (thenBranch == null) {
JetType type = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, context);
JetType type = context.getServices().getBlockReturnedTypeWithWritableScope(elseScope, Collections.singletonList(elseBranch), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(elseInfo));
if (type != null && JetStandardClasses.isNothing(type)) {
facade.setResultingDataFlowInfo(thenInfo);
// resultScope = thenScope;
}
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
}
JetType thenType = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, contextWithExpectedType);
JetType elseType = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, contextWithExpectedType);
JetType thenType = context.getServices().getBlockReturnedTypeWithWritableScope(thenScope, Collections.singletonList(thenBranch), CoercionStrategy.NO_COERCION, contextWithExpectedType.replaceDataFlowInfo(thenInfo));
JetType elseType = context.getServices().getBlockReturnedTypeWithWritableScope(elseScope, Collections.singletonList(elseBranch), CoercionStrategy.NO_COERCION, contextWithExpectedType.replaceDataFlowInfo(elseInfo));
JetType result;
if (thenType == null) {
@@ -120,7 +119,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
if (body != null) {
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in while's condition");
DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context);
getTypeWithNewScopeAndDataFlowInfo(scopeToExtend, body, conditionInfo, context);
context.getServices().getBlockReturnedTypeWithWritableScope(scopeToExtend, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo));
}
if (!containsBreak(expression, context)) {
facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context));
@@ -229,8 +228,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetExpression body = expression.getBody();
if (body != null) {
ExpressionTypingInternals blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(loopScope);
blockLevelVisitor.getType(body, context.replaceScope(loopScope));
context.getServices().getBlockReturnedTypeWithWritableScope(loopScope, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context);
}
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
@@ -14,4 +14,7 @@ public interface ExpressionTypingFacade {
@Nullable
JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context);
@Nullable
JetType getTypeForStatement(@NotNull JetExpression expression, ExpressionTypingContext context);
}
@@ -218,13 +218,13 @@ public class ExpressionTypingServices {
final boolean[] mismatch = new boolean[1];
ObservableBindingTrace errorInterceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceExpectingUnit, statementExpression, mismatch);
newContext = createContext(newContext, errorInterceptingTrace, scope, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext);
result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
if (mismatch[0]) {
TemporaryBindingTrace temporaryTraceNoExpectedType = TemporaryBindingTrace.create(trace);
mismatch[0] = false;
ObservableBindingTrace interceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceNoExpectedType, statementExpression, mismatch);
newContext = createContext(newContext, interceptingTrace, scope, newContext.dataFlowInfo, NO_EXPECTED_TYPE, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext);
result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
if (mismatch[0]) {
temporaryTraceExpectingUnit.commit();
}
@@ -238,11 +238,11 @@ public class ExpressionTypingServices {
}
else {
newContext = createContext(newContext, trace, scope, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext);
result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
}
}
else {
result = blockLevelVisitor.getType(statementExpression, newContext);
result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
if (coercionStrategyForLastExpression == CoercionStrategy.COERCION_TO_UNIT) {
boolean mightBeUnit = false;
if (statementExpression instanceof JetDeclaration) {
@@ -264,7 +264,7 @@ public class ExpressionTypingServices {
}
}
else {
result = blockLevelVisitor.getType(statementExpression, newContext);
result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
}
DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo();
@@ -14,6 +14,7 @@ import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.util.lazy.ReenteringLazyValueComputationException;
import static org.jetbrains.jet.lang.diagnostics.Errors.ELSE_MISPLACED_IN_WHEN;
import static org.jetbrains.jet.lang.diagnostics.Errors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM;
/**
@@ -21,36 +22,33 @@ import static org.jetbrains.jet.lang.diagnostics.Errors.TYPECHECKER_HAS_RUN_INTO
*/
public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, ExpressionTypingContext> implements ExpressionTypingInternals {
private static Function<ExpressionTypingInternals, BasicExpressionTypingVisitor> BASIC_FACTORY = new Function<ExpressionTypingInternals, BasicExpressionTypingVisitor>() {
@Override
public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) {
return new BasicExpressionTypingVisitor(facade);
}
};
private static ExpressionTypingVisitorDispatcher BASIC_DISPATCHER = new ExpressionTypingVisitorDispatcher(null);
@NotNull
public static ExpressionTypingFacade create() {
return new ExpressionTypingVisitorDispatcher(BASIC_FACTORY);
return BASIC_DISPATCHER;
}
@NotNull
public static ExpressionTypingInternals createForBlock(final WritableScope writableScope) {
return new ExpressionTypingVisitorDispatcher(new Function<ExpressionTypingInternals, BasicExpressionTypingVisitor>() {
@Override
public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) {
return new ExpressionTypingVisitorForStatements(facade, writableScope);
}
});
return new ExpressionTypingVisitorDispatcher(writableScope);
}
private final BasicExpressionTypingVisitor basic;
private final ExpressionTypingVisitorForStatements statements;
private final ClosureExpressionsTypingVisitor closures = new ClosureExpressionsTypingVisitor(this);
private final ControlStructureTypingVisitor controlStructures = new ControlStructureTypingVisitor(this);
private final PatternMatchingTypingVisitor patterns = new PatternMatchingTypingVisitor(this);
protected DataFlowInfo resultDataFlowInfo;
private ExpressionTypingVisitorDispatcher(Function<ExpressionTypingInternals, BasicExpressionTypingVisitor> factoryForBasic) {
this.basic = factoryForBasic.fun(this);
private ExpressionTypingVisitorDispatcher(WritableScope writableScope) {
this.basic = new BasicExpressionTypingVisitor(this);
if (writableScope != null) {
this.statements = new ExpressionTypingVisitorForStatements(this, writableScope, basic);
}
else {
this.statements = null;
}
}
@Override
@@ -87,12 +85,23 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, Expre
@Override
@Nullable
public final JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context) {
return getType(expression, context, this);
}
@Override
@Nullable
public final JetType getTypeForStatement(@NotNull JetExpression expression, ExpressionTypingContext context) {
return getType(expression, context, statements);
}
@Nullable
private JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context, JetVisitor<JetType, ExpressionTypingContext> visitor) {
if (context.trace.get(BindingContext.PROCESSED, expression)) {
return context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
}
JetType result;
try {
result = expression.accept(this, context);
result = expression.accept(visitor, context);
// Some recursive definitions (object expressions) must put their types in the cache manually:
if (context.trace.get(BindingContext.PROCESSED, expression)) {
return context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
@@ -115,10 +124,10 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, Expre
context.trace.record(BindingContext.RESOLUTION_SCOPE, expression, context.scope);
}
context.trace.record(BindingContext.PROCESSED, expression);
return result;
}
return result;
}
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
@Override
public JetType visitFunctionLiteralExpression(JetFunctionLiteralExpression expression, ExpressionTypingContext data) {
@@ -27,12 +27,14 @@ import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.get
/**
* @author abreslav
*/
public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingVisitor {
public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisitor {
private final WritableScope scope;
private final BasicExpressionTypingVisitor basic;
public ExpressionTypingVisitorForStatements(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope) {
public ExpressionTypingVisitorForStatements(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope, BasicExpressionTypingVisitor basic) {
super(facade);
this.scope = scope;
this.basic = basic;
}
@Nullable
@@ -126,6 +128,22 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
}
@Override
public JetType visitBinaryExpression(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
JetSimpleNameExpression operationSign = expression.getOperationReference();
IElementType operationType = operationSign.getReferencedNameElementType();
JetType result;
if (operationType == JetTokens.EQ) {
result = visitAssignment(expression, contextWithExpectedType);
}
else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
result = visitAssignmentOperation(expression, contextWithExpectedType);
}
else {
return facade.getType(expression, contextWithExpectedType);
}
return DataFlowUtils.checkType(result, expression, contextWithExpectedType);
}
protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE);
// If there's += (or similar op) defined as such, we just call it.
@@ -134,7 +152,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace);
JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression);
JetType assignmentOperationType = basic.getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
// If there isn't, we call plus (or like) and then assign
@@ -145,7 +163,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context);
}
assignmentOperationType = getTypeForBinaryCall(scope, counterpartName, context, expression);
assignmentOperationType = basic.getTypeForBinaryCall(scope, counterpartName, context, expression);
if (assignmentOperationType != null) {
context.trace.record(VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context);
@@ -155,7 +173,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
temporaryBindingTrace.commit();
checkReassignment(expression, context, assignmentOperationType, left);
}
checkLValue(context.trace, expression.getLeft());
basic.checkLValue(context.trace, expression.getLeft());
return checkAssignmentType(assignmentOperationType, expression, contextWithExpectedType);
}
@@ -176,7 +194,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
}
}
@Override
protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
@@ -184,7 +202,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
JetType assignmentType = resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context);
checkLValue(context.trace, arrayAccessExpression);
basic.checkLValue(context.trace, arrayAccessExpression);
return checkAssignmentType(assignmentType, expression, contextWithExpectedType);
}
JetType leftType = facade.getType(expression.getLeft(), context.replaceScope(scope));
@@ -192,7 +210,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope));
}
if (leftType != null) { //if leftType == null, some another error has been generated
checkLValue(context.trace, expression.getLeft());
basic.checkLValue(context.trace, expression.getLeft());
}
if (left instanceof JetSimpleNameExpression) {
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left;
@@ -237,6 +255,11 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
return setFunctionDescriptor.getReturnType();
}
@Override
public JetType visitExpression(JetExpression expression, ExpressionTypingContext context) {
return facade.getType(expression, context);
}
@Override
public JetType visitJetElement(JetElement element, ExpressionTypingContext context) {
context.trace.report(UNSUPPORTED.on(element, "in a block"));
@@ -0,0 +1,23 @@
//KT-629 Assignments are parsed as expressions.
namespace kt629
class A() {
var p = "yeah"
fun mod(other : A) : A {
return A();
}
}
fun box() : Boolean {
var c = A()
val d = c;
c %= A();
return (c != d) && (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>c.p = "yeah"<!>)
}
fun box2() : Boolean {
var <!UNUSED_VARIABLE!>c<!> = A()
return (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>c.p = "yeah"<!>) && true
}
@@ -113,7 +113,7 @@ public class JetTypeCheckerTest extends JetLiteFixture {
assertType("if (true) 1 else '1'", "Any");
assertType("if (true) else '1'", "Unit");
assertType("if (true) else a = 0", "Unit");
assertType("if (true) else { var a = 0; a = 1 }", "Unit");
}
public void testWhen() throws Exception {