diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index 8d012378264..bf967fee95b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -36,6 +36,9 @@ public interface BindingContext { WritableSlice LOOP_RANGE_HAS_NEXT = Slices.createSimpleSlice("LOOP_RANGE_HAS_NEXT"); WritableSlice LOOP_RANGE_NEXT = Slices.createSimpleSlice("LOOP_RANGE_NEXT"); + WritableSlice INDEXED_LVALUE_GET = Slices.createSimpleSlice("INDEXED_LVALUE_GET"); + WritableSlice INDEXED_LVALUE_SET = Slices.createSimpleSlice("INDEXED_LVALUE_SET"); + WritableSlice AUTOCAST = Slices.createSimpleSlice("AUTOCAST"); WritableSlice RESOLUTION_SCOPE = Slices.createSimpleSlice("RESOLUTION_SCOPE"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallMaker.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallMaker.java index 2ad04ca2b27..d9ce684a5d6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallMaker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallMaker.java @@ -165,12 +165,16 @@ public class CallMaker { return makeCall(expression, baseAsReceiver, null, expression.getOperationSign(), Collections.emptyList()); } - public static Call makeCall(@NotNull ReceiverDescriptor arrayAsReceiver,JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide) { + public static Call makeArraySetCall(@NotNull ReceiverDescriptor arrayAsReceiver, JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide) { List arguments = Lists.newArrayList(arrayAccessExpression.getIndexExpressions()); arguments.add(rightHandSide); return makeCallWithExpressions(arrayAccessExpression, arrayAsReceiver, null, arrayAccessExpression, arguments); } + public static Call makeArrayGetCall(@NotNull ReceiverDescriptor arrayAsReceiver, JetArrayAccessExpression arrayAccessExpression) { + return makeCallWithExpressions(arrayAccessExpression, arrayAsReceiver, null, arrayAccessExpression, arrayAccessExpression.getIndexExpressions()); + } + public static ValueArgument makeValueArgument(@NotNull JetExpression expression) { return makeValueArgument(expression, expression); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index aacb5b29402..13731b9f345 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -246,7 +246,7 @@ public class ExpressionTypingServices { } } if (mightBeUnit) { - // ExpressionTypingVisitorWithWritableScope should return only null or Unit for declarations and assignments + // ExpressionTypingVisitorForStatements should return only null or Unit for declarations and assignments assert result == null || JetStandardClasses.isUnit(result); result = JetStandardClasses.getUnitType(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java index 735f0e5e6b3..12ad4d0e258 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java @@ -38,7 +38,7 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor() { @Override public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) { - return new ExpressionTypingVisitorWithWritableScope(facade, writableScope); + return new ExpressionTypingVisitorForStatements(facade, writableScope); } }); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java similarity index 76% rename from compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java rename to compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java index 844f980e8b5..1eea9c06ae8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java @@ -11,24 +11,39 @@ import org.jetbrains.jet.lang.resolve.TopDownAnalyzer; import org.jetbrains.jet.lang.resolve.calls.CallMaker; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; -import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lexer.JetTokens; import static org.jetbrains.jet.lang.diagnostics.Errors.*; +import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_GET; +import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_SET; import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.getExpressionReceiver; /** * @author abreslav */ -public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTypingVisitor { +public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingVisitor { private final WritableScope scope; - public ExpressionTypingVisitorWithWritableScope(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope) { + public ExpressionTypingVisitorForStatements(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope) { super(facade); this.scope = scope; } + @Nullable + private JetType checkExpectedType(JetExpression expression, ExpressionTypingContext context) { + if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE) { + if (JetStandardClasses.isUnit(context.expectedType)) { + return JetStandardClasses.getUnitType(); + } + context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType)); + } + return null; + } + @Override public JetType visitObjectDeclaration(JetObjectDeclaration declaration, ExpressionTypingContext context) { TopDownAnalyzer.processObject(context.semanticServices, context.trace, scope, scope.getContainingDeclaration(), declaration); @@ -93,17 +108,24 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp @Override protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext context) { + // If there's += (or similar op) defined as such, we just call it. IElementType operationType = expression.getOperationReference().getReferencedNameElementType(); String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType); TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace); JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression); + // If there isn't, we call plus (or like) and then assign if (assignmentOperationType == null) { String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType)); JetType typeForBinaryCall = getTypeForBinaryCall(scope, counterpartName, context, expression); if (typeForBinaryCall != null) { + JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); + if (left instanceof JetArrayAccessExpression) { + resolveArrayAccessToLValue((JetArrayAccessExpression) left, expression.getRight(), expression.getOperationReference(), context, true); + } + context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression); ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context); } @@ -114,25 +136,14 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp return checkExpectedType(expression, context); } - @Nullable - private JetType checkExpectedType(JetExpression expression, ExpressionTypingContext context) { - if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE) { - if (JetStandardClasses.isUnit(context.expectedType)) { - return JetStandardClasses.getUnitType(); - } - context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType)); - } - return null; - } - @Override protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext context) { - JetExpression left = expression.getLeft(); - JetExpression deparenthesized = JetPsiUtil.deparenthesize(left); + boolean getterNeeded = false; + JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); JetExpression right = expression.getRight(); - if (deparenthesized instanceof JetArrayAccessExpression) { - JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) deparenthesized; - return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context); + if (left instanceof JetArrayAccessExpression) { + JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; + return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context, getterNeeded); } JetType leftType = facade.getType(left, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(scope)); if (right != null) { @@ -153,18 +164,30 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp return checkExpectedType(expression, context); } - private JetType resolveArrayAccessToLValue(JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide, JetSimpleNameExpression operationSign, ExpressionTypingContext context) { + private JetType resolveArrayAccessToLValue(JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide, JetSimpleNameExpression operationSign, ExpressionTypingContext context, boolean getterNeeded) { ExpressionReceiver receiver = getExpressionReceiver(facade, arrayAccessExpression.getArrayExpression(), context.replaceScope(scope)); if (receiver == null) return null; -// - Call call = CallMaker.makeCall(receiver, arrayAccessExpression, rightHandSide); - FunctionDescriptor functionDescriptor = context.replaceScope(scope).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).resolveCallWithGivenName( + + Call call = CallMaker.makeArraySetCall(receiver, arrayAccessExpression, rightHandSide); + FunctionDescriptor setFunction = context.replaceScope(scope).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).resolveCallWithGivenName( call, arrayAccessExpression, "set", receiver); - if (functionDescriptor == null) return null; - context.trace.record(REFERENCE_TARGET, operationSign, functionDescriptor); - return DataFlowUtils.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context); + if (setFunction == null) return null; + context.trace.record(INDEXED_LVALUE_SET, arrayAccessExpression, setFunction); + + if (getterNeeded) { + FunctionDescriptor getFunction = context.replaceScope(scope).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).resolveCallWithGivenName( + CallMaker.makeArrayGetCall(receiver, arrayAccessExpression), + arrayAccessExpression, + "get", receiver); + if (getFunction == null) return null; + context.trace.record(INDEXED_LVALUE_GET, arrayAccessExpression, getFunction); + } + else { + context.trace.record(REFERENCE_TARGET, operationSign, setFunction); + } + return DataFlowUtils.checkType(setFunction.getReturnType(), arrayAccessExpression, context); } @Override diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetArrayAccessReference.java b/idea/src/org/jetbrains/jet/plugin/references/JetArrayAccessReference.java index f59c7c7b02b..cdc32fa6d6b 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetArrayAccessReference.java +++ b/idea/src/org/jetbrains/jet/plugin/references/JetArrayAccessReference.java @@ -2,15 +2,20 @@ package org.jetbrains.jet.plugin.references; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.MultiRangeReference; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.psi.JetArrayAccessExpression; import org.jetbrains.jet.lang.psi.JetContainerNode; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; import java.util.ArrayList; import java.util.List; +import static org.jetbrains.jet.lang.resolve.BindingContext.*; + /** * @author yole */ @@ -32,6 +37,28 @@ class JetArrayAccessReference extends JetPsiReference implements MultiRangeRefer return getElement().getTextRange().shiftRight(-getElement().getTextOffset()); } + @Override + protected PsiElement doResolve() { + BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) getElement().getContainingFile()); + FunctionDescriptor getFunction = bindingContext.get(INDEXED_LVALUE_GET, expression); + FunctionDescriptor setFunction = bindingContext.get(INDEXED_LVALUE_SET, expression); + if (getFunction != null && setFunction != null) { + return null; // Call doMultiResolve + } + return super.doResolve(); + } + + @Override + protected ResolveResult[] doMultiResolve() { + BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) getElement().getContainingFile()); + FunctionDescriptor getFunction = bindingContext.get(INDEXED_LVALUE_GET, expression); + PsiElement getFunctionElement = bindingContext.get(DESCRIPTOR_TO_DECLARATION, getFunction); + FunctionDescriptor setFunction = bindingContext.get(INDEXED_LVALUE_SET, expression); + PsiElement setFunctionElement = bindingContext.get(DESCRIPTOR_TO_DECLARATION, setFunction); + return new ResolveResult[] {new PsiElementResolveResult(getFunctionElement, true), new PsiElementResolveResult(setFunctionElement, true)}; +// return super.doMultiResolve(); + } + @Override public List getRanges() { List list = new ArrayList();