diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java index e38843bd970..6856c9cbb9a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java @@ -20,6 +20,7 @@ import com.intellij.lang.ASTNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.ReadOnly; +import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; @@ -31,6 +32,10 @@ public interface Call { @Nullable ASTNode getCallOperationNode(); + default boolean isSemanticallyEquivalentToSafeCall() { + return getCallOperationNode() != null && getCallOperationNode().getElementType() == KtTokens.SAFE_ACCESS; + } + @Nullable Receiver getExplicitReceiver(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt index 62218d4896e..9a2dff7ff55 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.psi -import com.intellij.lang.ASTNode import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.psi.PsiComment @@ -65,10 +64,6 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m return property.valOrVarKeyword } - fun createSafeCallNode(): ASTNode { - return (createExpression("a?.b") as KtSafeQualifiedExpression).operationTokenNode - } - private fun doCreateExpression(text: String): KtExpression? { //NOTE: '\n' below is important - some strange code indenting problems appear without it return createProperty("val x =\n$text").initializer diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index 4dcf3bd601b..8bef9953d1f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -32,7 +32,6 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode. import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers -import org.jetbrains.kotlin.resolve.calls.callUtil.isExplicitSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker import org.jetbrains.kotlin.resolve.calls.context.* @@ -508,7 +507,7 @@ class CandidateResolver( // Here we know that receiver is OK ignoring nullability and check that nullability is OK too // Doing it simply as full subtyping check (receiverValueType <: receiverParameterType) val call = candidateCall.call - val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall() + val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isSemanticallyEquivalentToSafeCall val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type val smartCastSubtypingResult = smartCastManager.getSmartCastReceiverResult(receiverArgument, expectedReceiverParameterType, this) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java index 27fc4953e52..efccc9d8545 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java @@ -108,19 +108,34 @@ public class CallMaker { private final KtExpression calleeExpression; private final List valueArguments; private final Call.CallType callType; + private final boolean isSemanticallyEquivalentToSafeCall; - protected CallImpl(@NotNull KtElement callElement, @NotNull Receiver explicitReceiver, @Nullable ASTNode callOperationNode, @Nullable KtExpression calleeExpression, @NotNull List valueArguments) { - this(callElement, explicitReceiver, callOperationNode, calleeExpression, valueArguments, CallType.DEFAULT); + protected CallImpl( + @NotNull KtElement callElement, + @NotNull Receiver explicitReceiver, + @Nullable ASTNode callOperationNode, + @Nullable KtExpression calleeExpression, + @NotNull List valueArguments + ) { + this(callElement, explicitReceiver, callOperationNode, calleeExpression, valueArguments, CallType.DEFAULT, false); } - protected CallImpl(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver, @Nullable ASTNode callOperationNode, - @Nullable KtExpression calleeExpression, @NotNull List valueArguments, @NotNull CallType callType) { + protected CallImpl( + @NotNull KtElement callElement, + @Nullable Receiver explicitReceiver, + @Nullable ASTNode callOperationNode, + @Nullable KtExpression calleeExpression, + @NotNull List valueArguments, + @NotNull CallType callType, + boolean isSemanticallyEquivalentToSafeCall + ) { this.callElement = callElement; this.explicitReceiver = explicitReceiver; this.callOperationNode = callOperationNode; this.calleeExpression = calleeExpression; this.valueArguments = valueArguments; this.callType = callType; + this.isSemanticallyEquivalentToSafeCall = isSemanticallyEquivalentToSafeCall; } @Override @@ -128,6 +143,11 @@ public class CallMaker { return callOperationNode; } + @Override + public boolean isSemanticallyEquivalentToSafeCall() { + return isSemanticallyEquivalentToSafeCall || Call.super.isSemanticallyEquivalentToSafeCall(); + } + @Nullable @Override public Receiver getExplicitReceiver() { @@ -194,13 +214,26 @@ public class CallMaker { public static Call makeCallWithExpressions(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver, @Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression, @NotNull List argumentExpressions) { - return makeCallWithExpressions(callElement, explicitReceiver, callOperationNode, calleeExpression, argumentExpressions, CallType.DEFAULT); + return makeCallWithExpressions(callElement, explicitReceiver, callOperationNode, calleeExpression, argumentExpressions, CallType.DEFAULT, + false); } @NotNull public static Call makeCallWithExpressions(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver, - @Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression, - @NotNull List argumentExpressions, @NotNull CallType callType) { + @Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression, + @NotNull List argumentExpressions, @NotNull CallType callType) { + return makeCallWithExpressions(callElement, explicitReceiver, callOperationNode, calleeExpression, argumentExpressions, callType, + false); + } + + + @NotNull + public static Call makeCallWithExpressions( + @NotNull KtElement callElement, @Nullable Receiver explicitReceiver, + @Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression, + @NotNull List argumentExpressions, @NotNull CallType callType, + boolean isSemanticallyEquivalentToSafeCall + ) { List arguments; if (argumentExpressions.isEmpty()) { arguments = Collections.emptyList(); @@ -211,7 +244,9 @@ public class CallMaker { arguments.add(makeValueArgument(argumentExpression, calleeExpression)); } } - return makeCall(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType); + return makeCall( + callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType, isSemanticallyEquivalentToSafeCall + ); } @NotNull @@ -222,8 +257,22 @@ public class CallMaker { @NotNull public static Call makeCall( KtElement callElement, @Nullable Receiver explicitReceiver, @Nullable ASTNode callOperationNode, - KtExpression calleeExpression, List arguments, CallType callType) { - return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType); + KtExpression calleeExpression, List arguments, CallType callType + ) { + return makeCall(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType, false); + } + + @NotNull + public static Call makeCall( + KtElement callElement, + @Nullable Receiver explicitReceiver, + @Nullable ASTNode callOperationNode, + KtExpression calleeExpression, + List arguments, + CallType callType, + boolean isSemanticallyEquivalentToSafeCall + ) { + return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType, isSemanticallyEquivalentToSafeCall); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt index 63d02d0ab04..ce22d7cb5d6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.resolve.calls.callUtil import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.KotlinLookupLocation -import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getTextWithLocation import org.jetbrains.kotlin.resolve.BindingContext @@ -237,15 +236,13 @@ val KtElement.isFakeElement: Boolean fun Call.isSafeCall(): Boolean { if (this is CallTransformer.CallForImplicitInvoke) { //implicit safe 'invoke' - if (outerCall.isExplicitSafeCall()) { + if (outerCall.isSemanticallyEquivalentToSafeCall) { return true } } - return isExplicitSafeCall() + return isSemanticallyEquivalentToSafeCall } -fun Call.isExplicitSafeCall(): Boolean = callOperationNode?.elementType == KtTokens.SAFE_ACCESS - fun Call.isCallableReference(): Boolean { val callElement = callElement return callElement is KtNameReferenceExpression && diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 195fcd57bf8..da33205599b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -1129,10 +1129,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { Call call = CallMaker.makeCallWithExpressions( expression, receiver, - // semantically, a call to `==` is a safe call - new KtPsiFactory(expression.getProject(), false).createSafeCallNode(), + null, operationSign, - Collections.singletonList(right) + Collections.singletonList(right), + Call.CallType.DEFAULT, + // semantically, a call to `==` is a safe call + true ); OverloadResolutionResults resolutionResults =