'isSafeCall' method added to ResolvedCall
This commit is contained in:
@@ -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);
|
||||
|
||||
+28
-16
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -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);
|
||||
}
|
||||
|
||||
+5
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user