KT-1875 Safe call should be binded with receiver or this object (but not with both by default)
#KT-1875 fixed
This commit is contained in:
@@ -224,6 +224,7 @@ public class CallResolver {
|
||||
FunctionDescriptorUtil.initializeFromFunctionType(functionDescriptor, calleeType, NO_RECEIVER);
|
||||
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(functionDescriptor);
|
||||
resolutionCandidate.setReceiverArgument(context.call.getExplicitReceiver());
|
||||
resolutionCandidate.setExplicitReceiverKind(ExplicitReceiverKind.RECEIVER_ARGUMENT);
|
||||
|
||||
// strictly speaking, this is a hack:
|
||||
// we need to pass a reference, but there's no reference in the PSI,
|
||||
@@ -327,7 +328,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()));
|
||||
newCandidates.add(ResolutionCandidate.create(candidate.getDescriptor(), candidate.getExplicitReceiverKind()));
|
||||
}
|
||||
ResolutionTask<D, F> newContext = new ResolutionTask<D, F>(newCandidates, task.reference, TemporaryBindingTrace.create(task.trace), task.scope, new DelegatingCall(task.call) {
|
||||
@NotNull
|
||||
@@ -628,20 +629,35 @@ public class CallResolver {
|
||||
private <D extends CallableDescriptor, F extends D> ResolutionStatus checkAllValueArguments(CallResolutionContext<D, F> context) {
|
||||
ResolutionStatus result = checkValueArgumentTypes(context);
|
||||
ResolvedCall candidateCall = context.candidateCall;
|
||||
result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument()));
|
||||
result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject()));
|
||||
|
||||
// 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(),
|
||||
candidateCall.getExplicitReceiverKind().isReceiver(), false));
|
||||
|
||||
result = result.combine(checkReceiver(context, 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
|
||||
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, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument,
|
||||
boolean isExplicitReceiver, boolean implicitInvokeCheck) {
|
||||
|
||||
ResolutionStatus result = SUCCESS;
|
||||
if (receiverParameter.exists() && receiverArgument.exists()) {
|
||||
ASTNode callOperationNode = context.call.getCallOperationNode();
|
||||
boolean safeAccess = callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
|
||||
|
||||
boolean safeAccess = !implicitInvokeCheck && isExplicitReceiver && callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
|
||||
JetType receiverArgumentType = receiverArgument.getType();
|
||||
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.candidateCall.getTrace().getBindingContext());
|
||||
if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) {
|
||||
context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType);
|
||||
|
||||
context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType, implicitInvokeCheck);
|
||||
result = UNSAFE_CALL_ERROR;
|
||||
}
|
||||
else {
|
||||
@@ -653,7 +669,6 @@ public class CallResolver {
|
||||
result = OTHER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (safeAccess && (receiverParameter.getType().isNullable() || !receiverArgumentType.isNullable())) {
|
||||
context.tracing.unnecessarySafeCall(context.candidateCall.getTrace(), receiverArgumentType);
|
||||
}
|
||||
@@ -840,9 +855,6 @@ public class CallResolver {
|
||||
TemporaryBindingTrace temporaryTrace = result.getTrace();
|
||||
temporaryTrace.commit();
|
||||
|
||||
if (result instanceof VariableAsFunctionResolvedCall) {
|
||||
((VariableAsFunctionResolvedCall)result).getVariableCall().getTrace().commit();
|
||||
}
|
||||
return OverloadResolutionResultsImpl.success(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);
|
||||
candidate.getDescriptor(), candidate.getThisObject(), ReceiverDescriptor.NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
|
||||
|
||||
CallResolutionContext<CallableDescriptor, FunctionDescriptor> contextWithoutReceiver = createContextWithChainedTrace(
|
||||
candidateWithoutReceiver, variableCallWithoutReceiver, temporaryTrace, task);
|
||||
@@ -203,7 +203,7 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
|
||||
final JetSimpleNameExpression invokeExpression = (JetSimpleNameExpression) JetPsiFactory.createExpression(
|
||||
task.call.getCallElement().getProject(), "invoke");
|
||||
|
||||
return new DelegatingCall(task.call) {
|
||||
return new CallForImplicitInvoke(task.call) {
|
||||
@NotNull
|
||||
@Override
|
||||
public ReceiverDescriptor getExplicitReceiver() {
|
||||
@@ -236,4 +236,10 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public static class CallForImplicitInvoke extends DelegatingCall {
|
||||
public CallForImplicitInvoke(@NotNull Call delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
|
||||
/**
|
||||
* @author svtk
|
||||
*/
|
||||
public enum ExplicitReceiverKind {
|
||||
RECEIVER_ARGUMENT,
|
||||
THIS_OBJECT,
|
||||
NO_EXPLICIT_RECEIVER,
|
||||
|
||||
// A very special case.
|
||||
// In a call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' function 'invoke' has two explicit receivers:
|
||||
// 'b' (as receiver argument) and 'foo' (as this object).
|
||||
BOTH_RECEIVERS;
|
||||
|
||||
public boolean isReceiver() {
|
||||
return this == RECEIVER_ARGUMENT || this == BOTH_RECEIVERS;
|
||||
}
|
||||
|
||||
public boolean isThisObject() {
|
||||
return this == THIS_OBJECT || this == BOTH_RECEIVERS;
|
||||
}
|
||||
}
|
||||
+24
-4
@@ -33,6 +33,7 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
|
||||
private final D candidateDescriptor;
|
||||
private ReceiverDescriptor thisObject; // receiver object of a method
|
||||
private ReceiverDescriptor receiverArgument; // receiver of an extension function
|
||||
private ExplicitReceiverKind explicitReceiverKind;
|
||||
|
||||
private ResolutionCandidate(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument) {
|
||||
this.candidateDescriptor = descriptor;
|
||||
@@ -44,8 +45,16 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
|
||||
return new ResolutionCandidate<D>(descriptor, NO_RECEIVER, NO_RECEIVER);
|
||||
}
|
||||
|
||||
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument) {
|
||||
return new ResolutionCandidate<D>(descriptor, thisObject, receiverArgument);
|
||||
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, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind) {
|
||||
ResolutionCandidate<D> resolutionCandidate = new ResolutionCandidate<D>(descriptor, thisObject, receiverArgument);
|
||||
resolutionCandidate.setExplicitReceiverKind(explicitReceiverKind);
|
||||
return resolutionCandidate;
|
||||
}
|
||||
|
||||
public void setThisObject(@NotNull ReceiverDescriptor thisObject) {
|
||||
@@ -56,6 +65,10 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
|
||||
this.receiverArgument = receiverArgument;
|
||||
}
|
||||
|
||||
public void setExplicitReceiverKind(@NotNull ExplicitReceiverKind explicitReceiverKind) {
|
||||
this.explicitReceiverKind = explicitReceiverKind;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public D getDescriptor() {
|
||||
return candidateDescriptor;
|
||||
@@ -70,12 +83,19 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
|
||||
public ReceiverDescriptor getReceiverArgument() {
|
||||
return receiverArgument;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public ExplicitReceiverKind getExplicitReceiverKind() {
|
||||
return explicitReceiverKind;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <D extends CallableDescriptor> List<ResolutionCandidate<D>> convertCollection(@NotNull Collection<? extends D> descriptors) {
|
||||
List<ResolutionCandidate<D>> result = Lists.newArrayList();
|
||||
for (D descriptor : descriptors) {
|
||||
result.add(create(descriptor));
|
||||
ResolutionCandidate<D> candidate = create(descriptor);
|
||||
candidate.setExplicitReceiverKind(ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
|
||||
result.add(candidate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -195,9 +194,9 @@ public class ResolutionTask<D extends CallableDescriptor, F extends D> extends R
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type) {
|
||||
public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) {
|
||||
ASTNode callOperationNode = call.getCallOperationNode();
|
||||
if (callOperationNode != null) {
|
||||
if (callOperationNode != null && !isCallForImplicitInvoke) {
|
||||
trace.report(UNSAFE_CALL.on(callOperationNode.getPsi(), type));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -46,6 +46,10 @@ public interface ResolvedCall<D extends CallableDescriptor> {
|
||||
@NotNull
|
||||
ReceiverDescriptor getThisObject();
|
||||
|
||||
/** Determines whether receiver argument or this object is substituted for explicit receiver */
|
||||
@NotNull
|
||||
ExplicitReceiverKind getExplicitReceiverKind();
|
||||
|
||||
/** Values (arguments) for value parameters */
|
||||
@NotNull
|
||||
Map<ValueParameterDescriptor, ResolvedValueArgument> getValueArguments();
|
||||
|
||||
@@ -53,18 +53,14 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
|
||||
|
||||
@NotNull
|
||||
public static <D extends CallableDescriptor> ResolvedCallImpl<D> create(@NotNull ResolutionCandidate<D> candidate, @NotNull TemporaryBindingTrace trace) {
|
||||
return create(candidate.getDescriptor(), candidate.getThisObject(), candidate.getReceiverArgument(), trace);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <D extends CallableDescriptor> ResolvedCallImpl<D> create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull TemporaryBindingTrace trace) {
|
||||
return new ResolvedCallImpl<D>(descriptor, thisObject, receiverArgument, trace);
|
||||
return new ResolvedCallImpl<D>(candidate, trace);
|
||||
}
|
||||
|
||||
private final D candidateDescriptor;
|
||||
private D resultingDescriptor; // Probably substituted
|
||||
private final ReceiverDescriptor thisObject; // receiver object of a method
|
||||
private final ReceiverDescriptor receiverArgument; // receiver of an extension function
|
||||
private final ExplicitReceiverKind explicitReceiverKind;
|
||||
|
||||
private final Map<TypeParameterDescriptor, JetType> typeArguments = Maps.newLinkedHashMap();
|
||||
private final Map<ValueParameterDescriptor, JetType> autoCasts = Maps.newHashMap();
|
||||
@@ -73,13 +69,15 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
|
||||
private TemporaryBindingTrace trace;
|
||||
private ResolutionStatus status = UNKNOWN_STATUS;
|
||||
|
||||
private ResolvedCallImpl(@NotNull D candidateDescriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull TemporaryBindingTrace trace) {
|
||||
this.candidateDescriptor = candidateDescriptor;
|
||||
this.thisObject = thisObject;
|
||||
this.receiverArgument = receiverArgument;
|
||||
private ResolvedCallImpl(@NotNull ResolutionCandidate<D> candidate, @NotNull TemporaryBindingTrace trace) {
|
||||
this.candidateDescriptor = candidate.getDescriptor();
|
||||
this.thisObject = candidate.getThisObject();
|
||||
this.receiverArgument = candidate.getReceiverArgument();
|
||||
this.explicitReceiverKind = candidate.getExplicitReceiverKind();
|
||||
this.trace = trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ResolutionStatus getStatus() {
|
||||
return status;
|
||||
@@ -89,6 +87,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
|
||||
this.status = this.status.combine(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TemporaryBindingTrace getTrace() {
|
||||
return trace;
|
||||
@@ -138,6 +137,12 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
|
||||
return thisObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ExplicitReceiverKind getExplicitReceiverKind() {
|
||||
return explicitReceiverKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<ValueParameterDescriptor, ResolvedValueArgument> getValueArguments() {
|
||||
@@ -171,6 +176,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
|
||||
this.someArgumentHasNoType = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return someArgumentHasNoType;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,8 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
|
||||
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.trace.getBindingContext());
|
||||
List<ReceiverDescriptor> implicitReceivers = Lists.newArrayList();
|
||||
scope.getImplicitReceiversHierarchy(implicitReceivers);
|
||||
if (context.call.getThisObject().exists()) {
|
||||
boolean hasExplicitThisObject = context.call.getThisObject().exists();
|
||||
if (hasExplicitThisObject) {
|
||||
implicitReceivers.add(context.call.getThisObject());
|
||||
}
|
||||
if (receiver.exists()) {
|
||||
@@ -124,7 +125,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
|
||||
Collection<ResolutionCandidate<D>> members = Lists.newArrayList();
|
||||
for (ReceiverDescriptor variant : variantsForExplicitReceiver) {
|
||||
Collection<? extends D> membersForThisVariant = callableDescriptorCollector.getMembersByName(variant.getType(), name);
|
||||
convertWithReceivers(membersForThisVariant, Collections.singletonList(variant), Collections.singletonList(NO_RECEIVER), members);
|
||||
convertWithReceivers(membersForThisVariant, Collections.singletonList(variant), Collections.singletonList(NO_RECEIVER), members, hasExplicitThisObject);
|
||||
}
|
||||
|
||||
result.addLocalExtensions(locals);
|
||||
@@ -134,7 +135,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
|
||||
Collection<? extends D> memberExtensions = callableDescriptorCollector.getNonMembersByName(
|
||||
implicitReceiver.getType().getMemberScope(), name);
|
||||
List<ReceiverDescriptor> variantsForImplicitReceiver = autoCastService.getVariantsForReceiver(implicitReceiver);
|
||||
result.addNonLocalExtensions(convertWithReceivers(memberExtensions, variantsForImplicitReceiver, variantsForExplicitReceiver));
|
||||
result.addNonLocalExtensions(convertWithReceivers(memberExtensions, variantsForImplicitReceiver, variantsForExplicitReceiver, hasExplicitThisObject));
|
||||
}
|
||||
|
||||
result.addNonLocalExtensions(nonlocals);
|
||||
@@ -157,20 +158,26 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
private static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithReceivers(Collection<? extends D> descriptors, Iterable<ReceiverDescriptor> thisObjects, Iterable<ReceiverDescriptor> receiverParameters) {
|
||||
private static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithReceivers(Collection<? extends D> descriptors, Iterable<ReceiverDescriptor> thisObjects,
|
||||
Iterable<ReceiverDescriptor> receiverParameters, boolean hasExplicitThisObject) {
|
||||
|
||||
Collection<ResolutionCandidate<D>> result = Lists.newArrayList();
|
||||
convertWithReceivers(descriptors, thisObjects, receiverParameters, result);
|
||||
convertWithReceivers(descriptors, thisObjects, receiverParameters, result, hasExplicitThisObject);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <D extends CallableDescriptor> void convertWithReceivers(Collection<? extends D> descriptors, Iterable<ReceiverDescriptor> thisObjects, Iterable<ReceiverDescriptor> receiverParameters, Collection<ResolutionCandidate<D>> result) {
|
||||
private static <D extends CallableDescriptor> void convertWithReceivers(Collection<? extends D> descriptors, Iterable<ReceiverDescriptor> thisObjects, Iterable<ReceiverDescriptor> receiverParameters,
|
||||
Collection<ResolutionCandidate<D>> result, boolean hasExplicitThisObject) {
|
||||
|
||||
for (ReceiverDescriptor thisObject : thisObjects) {
|
||||
for (ReceiverDescriptor receiverParameter : receiverParameters) {
|
||||
for (D extension : descriptors) {
|
||||
ResolutionCandidate<D> resolvedCall = ResolutionCandidate.create(extension);
|
||||
resolvedCall.setThisObject(thisObject);
|
||||
resolvedCall.setReceiverArgument(receiverParameter);
|
||||
result.add(resolvedCall);
|
||||
ResolutionCandidate<D> candidate = ResolutionCandidate.create(extension);
|
||||
candidate.setThisObject(thisObject);
|
||||
candidate.setReceiverArgument(receiverParameter);
|
||||
candidate.setExplicitReceiverKind(
|
||||
hasExplicitThisObject ? ExplicitReceiverKind.BOTH_RECEIVERS : ExplicitReceiverKind.THIS_OBJECT);
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,10 +187,12 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
|
||||
Collection<ResolutionCandidate<D>> result = Lists.newArrayList();
|
||||
for (ReceiverDescriptor receiverParameter : receiverParameters) {
|
||||
for (D descriptor : descriptors) {
|
||||
ResolutionCandidate<D> resolvedCall = ResolutionCandidate.create(descriptor);
|
||||
resolvedCall.setReceiverArgument(receiverParameter);
|
||||
if (setImpliedThis(scope, resolvedCall)) {
|
||||
result.add(resolvedCall);
|
||||
ResolutionCandidate<D> candidate = ResolutionCandidate.create(descriptor);
|
||||
candidate.setReceiverArgument(receiverParameter);
|
||||
candidate.setExplicitReceiverKind(
|
||||
receiverParameter.exists() ? ExplicitReceiverKind.RECEIVER_ARGUMENT : ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
|
||||
if (setImpliedThis(scope, candidate)) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,24 +204,25 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor
|
||||
containingDeclaration = containingDeclaration.getContainingDeclaration();
|
||||
}
|
||||
if (containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.OBJECT) {
|
||||
ResolutionCandidate<D> resolvedCall = ResolutionCandidate.create(descriptor);
|
||||
resolvedCall.setThisObject(new ClassReceiver((ClassDescriptor) containingDeclaration));
|
||||
result.add(resolvedCall);
|
||||
ResolutionCandidate<D> candidate = ResolutionCandidate.create(descriptor);
|
||||
candidate.setThisObject(new ClassReceiver((ClassDescriptor) containingDeclaration));
|
||||
candidate.setExplicitReceiverKind(ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <D extends CallableDescriptor> boolean setImpliedThis(@NotNull JetScope scope, ResolutionCandidate<D> resolvedCall) {
|
||||
ReceiverDescriptor expectedThisObject = resolvedCall.getDescriptor().getExpectedThisObject();
|
||||
private static <D extends CallableDescriptor> boolean setImpliedThis(@NotNull JetScope scope, ResolutionCandidate<D> candidate) {
|
||||
ReceiverDescriptor expectedThisObject = candidate.getDescriptor().getExpectedThisObject();
|
||||
if (!expectedThisObject.exists()) return true;
|
||||
List<ReceiverDescriptor> receivers = Lists.newArrayList();
|
||||
scope.getImplicitReceiversHierarchy(receivers);
|
||||
for (ReceiverDescriptor receiver : receivers) {
|
||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(receiver.getType(), expectedThisObject.getType())) {
|
||||
// TODO : Autocasts & nullability
|
||||
resolvedCall.setThisObject(expectedThisObject);
|
||||
candidate.setThisObject(expectedThisObject);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ import java.util.List;
|
||||
public void typeInferenceFailed(@NotNull BindingTrace trace, SolutionStatus status) {}
|
||||
|
||||
@Override
|
||||
public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type) {}
|
||||
public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) {}
|
||||
|
||||
@Override
|
||||
public void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type) {}
|
||||
@@ -108,7 +108,7 @@ import java.util.List;
|
||||
|
||||
void typeInferenceFailed(@NotNull BindingTrace trace, SolutionStatus status);
|
||||
|
||||
void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type);
|
||||
void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke);
|
||||
|
||||
void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type);
|
||||
|
||||
|
||||
+6
@@ -73,6 +73,12 @@ public class VariableAsFunctionResolvedCall implements ResolvedCallWithTrace<Fun
|
||||
return variableCall.getThisObject();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ExplicitReceiverKind getExplicitReceiverKind() {
|
||||
return variableCall.getExplicitReceiverKind();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<ValueParameterDescriptor, ResolvedValueArgument> getValueArguments() {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//KT-1875 Safe call should be binded with receiver or this object (but not with both by default)
|
||||
|
||||
package kt1875
|
||||
|
||||
fun f(a : Int?, b : Int.(Int)->Int) = a?.b(1) //unnecessary safe call warning
|
||||
|
||||
trait T {
|
||||
val f : ((i: Int) -> Unit)?
|
||||
}
|
||||
|
||||
fun test(t: T) {
|
||||
t.<!UNSAFE_CALL!>f<!>(1) //unsafe call error
|
||||
t.f?.invoke(1)
|
||||
}
|
||||
|
||||
fun test1(t: T?) {
|
||||
t<!UNSAFE_CALL!>.<!><!UNSAFE_CALL!>f<!>(1)
|
||||
t?.<!UNSAFE_CALL!>f<!>(1)
|
||||
t<!UNSAFE_CALL!>.<!>f?.invoke(1)
|
||||
t?.f?.invoke(1)
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTraceContext;
|
||||
import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ExplicitReceiverKind;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolutionCandidate;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl;
|
||||
@@ -106,7 +107,7 @@ public final class CallBuilder {
|
||||
private CallTranslator finish() {
|
||||
if (resolvedCall == null) {
|
||||
assert descriptor != null;
|
||||
resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor),
|
||||
resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER),
|
||||
TemporaryBindingTrace.create(new BindingTraceContext())); //todo
|
||||
}
|
||||
if (descriptor == null) {
|
||||
|
||||
Reference in New Issue
Block a user