Avoid creating synthetic safe-call nodes when resolving == calls
Each of them leads to a separate synthetic file retained in the memory
This commit is contained in:
@@ -20,6 +20,7 @@ import com.intellij.lang.ASTNode;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.annotations.ReadOnly;
|
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.Receiver;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||||
|
|
||||||
@@ -31,6 +32,10 @@ public interface Call {
|
|||||||
@Nullable
|
@Nullable
|
||||||
ASTNode getCallOperationNode();
|
ASTNode getCallOperationNode();
|
||||||
|
|
||||||
|
default boolean isSemanticallyEquivalentToSafeCall() {
|
||||||
|
return getCallOperationNode() != null && getCallOperationNode().getElementType() == KtTokens.SAFE_ACCESS;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Receiver getExplicitReceiver();
|
Receiver getExplicitReceiver();
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.psi
|
package org.jetbrains.kotlin.psi
|
||||||
|
|
||||||
import com.intellij.lang.ASTNode
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.psi.PsiComment
|
import com.intellij.psi.PsiComment
|
||||||
@@ -65,10 +64,6 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
|
|||||||
return property.valOrVarKeyword
|
return property.valOrVarKeyword
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSafeCallNode(): ASTNode {
|
|
||||||
return (createExpression("a?.b") as KtSafeQualifiedExpression).operationTokenNode
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doCreateExpression(text: String): KtExpression? {
|
private fun doCreateExpression(text: String): KtExpression? {
|
||||||
//NOTE: '\n' below is important - some strange code indenting problems appear without it
|
//NOTE: '\n' below is important - some strange code indenting problems appear without it
|
||||||
return createProperty("val x =\n$text").initializer
|
return createProperty("val x =\n$text").initializer
|
||||||
|
|||||||
@@ -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.getEffectiveExpectedType
|
||||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
|
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
|
||||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers
|
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.callUtil.isSafeCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.*
|
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
|
// 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)
|
// Doing it simply as full subtyping check (receiverValueType <: receiverParameterType)
|
||||||
val call = candidateCall.call
|
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 expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type
|
||||||
|
|
||||||
val smartCastSubtypingResult = smartCastManager.getSmartCastReceiverResult(receiverArgument, expectedReceiverParameterType, this)
|
val smartCastSubtypingResult = smartCastManager.getSmartCastReceiverResult(receiverArgument, expectedReceiverParameterType, this)
|
||||||
|
|||||||
@@ -108,19 +108,34 @@ public class CallMaker {
|
|||||||
private final KtExpression calleeExpression;
|
private final KtExpression calleeExpression;
|
||||||
private final List<? extends ValueArgument> valueArguments;
|
private final List<? extends ValueArgument> valueArguments;
|
||||||
private final Call.CallType callType;
|
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<? extends ValueArgument> valueArguments) {
|
protected CallImpl(
|
||||||
this(callElement, explicitReceiver, callOperationNode, calleeExpression, valueArguments, CallType.DEFAULT);
|
@NotNull KtElement callElement,
|
||||||
|
@NotNull Receiver explicitReceiver,
|
||||||
|
@Nullable ASTNode callOperationNode,
|
||||||
|
@Nullable KtExpression calleeExpression,
|
||||||
|
@NotNull List<? extends ValueArgument> valueArguments
|
||||||
|
) {
|
||||||
|
this(callElement, explicitReceiver, callOperationNode, calleeExpression, valueArguments, CallType.DEFAULT, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CallImpl(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver, @Nullable ASTNode callOperationNode,
|
protected CallImpl(
|
||||||
@Nullable KtExpression calleeExpression, @NotNull List<? extends ValueArgument> valueArguments, @NotNull CallType callType) {
|
@NotNull KtElement callElement,
|
||||||
|
@Nullable Receiver explicitReceiver,
|
||||||
|
@Nullable ASTNode callOperationNode,
|
||||||
|
@Nullable KtExpression calleeExpression,
|
||||||
|
@NotNull List<? extends ValueArgument> valueArguments,
|
||||||
|
@NotNull CallType callType,
|
||||||
|
boolean isSemanticallyEquivalentToSafeCall
|
||||||
|
) {
|
||||||
this.callElement = callElement;
|
this.callElement = callElement;
|
||||||
this.explicitReceiver = explicitReceiver;
|
this.explicitReceiver = explicitReceiver;
|
||||||
this.callOperationNode = callOperationNode;
|
this.callOperationNode = callOperationNode;
|
||||||
this.calleeExpression = calleeExpression;
|
this.calleeExpression = calleeExpression;
|
||||||
this.valueArguments = valueArguments;
|
this.valueArguments = valueArguments;
|
||||||
this.callType = callType;
|
this.callType = callType;
|
||||||
|
this.isSemanticallyEquivalentToSafeCall = isSemanticallyEquivalentToSafeCall;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -128,6 +143,11 @@ public class CallMaker {
|
|||||||
return callOperationNode;
|
return callOperationNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSemanticallyEquivalentToSafeCall() {
|
||||||
|
return isSemanticallyEquivalentToSafeCall || Call.super.isSemanticallyEquivalentToSafeCall();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Receiver getExplicitReceiver() {
|
public Receiver getExplicitReceiver() {
|
||||||
@@ -194,13 +214,26 @@ public class CallMaker {
|
|||||||
public static Call makeCallWithExpressions(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver,
|
public static Call makeCallWithExpressions(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver,
|
||||||
@Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression,
|
@Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression,
|
||||||
@NotNull List<KtExpression> argumentExpressions) {
|
@NotNull List<KtExpression> argumentExpressions) {
|
||||||
return makeCallWithExpressions(callElement, explicitReceiver, callOperationNode, calleeExpression, argumentExpressions, CallType.DEFAULT);
|
return makeCallWithExpressions(callElement, explicitReceiver, callOperationNode, calleeExpression, argumentExpressions, CallType.DEFAULT,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Call makeCallWithExpressions(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver,
|
public static Call makeCallWithExpressions(@NotNull KtElement callElement, @Nullable Receiver explicitReceiver,
|
||||||
@Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression,
|
@Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression,
|
||||||
@NotNull List<KtExpression> argumentExpressions, @NotNull CallType callType) {
|
@NotNull List<KtExpression> 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<KtExpression> argumentExpressions, @NotNull CallType callType,
|
||||||
|
boolean isSemanticallyEquivalentToSafeCall
|
||||||
|
) {
|
||||||
List<ValueArgument> arguments;
|
List<ValueArgument> arguments;
|
||||||
if (argumentExpressions.isEmpty()) {
|
if (argumentExpressions.isEmpty()) {
|
||||||
arguments = Collections.emptyList();
|
arguments = Collections.emptyList();
|
||||||
@@ -211,7 +244,9 @@ public class CallMaker {
|
|||||||
arguments.add(makeValueArgument(argumentExpression, calleeExpression));
|
arguments.add(makeValueArgument(argumentExpression, calleeExpression));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return makeCall(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType);
|
return makeCall(
|
||||||
|
callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType, isSemanticallyEquivalentToSafeCall
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -222,8 +257,22 @@ public class CallMaker {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static Call makeCall(
|
public static Call makeCall(
|
||||||
KtElement callElement, @Nullable Receiver explicitReceiver, @Nullable ASTNode callOperationNode,
|
KtElement callElement, @Nullable Receiver explicitReceiver, @Nullable ASTNode callOperationNode,
|
||||||
KtExpression calleeExpression, List<? extends ValueArgument> arguments, CallType callType) {
|
KtExpression calleeExpression, List<? extends ValueArgument> arguments, CallType callType
|
||||||
return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, 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<? extends ValueArgument> arguments,
|
||||||
|
CallType callType,
|
||||||
|
boolean isSemanticallyEquivalentToSafeCall
|
||||||
|
) {
|
||||||
|
return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType, isSemanticallyEquivalentToSafeCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.resolve.calls.callUtil
|
|||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getTextWithLocation
|
import org.jetbrains.kotlin.psi.psiUtil.getTextWithLocation
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
@@ -237,15 +236,13 @@ val KtElement.isFakeElement: Boolean
|
|||||||
fun Call.isSafeCall(): Boolean {
|
fun Call.isSafeCall(): Boolean {
|
||||||
if (this is CallTransformer.CallForImplicitInvoke) {
|
if (this is CallTransformer.CallForImplicitInvoke) {
|
||||||
//implicit safe 'invoke'
|
//implicit safe 'invoke'
|
||||||
if (outerCall.isExplicitSafeCall()) {
|
if (outerCall.isSemanticallyEquivalentToSafeCall) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isExplicitSafeCall()
|
return isSemanticallyEquivalentToSafeCall
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Call.isExplicitSafeCall(): Boolean = callOperationNode?.elementType == KtTokens.SAFE_ACCESS
|
|
||||||
|
|
||||||
fun Call.isCallableReference(): Boolean {
|
fun Call.isCallableReference(): Boolean {
|
||||||
val callElement = callElement
|
val callElement = callElement
|
||||||
return callElement is KtNameReferenceExpression &&
|
return callElement is KtNameReferenceExpression &&
|
||||||
|
|||||||
+5
-3
@@ -1129,10 +1129,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
Call call = CallMaker.makeCallWithExpressions(
|
Call call = CallMaker.makeCallWithExpressions(
|
||||||
expression,
|
expression,
|
||||||
receiver,
|
receiver,
|
||||||
// semantically, a call to `==` is a safe call
|
null,
|
||||||
new KtPsiFactory(expression.getProject(), false).createSafeCallNode(),
|
|
||||||
operationSign,
|
operationSign,
|
||||||
Collections.singletonList(right)
|
Collections.singletonList(right),
|
||||||
|
Call.CallType.DEFAULT,
|
||||||
|
// semantically, a call to `==` is a safe call
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
OverloadResolutionResults<FunctionDescriptor> resolutionResults =
|
OverloadResolutionResults<FunctionDescriptor> resolutionResults =
|
||||||
|
|||||||
Reference in New Issue
Block a user