diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java index e0ba360e177..cde638bb6a7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -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; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 2cd629a531a..222d0143e38 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -184,6 +184,9 @@ public class CallResolver { return checkArgumentTypesAndFail(context); } Collection> candidates = TaskPrioritizer.convertWithImpliedThis(context.scope, Collections.singletonList(NO_RECEIVER), constructors); + for (ResolutionCandidate candidate : candidates) { + candidate.setSafeCall(JetPsiUtil.isSafeCall(context.call)); + } prioritizedTasks.add(new ResolutionTask(candidates, functionReference, context)); // !! DataFlowInfo.EMPTY } else { @@ -205,7 +208,7 @@ public class CallResolver { context.trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn)); return checkArgumentTypesAndFail(context); } - List> candidates = ResolutionCandidate.convertCollection(constructors); + List> candidates = ResolutionCandidate.convertCollection(constructors, JetPsiUtil.isSafeCall(context.call)); prioritizedTasks = Collections.singletonList(new ResolutionTask(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 resolutionCandidate = ResolutionCandidate.create(functionDescriptor); + ResolutionCandidate resolutionCandidate = ResolutionCandidate.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> newCandidates = Lists.newArrayList(); for (ResolutionCandidate 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 newContext = new ResolutionTask(newCandidates, task.reference, TemporaryBindingTrace.create(task.trace), task.scope, new DelegatingCall(task.call) { @NotNull @@ -628,31 +631,30 @@ public class CallResolver { private ResolutionStatus checkAllValueArguments(CallResolutionContext context) { ResolutionStatus result = checkValueArgumentTypes(context); - ResolvedCall candidateCall = context.candidateCall; + ResolvedCall 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 ResolutionStatus checkReceiver(CallResolutionContext context, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, + private ResolutionStatus checkReceiver(CallResolutionContext context, ResolvedCall 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 parameterTypes) { List> result = Lists.newArrayList(); if (receiver.exists()) { - Collection> extensionFunctionDescriptors = ResolutionCandidate.convertCollection(scope.getFunctions(name)); + Collection> extensionFunctionDescriptors = ResolutionCandidate.convertCollection(scope.getFunctions(name), false); List> nonlocal = Lists.newArrayList(); List> local = Lists.newArrayList(); TaskPrioritizer.splitLexicallyLocalDescriptors(extensionFunctionDescriptors, scope.getContainingDeclaration(), local, nonlocal); @@ -907,7 +909,7 @@ public class CallResolver { return result; } - Collection> functionDescriptors = ResolutionCandidate.convertCollection(receiver.getType().getMemberScope().getFunctions(name)); + Collection> 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; } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java index 529daf39f6e..71563284a29 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java @@ -101,7 +101,7 @@ public class CallTransformer { candidate, variableCall, temporaryTrace, task); ResolutionCandidate 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 contextWithoutReceiver = createContextWithChainedTrace( candidateWithoutReceiver, variableCallWithoutReceiver, temporaryTrace, task); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java index 029e96f71aa..03531d5fff0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java @@ -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 { 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 ResolutionCandidate create(@NotNull D descriptor) { - return new ResolutionCandidate(descriptor, NO_RECEIVER, NO_RECEIVER); + /*package*/ static ResolutionCandidate create(@NotNull D descriptor) { + return new ResolutionCandidate(descriptor, NO_RECEIVER, NO_RECEIVER, NO_EXPLICIT_RECEIVER, null); } - public static ResolutionCandidate create(@NotNull D descriptor, @NotNull ExplicitReceiverKind explicitReceiverKind) { - ResolutionCandidate candidate = new ResolutionCandidate(descriptor, NO_RECEIVER, NO_RECEIVER); - candidate.setExplicitReceiverKind(explicitReceiverKind); - return candidate; + public static ResolutionCandidate create(@NotNull D descriptor, boolean isSafeCall) { + return create(descriptor, NO_RECEIVER, NO_RECEIVER, NO_EXPLICIT_RECEIVER, isSafeCall); } - public static ResolutionCandidate create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind) { - ResolutionCandidate resolutionCandidate = new ResolutionCandidate(descriptor, thisObject, receiverArgument); - resolutionCandidate.setExplicitReceiverKind(explicitReceiverKind); - return resolutionCandidate; + public static ResolutionCandidate create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, + @NotNull ReceiverDescriptor receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind, boolean isSafeCall) { + return new ResolutionCandidate(descriptor, thisObject, receiverArgument, explicitReceiverKind, isSafeCall); } public void setThisObject(@NotNull ReceiverDescriptor thisObject) { @@ -90,14 +95,21 @@ public class ResolutionCandidate { } @NotNull - public static List> convertCollection(@NotNull Collection descriptors) { + public static List> convertCollection(@NotNull Collection descriptors, boolean isSafeCall) { List> result = Lists.newArrayList(); for (D descriptor : descriptors) { - ResolutionCandidate 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; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTaskHolder.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTaskHolder.java index 5054e683c22..46cd6ee02cf 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTaskHolder.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTaskHolder.java @@ -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 { private final JetReferenceExpression reference; private final BasicResolutionContext basicResolutionContext; private final Predicate> visibleStrategy; + private final boolean isSafeCall; private final Collection>> localExtensions = Sets.newLinkedHashSet(); private final Collection>> members = Sets.newLinkedHashSet(); @@ -48,23 +52,31 @@ public class ResolutionTaskHolder { this.reference = reference; this.basicResolutionContext = basicResolutionContext; this.visibleStrategy = visibleStrategy; + this.isSafeCall = JetPsiUtil.isSafeCall(basicResolutionContext.call); + } + + public Collection> setIsSafeCall(@NotNull Collection> candidates) { + for (ResolutionCandidate candidate : candidates) { + candidate.setSafeCall(isSafeCall); + } + return candidates; } public void addLocalExtensions(@NotNull Collection> candidates) { if (!candidates.isEmpty()) { - localExtensions.add(candidates); + localExtensions.add(setIsSafeCall(candidates)); } } public void addMembers(@NotNull Collection> candidates) { if (!candidates.isEmpty()) { - members.add(candidates); + members.add(setIsSafeCall(candidates)); } } public void addNonLocalExtensions(@NotNull Collection> candidates) { if (!candidates.isEmpty()) { - nonLocalExtensions.add(candidates); + nonLocalExtensions.add(setIsSafeCall(candidates)); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java index 09319f2120a..26dd0013cab 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java @@ -61,4 +61,6 @@ public interface ResolvedCall { /** What's substituted for type parameters */ @NotNull Map getTypeArguments(); + + boolean isSafeCall(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java index f01bafae443..23c387d8ac7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java @@ -61,6 +61,7 @@ public class ResolvedCallImpl 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 typeArguments = Maps.newLinkedHashMap(); private final Map autoCasts = Maps.newHashMap(); @@ -74,6 +75,7 @@ public class ResolvedCallImpl 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 implements ResolvedC public Map getTypeArguments() { return typeArguments; } + + @Override + public boolean isSafeCall() { + return isSafeCall; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java index 33f970a8110..c8dec20fb25 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java @@ -96,7 +96,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor } }; - ResolutionTaskHolder result = new ResolutionTaskHolder(functionReference, context, visibleStrategy ); + ResolutionTaskHolder result = new ResolutionTaskHolder(functionReference, context, visibleStrategy); for (CallableDescriptorCollector callableDescriptorCollector : callableDescriptorCollectors) { doComputeTasks(scope, explicitReceiver, name, result, context, callableDescriptorCollector); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java index a314992c3dd..4572075a285 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java @@ -116,4 +116,9 @@ public class VariableAsFunctionResolvedCall implements ResolvedCallWithTrace