KT-33 Provide method descriptors for both getter and setter when accessing collections via []

This commit is contained in:
Andrey Breslav
2011-11-02 13:05:37 +03:00
parent 93240981ad
commit d5266ddedd
6 changed files with 88 additions and 31 deletions
@@ -36,6 +36,9 @@ public interface BindingContext {
WritableSlice<JetExpression, CallableDescriptor> LOOP_RANGE_HAS_NEXT = Slices.createSimpleSlice("LOOP_RANGE_HAS_NEXT"); WritableSlice<JetExpression, CallableDescriptor> LOOP_RANGE_HAS_NEXT = Slices.createSimpleSlice("LOOP_RANGE_HAS_NEXT");
WritableSlice<JetExpression, FunctionDescriptor> LOOP_RANGE_NEXT = Slices.createSimpleSlice("LOOP_RANGE_NEXT"); WritableSlice<JetExpression, FunctionDescriptor> LOOP_RANGE_NEXT = Slices.createSimpleSlice("LOOP_RANGE_NEXT");
WritableSlice<JetExpression, FunctionDescriptor> INDEXED_LVALUE_GET = Slices.createSimpleSlice("INDEXED_LVALUE_GET");
WritableSlice<JetExpression, FunctionDescriptor> INDEXED_LVALUE_SET = Slices.createSimpleSlice("INDEXED_LVALUE_SET");
WritableSlice<JetExpression, JetType> AUTOCAST = Slices.createSimpleSlice("AUTOCAST"); WritableSlice<JetExpression, JetType> AUTOCAST = Slices.createSimpleSlice("AUTOCAST");
WritableSlice<JetExpression, JetScope> RESOLUTION_SCOPE = Slices.createSimpleSlice("RESOLUTION_SCOPE"); WritableSlice<JetExpression, JetScope> RESOLUTION_SCOPE = Slices.createSimpleSlice("RESOLUTION_SCOPE");
@@ -165,12 +165,16 @@ public class CallMaker {
return makeCall(expression, baseAsReceiver, null, expression.getOperationSign(), Collections.<ValueArgument>emptyList()); return makeCall(expression, baseAsReceiver, null, expression.getOperationSign(), Collections.<ValueArgument>emptyList());
} }
public static Call makeCall(@NotNull ReceiverDescriptor arrayAsReceiver,JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide) { public static Call makeArraySetCall(@NotNull ReceiverDescriptor arrayAsReceiver, JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide) {
List<JetExpression> arguments = Lists.newArrayList(arrayAccessExpression.getIndexExpressions()); List<JetExpression> arguments = Lists.newArrayList(arrayAccessExpression.getIndexExpressions());
arguments.add(rightHandSide); arguments.add(rightHandSide);
return makeCallWithExpressions(arrayAccessExpression, arrayAsReceiver, null, arrayAccessExpression, arguments); 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) { public static ValueArgument makeValueArgument(@NotNull JetExpression expression) {
return makeValueArgument(expression, expression); return makeValueArgument(expression, expression);
} }
@@ -246,7 +246,7 @@ public class ExpressionTypingServices {
} }
} }
if (mightBeUnit) { 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); assert result == null || JetStandardClasses.isUnit(result);
result = JetStandardClasses.getUnitType(); result = JetStandardClasses.getUnitType();
} }
@@ -38,7 +38,7 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, Expre
return new ExpressionTypingVisitorDispatcher(new Function<ExpressionTypingInternals, BasicExpressionTypingVisitor>() { return new ExpressionTypingVisitorDispatcher(new Function<ExpressionTypingInternals, BasicExpressionTypingVisitor>() {
@Override @Override
public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) { public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) {
return new ExpressionTypingVisitorWithWritableScope(facade, writableScope); return new ExpressionTypingVisitorForStatements(facade, writableScope);
} }
}); });
} }
@@ -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.calls.CallMaker;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; 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 org.jetbrains.jet.lexer.JetTokens;
import static org.jetbrains.jet.lang.diagnostics.Errors.*; 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.resolve.BindingContext.REFERENCE_TARGET;
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.getExpressionReceiver; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.getExpressionReceiver;
/** /**
* @author abreslav * @author abreslav
*/ */
public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTypingVisitor { public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingVisitor {
private final WritableScope scope; private final WritableScope scope;
public ExpressionTypingVisitorWithWritableScope(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope) { public ExpressionTypingVisitorForStatements(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope) {
super(facade); super(facade);
this.scope = scope; 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 @Override
public JetType visitObjectDeclaration(JetObjectDeclaration declaration, ExpressionTypingContext context) { public JetType visitObjectDeclaration(JetObjectDeclaration declaration, ExpressionTypingContext context) {
TopDownAnalyzer.processObject(context.semanticServices, context.trace, scope, scope.getContainingDeclaration(), declaration); TopDownAnalyzer.processObject(context.semanticServices, context.trace, scope, scope.getContainingDeclaration(), declaration);
@@ -93,17 +108,24 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp
@Override @Override
protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext context) { 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(); IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType); String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace); TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace);
JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression); JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression);
// If there isn't, we call plus (or like) and then assign
if (assignmentOperationType == null) { if (assignmentOperationType == null) {
String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType)); String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
JetType typeForBinaryCall = getTypeForBinaryCall(scope, counterpartName, context, expression); JetType typeForBinaryCall = getTypeForBinaryCall(scope, counterpartName, context, expression);
if (typeForBinaryCall != null) { 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); context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context); ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context);
} }
@@ -114,25 +136,14 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp
return checkExpectedType(expression, context); 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 @Override
protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext context) { protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext context) {
JetExpression left = expression.getLeft(); boolean getterNeeded = false;
JetExpression deparenthesized = JetPsiUtil.deparenthesize(left); JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
JetExpression right = expression.getRight(); JetExpression right = expression.getRight();
if (deparenthesized instanceof JetArrayAccessExpression) { if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) deparenthesized; JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context); return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context, getterNeeded);
} }
JetType leftType = facade.getType(left, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(scope)); JetType leftType = facade.getType(left, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(scope));
if (right != null) { if (right != null) {
@@ -153,18 +164,30 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp
return checkExpectedType(expression, context); 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)); ExpressionReceiver receiver = getExpressionReceiver(facade, arrayAccessExpression.getArrayExpression(), context.replaceScope(scope));
if (receiver == null) return null; if (receiver == null) return null;
//
Call call = CallMaker.makeCall(receiver, arrayAccessExpression, rightHandSide); Call call = CallMaker.makeArraySetCall(receiver, arrayAccessExpression, rightHandSide);
FunctionDescriptor functionDescriptor = context.replaceScope(scope).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).resolveCallWithGivenName( FunctionDescriptor setFunction = context.replaceScope(scope).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).resolveCallWithGivenName(
call, call,
arrayAccessExpression, arrayAccessExpression,
"set", receiver); "set", receiver);
if (functionDescriptor == null) return null; if (setFunction == null) return null;
context.trace.record(REFERENCE_TARGET, operationSign, functionDescriptor); context.trace.record(INDEXED_LVALUE_SET, arrayAccessExpression, setFunction);
return DataFlowUtils.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context);
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 @Override
@@ -2,15 +2,20 @@ package org.jetbrains.jet.plugin.references;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
import com.intellij.psi.MultiRangeReference; import com.intellij.psi.*;
import com.intellij.psi.PsiReference; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetArrayAccessExpression; import org.jetbrains.jet.lang.psi.JetArrayAccessExpression;
import org.jetbrains.jet.lang.psi.JetContainerNode; 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.lexer.JetTokens;
import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
/** /**
* @author yole * @author yole
*/ */
@@ -32,6 +37,28 @@ class JetArrayAccessReference extends JetPsiReference implements MultiRangeRefer
return getElement().getTextRange().shiftRight(-getElement().getTextOffset()); 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 @Override
public List<TextRange> getRanges() { public List<TextRange> getRanges() {
List<TextRange> list = new ArrayList<TextRange>(); List<TextRange> list = new ArrayList<TextRange>();