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:
Denis Zharkov
2017-11-16 13:40:45 +03:00
parent cc312bcaa2
commit ce41b5745a
6 changed files with 72 additions and 25 deletions
@@ -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 &&
@@ -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 =