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.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();
@@ -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
@@ -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)
@@ -108,19 +108,34 @@ public class CallMaker {
private final KtExpression calleeExpression;
private final List<? extends ValueArgument> 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<? extends ValueArgument> 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<? extends ValueArgument> 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<? extends ValueArgument> valueArguments, @NotNull CallType callType) {
protected CallImpl(
@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.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<KtExpression> 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<KtExpression> argumentExpressions, @NotNull CallType callType) {
@Nullable ASTNode callOperationNode, @NotNull KtExpression calleeExpression,
@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;
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<? extends ValueArgument> arguments, CallType callType) {
return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType);
KtExpression calleeExpression, List<? extends ValueArgument> 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<? extends ValueArgument> arguments,
CallType callType,
boolean isSemanticallyEquivalentToSafeCall
) {
return new CallImpl(callElement, explicitReceiver, callOperationNode, calleeExpression, arguments, callType, isSemanticallyEquivalentToSafeCall);
}
@NotNull
@@ -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 &&
@@ -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<FunctionDescriptor> resolutionResults =