'isSafeCall' method added to ResolvedCall

This commit is contained in:
Svetlana Isakova
2012-05-03 22:02:22 +04:00
parent 5846123de1
commit 335f0a6b5e
10 changed files with 82 additions and 36 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.CheckUtil;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
@@ -308,4 +309,9 @@ public class JetPsiUtil {
return "Unit".equals(typeReference.getText());
}
public static boolean isSafeCall(@NotNull Call call) {
ASTNode callOperationNode = call.getCallOperationNode();
return callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
}
}
@@ -184,6 +184,9 @@ public class CallResolver {
return checkArgumentTypesAndFail(context);
}
Collection<ResolutionCandidate<CallableDescriptor>> candidates = TaskPrioritizer.<CallableDescriptor>convertWithImpliedThis(context.scope, Collections.<ReceiverDescriptor>singletonList(NO_RECEIVER), constructors);
for (ResolutionCandidate<CallableDescriptor> candidate : candidates) {
candidate.setSafeCall(JetPsiUtil.isSafeCall(context.call));
}
prioritizedTasks.add(new ResolutionTask<CallableDescriptor, FunctionDescriptor>(candidates, functionReference, context)); // !! DataFlowInfo.EMPTY
}
else {
@@ -205,7 +208,7 @@ public class CallResolver {
context.trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn));
return checkArgumentTypesAndFail(context);
}
List<ResolutionCandidate<CallableDescriptor>> candidates = ResolutionCandidate.<CallableDescriptor>convertCollection(constructors);
List<ResolutionCandidate<CallableDescriptor>> candidates = ResolutionCandidate.<CallableDescriptor>convertCollection(constructors, JetPsiUtil.isSafeCall(context.call));
prioritizedTasks = Collections.singletonList(new ResolutionTask<CallableDescriptor, FunctionDescriptor>(candidates, functionReference, context)); // !! DataFlowInfo.EMPTY
}
else if (calleeExpression != null) {
@@ -222,7 +225,7 @@ public class CallResolver {
FunctionDescriptorImpl functionDescriptor = new ExpressionAsFunctionDescriptor(context.scope.getContainingDeclaration(), "[for expression " + calleeExpression.getText() + "]");
FunctionDescriptorUtil.initializeFromFunctionType(functionDescriptor, calleeType, NO_RECEIVER, Modality.FINAL, Visibilities.LOCAL);
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(functionDescriptor);
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(functionDescriptor, JetPsiUtil.isSafeCall(context.call));
resolutionCandidate.setReceiverArgument(context.call.getExplicitReceiver());
resolutionCandidate.setExplicitReceiverKind(ExplicitReceiverKind.RECEIVER_ARGUMENT);
@@ -328,7 +331,7 @@ public class CallResolver {
// Now, we try to remove this argument and see if it helps
Collection<ResolutionCandidate<D>> newCandidates = Lists.newArrayList();
for (ResolutionCandidate<D> candidate : task.getCandidates()) {
newCandidates.add(ResolutionCandidate.create(candidate.getDescriptor(), candidate.getExplicitReceiverKind()));
newCandidates.add(ResolutionCandidate.create(candidate.getDescriptor(), candidate.isSafeCall())); //todo check receivers are not necessary
}
ResolutionTask<D, F> newContext = new ResolutionTask<D, F>(newCandidates, task.reference, TemporaryBindingTrace.create(task.trace), task.scope, new DelegatingCall(task.call) {
@NotNull
@@ -628,31 +631,30 @@ public class CallResolver {
private <D extends CallableDescriptor, F extends D> ResolutionStatus checkAllValueArguments(CallResolutionContext<D, F> context) {
ResolutionStatus result = checkValueArgumentTypes(context);
ResolvedCall candidateCall = context.candidateCall;
ResolvedCall<D> candidateCall = context.candidateCall;
// Comment about a very special case.
// Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because
// both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'.
// Class 'CallForImplicitInvoke' helps up to recognise this case, and parameter 'implicitInvokeCheck' helps us to distinguish whether we check receiver or this object.
result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(),
result = result.combine(checkReceiver(context, candidateCall, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(),
candidateCall.getExplicitReceiverKind().isReceiver(), false));
result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(),
result = result.combine(checkReceiver(context, candidateCall, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(),
candidateCall.getExplicitReceiverKind().isThisObject(),
// for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' as callOperationNode if necessary
// for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error
context.call instanceof CallTransformer.CallForImplicitInvoke));
return result;
}
private <D extends CallableDescriptor, F extends D> ResolutionStatus checkReceiver(CallResolutionContext<D, F> context, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument,
private <D extends CallableDescriptor, F extends D> ResolutionStatus checkReceiver(CallResolutionContext<D, F> context, ResolvedCall<D> candidateCall,
ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument,
boolean isExplicitReceiver, boolean implicitInvokeCheck) {
ResolutionStatus result = SUCCESS;
if (receiverParameter.exists() && receiverArgument.exists()) {
ASTNode callOperationNode = context.call.getCallOperationNode();
boolean safeAccess = !implicitInvokeCheck && isExplicitReceiver && callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
JetType receiverArgumentType = receiverArgument.getType();
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.candidateCall.getTrace().getBindingContext());
if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) {
@@ -897,7 +899,7 @@ public class CallResolver {
String name, List<JetType> parameterTypes) {
List<ResolutionCandidate<FunctionDescriptor>> result = Lists.newArrayList();
if (receiver.exists()) {
Collection<ResolutionCandidate<FunctionDescriptor>> extensionFunctionDescriptors = ResolutionCandidate.convertCollection(scope.getFunctions(name));
Collection<ResolutionCandidate<FunctionDescriptor>> extensionFunctionDescriptors = ResolutionCandidate.convertCollection(scope.getFunctions(name), false);
List<ResolutionCandidate<FunctionDescriptor>> nonlocal = Lists.newArrayList();
List<ResolutionCandidate<FunctionDescriptor>> local = Lists.newArrayList();
TaskPrioritizer.splitLexicallyLocalDescriptors(extensionFunctionDescriptors, scope.getContainingDeclaration(), local, nonlocal);
@@ -907,7 +909,7 @@ public class CallResolver {
return result;
}
Collection<ResolutionCandidate<FunctionDescriptor>> functionDescriptors = ResolutionCandidate.convertCollection(receiver.getType().getMemberScope().getFunctions(name));
Collection<ResolutionCandidate<FunctionDescriptor>> functionDescriptors = ResolutionCandidate.convertCollection(receiver.getType().getMemberScope().getFunctions(name), false);
if (lookupExactSignature(functionDescriptors, parameterTypes, result)) {
return result;
@@ -916,7 +918,7 @@ public class CallResolver {
return result;
}
else {
lookupExactSignature(ResolutionCandidate.convertCollection(scope.getFunctions(name)), parameterTypes, result);
lookupExactSignature(ResolutionCandidate.convertCollection(scope.getFunctions(name), false), parameterTypes, result);
return result;
}
}
@@ -101,7 +101,7 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
candidate, variableCall, temporaryTrace, task);
ResolutionCandidate<CallableDescriptor> candidateWithoutReceiver = ResolutionCandidate.create(
candidate.getDescriptor(), candidate.getThisObject(), ReceiverDescriptor.NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
candidate.getDescriptor(), candidate.getThisObject(), ReceiverDescriptor.NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, false);
CallResolutionContext<CallableDescriptor, FunctionDescriptor> contextWithoutReceiver = createContextWithChainedTrace(
candidateWithoutReceiver, variableCallWithoutReceiver, temporaryTrace, task);
@@ -18,12 +18,16 @@ package org.jetbrains.jet.lang.resolve.calls;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.psi.Call;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.jet.lang.resolve.calls.ExplicitReceiverKind.NO_EXPLICIT_RECEIVER;
import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor.NO_RECEIVER;
/**
@@ -34,27 +38,28 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
private ReceiverDescriptor thisObject; // receiver object of a method
private ReceiverDescriptor receiverArgument; // receiver of an extension function
private ExplicitReceiverKind explicitReceiverKind;
private Boolean isSafeCall;
private ResolutionCandidate(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument) {
private ResolutionCandidate(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument,
@NotNull ExplicitReceiverKind explicitReceiverKind, @Nullable Boolean isSafeCall) {
this.candidateDescriptor = descriptor;
this.thisObject = thisObject;
this.receiverArgument = receiverArgument;
this.explicitReceiverKind = explicitReceiverKind;
this.isSafeCall = isSafeCall;
}
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor) {
return new ResolutionCandidate<D>(descriptor, NO_RECEIVER, NO_RECEIVER);
/*package*/ static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor) {
return new ResolutionCandidate<D>(descriptor, NO_RECEIVER, NO_RECEIVER, NO_EXPLICIT_RECEIVER, null);
}
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor, @NotNull ExplicitReceiverKind explicitReceiverKind) {
ResolutionCandidate<D> candidate = new ResolutionCandidate<D>(descriptor, NO_RECEIVER, NO_RECEIVER);
candidate.setExplicitReceiverKind(explicitReceiverKind);
return candidate;
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor, boolean isSafeCall) {
return create(descriptor, NO_RECEIVER, NO_RECEIVER, NO_EXPLICIT_RECEIVER, isSafeCall);
}
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind) {
ResolutionCandidate<D> resolutionCandidate = new ResolutionCandidate<D>(descriptor, thisObject, receiverArgument);
resolutionCandidate.setExplicitReceiverKind(explicitReceiverKind);
return resolutionCandidate;
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject,
@NotNull ReceiverDescriptor receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind, boolean isSafeCall) {
return new ResolutionCandidate<D>(descriptor, thisObject, receiverArgument, explicitReceiverKind, isSafeCall);
}
public void setThisObject(@NotNull ReceiverDescriptor thisObject) {
@@ -90,14 +95,21 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
}
@NotNull
public static <D extends CallableDescriptor> List<ResolutionCandidate<D>> convertCollection(@NotNull Collection<? extends D> descriptors) {
public static <D extends CallableDescriptor> List<ResolutionCandidate<D>> convertCollection(@NotNull Collection<? extends D> descriptors, boolean isSafeCall) {
List<ResolutionCandidate<D>> result = Lists.newArrayList();
for (D descriptor : descriptors) {
ResolutionCandidate<D> candidate = create(descriptor);
candidate.setExplicitReceiverKind(ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
result.add(candidate);
result.add(create(descriptor, isSafeCall));
}
return result;
}
public void setSafeCall(boolean safeCall) {
assert isSafeCall == null;
isSafeCall = safeCall;
}
public boolean isSafeCall() {
assert isSafeCall != null;
return isSafeCall;
}
}
@@ -21,9 +21,12 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import java.util.List;
@@ -35,6 +38,7 @@ public class ResolutionTaskHolder<D extends CallableDescriptor, F extends D> {
private final JetReferenceExpression reference;
private final BasicResolutionContext basicResolutionContext;
private final Predicate<ResolutionCandidate<D>> visibleStrategy;
private final boolean isSafeCall;
private final Collection<Collection<ResolutionCandidate<D>>> localExtensions = Sets.newLinkedHashSet();
private final Collection<Collection<ResolutionCandidate<D>>> members = Sets.newLinkedHashSet();
@@ -48,23 +52,31 @@ public class ResolutionTaskHolder<D extends CallableDescriptor, F extends D> {
this.reference = reference;
this.basicResolutionContext = basicResolutionContext;
this.visibleStrategy = visibleStrategy;
this.isSafeCall = JetPsiUtil.isSafeCall(basicResolutionContext.call);
}
public Collection<ResolutionCandidate<D>> setIsSafeCall(@NotNull Collection<ResolutionCandidate<D>> candidates) {
for (ResolutionCandidate<D> candidate : candidates) {
candidate.setSafeCall(isSafeCall);
}
return candidates;
}
public void addLocalExtensions(@NotNull Collection<ResolutionCandidate<D>> candidates) {
if (!candidates.isEmpty()) {
localExtensions.add(candidates);
localExtensions.add(setIsSafeCall(candidates));
}
}
public void addMembers(@NotNull Collection<ResolutionCandidate<D>> candidates) {
if (!candidates.isEmpty()) {
members.add(candidates);
members.add(setIsSafeCall(candidates));
}
}
public void addNonLocalExtensions(@NotNull Collection<ResolutionCandidate<D>> candidates) {
if (!candidates.isEmpty()) {
nonLocalExtensions.add(candidates);
nonLocalExtensions.add(setIsSafeCall(candidates));
}
}
@@ -61,4 +61,6 @@ public interface ResolvedCall<D extends CallableDescriptor> {
/** What's substituted for type parameters */
@NotNull
Map<TypeParameterDescriptor, JetType> getTypeArguments();
boolean isSafeCall();
}
@@ -61,6 +61,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
private final ReceiverDescriptor thisObject; // receiver object of a method
private final ReceiverDescriptor receiverArgument; // receiver of an extension function
private final ExplicitReceiverKind explicitReceiverKind;
private final boolean isSafeCall;
private final Map<TypeParameterDescriptor, JetType> typeArguments = Maps.newLinkedHashMap();
private final Map<ValueParameterDescriptor, JetType> autoCasts = Maps.newHashMap();
@@ -74,6 +75,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
this.thisObject = candidate.getThisObject();
this.receiverArgument = candidate.getReceiverArgument();
this.explicitReceiverKind = candidate.getExplicitReceiverKind();
this.isSafeCall = candidate.isSafeCall();
this.trace = trace;
}
@@ -186,4 +188,9 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
public Map<TypeParameterDescriptor, JetType> getTypeArguments() {
return typeArguments;
}
@Override
public boolean isSafeCall() {
return isSafeCall;
}
}
@@ -96,7 +96,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
}
};
ResolutionTaskHolder<D, F> result = new ResolutionTaskHolder<D, F>(functionReference, context, visibleStrategy );
ResolutionTaskHolder<D, F> result = new ResolutionTaskHolder<D, F>(functionReference, context, visibleStrategy);
for (CallableDescriptorCollector<? extends D> callableDescriptorCollector : callableDescriptorCollectors) {
doComputeTasks(scope, explicitReceiver, name, result, context, callableDescriptorCollector);
}
@@ -116,4 +116,9 @@ public class VariableAsFunctionResolvedCall implements ResolvedCallWithTrace<Fun
//functionCall.trace is temporary trace above variableCall.trace and is committed already
return variableCall.getTrace();
}
@Override
public boolean isSafeCall() {
return variableCall.isSafeCall();
}
}
@@ -107,7 +107,7 @@ public final class CallBuilder {
private CallTranslator finish() {
if (resolvedCall == null) {
assert descriptor != null;
resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER),
resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor, false),
TemporaryBindingTrace.create(new BindingTraceContext())); //todo
}
if (descriptor == null) {