Set the right call operation node for implicit invoke

Make a call for implicit invoke safe if an outer call is safe
This commit is contained in:
Svetlana Isakova
2014-10-29 15:08:30 +03:00
parent 7f62675665
commit 36fd8a1a08
14 changed files with 162 additions and 82 deletions
@@ -336,11 +336,6 @@ public class JetPsiUtil {
return KotlinBuiltIns.getInstance().getUnit().getName().asString().equals(typeReference.getText());
}
public static boolean isSafeCall(@NotNull Call call) {
ASTNode callOperationNode = call.getCallOperationNode();
return callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS;
}
// SCRIPT: is declaration in script?
public static boolean isScriptDeclaration(@NotNull JetDeclaration namedDeclaration) {
return getScript(namedDeclaration) != null;
@@ -37,6 +37,7 @@ import org.jetbrains.jet.lang.types.expressions.OperatorConventions
import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.PsiComment
import org.jetbrains.jet.lang.resolve.calls.CallTransformer.CallForImplicitInvoke
public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? {
val calleeExpression = getCalleeExpression()
@@ -377,4 +378,16 @@ public fun PsiElement.nextLeafSkipWhitespacesAndComments(): PsiElement? {
}
public fun JetExpression.isDotReceiver(): Boolean =
(getParent() as? JetDotQualifiedExpression)?.getReceiverExpression() == this
(getParent() as? JetDotQualifiedExpression)?.getReceiverExpression() == this
public fun Call.isSafeCall(): Boolean {
if (this is CallForImplicitInvoke) {
//implicit safe 'invoke'
if (getOuterCall().isExplicitSafeCall()) {
return true
}
}
return isExplicitSafeCall()
}
public fun Call.isExplicitSafeCall(): Boolean = getCallOperationNode()?.getElementType() == JetTokens.SAFE_ACCESS
@@ -287,8 +287,8 @@ public class CallResolver {
context.trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn));
return checkArgumentTypesAndFail(context);
}
List<ResolutionCandidate<CallableDescriptor>> candidates = ResolutionCandidate.<CallableDescriptor>convertCollection(
context.call, constructors, JetPsiUtil.isSafeCall(context.call));
List<ResolutionCandidate<CallableDescriptor>> candidates =
ResolutionCandidate.<CallableDescriptor>convertCollection(context.call, constructors);
prioritizedTasks = Collections.singletonList(new ResolutionTask<CallableDescriptor, FunctionDescriptor>(candidates, functionReference, context)); // !! DataFlowInfo.EMPTY
}
else if (calleeExpression != null) {
@@ -19,7 +19,9 @@ package org.jetbrains.jet.lang.resolve.calls;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
@@ -105,10 +107,17 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
boolean hasReceiver = candidate.getExtensionReceiver().exists();
Call variableCall = stripCallArguments(task.call);
ResolutionCandidate<CallableDescriptor> variableCandidate = getVariableCallCandidate(candidate, variableCall);
ResolutionCandidate<CallableDescriptor> variableCandidate = ResolutionCandidate.create(
variableCall,
candidate.getDescriptor(),
candidate.getDispatchReceiver(),
candidate.getExtensionReceiver(),
candidate.getExplicitReceiverKind()
);
if (!hasReceiver) {
CallCandidateResolutionContext<CallableDescriptor> context = CallCandidateResolutionContext.create(
ResolvedCallImpl.create(variableCandidate, candidateTrace, task.tracing, task.dataFlowInfoForArguments), task, candidateTrace, task.tracing, variableCall);
ResolvedCallImpl.create(variableCandidate, candidateTrace, task.tracing, task.dataFlowInfoForArguments),
task, candidateTrace, task.tracing, variableCall);
return Collections.singleton(context);
}
CallCandidateResolutionContext<CallableDescriptor> contextWithReceiver = createContextWithChainedTrace(
@@ -116,11 +125,11 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
Call variableCallWithoutReceiver = stripReceiver(variableCall);
ResolutionCandidate<CallableDescriptor> candidateWithoutReceiver = ResolutionCandidate.create(
variableCandidate.getCall(),
variableCandidate.getDescriptor(),
variableCandidate.getDispatchReceiver(),
variableCallWithoutReceiver,
candidate.getDescriptor(),
candidate.getDispatchReceiver(),
ReceiverValue.NO_RECEIVER,
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, false);
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
CallCandidateResolutionContext<CallableDescriptor> contextWithoutReceiver = createContextWithChainedTrace(
candidateWithoutReceiver, variableCallWithoutReceiver, candidateTrace, task, variableCall.getExplicitReceiver());
@@ -137,20 +146,6 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
return CallCandidateResolutionContext.create(resolvedCall, task, chainedTrace, task.tracing, call, receiverValue);
}
@NotNull
private ResolutionCandidate<CallableDescriptor> getVariableCallCandidate(
@NotNull ResolutionCandidate<CallableDescriptor> candidate,
@NotNull Call variableCall
) {
return ResolutionCandidate.create(
variableCall,
candidate.getDescriptor(),
candidate.getDispatchReceiver(),
candidate.getExtensionReceiver(),
candidate.getExplicitReceiverKind(),
candidate.isSafeCall());
}
private Call stripCallArguments(@NotNull Call call) {
return new DelegatingCall(call) {
@Override
@@ -194,6 +189,12 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
private Call stripReceiver(@NotNull Call variableCall) {
return new DelegatingCall(variableCall) {
@Nullable
@Override
public ASTNode getCallOperationNode() {
return null;
}
@NotNull
@Override
public ReceiverValue getExplicitReceiver() {
@@ -250,10 +251,10 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
};
public static class CallForImplicitInvoke extends DelegatingCall {
final Call outerCall;
final ReceiverValue explicitExtensionReceiver;
final ExpressionReceiver calleeExpressionAsDispatchReceiver;
final JetSimpleNameExpression fakeInvokeExpression;
private final Call outerCall;
private final ReceiverValue explicitExtensionReceiver;
private final ExpressionReceiver calleeExpressionAsDispatchReceiver;
private final JetSimpleNameExpression fakeInvokeExpression;
public CallForImplicitInvoke(
@NotNull ReceiverValue explicitExtensionReceiver,
@@ -267,6 +268,15 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
this.fakeInvokeExpression = (JetSimpleNameExpression) JetPsiFactory(call.getCallElement()).createExpression( "invoke");
}
@Nullable
@Override
public ASTNode getCallOperationNode() {
// if an explicit receiver corresponds to the implicit invoke, there is a corresponding call operation node:
// a.b() or a?.b() (where b has an extension function type);
// otherwise it's implicit
return explicitExtensionReceiver.exists() ? super.getCallOperationNode() : null;
}
@NotNull
@Override
public ReceiverValue getExplicitReceiver() {
@@ -284,12 +294,6 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
return fakeInvokeExpression;
}
@NotNull
@Override
public JetElement getCallElement() {
return outerCall.getCallElement();
}
@NotNull
@Override
public CallType getCallType() {
@@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.context.*;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
@@ -583,7 +584,7 @@ public class CandidateResolver {
D candidateDescriptor = candidateCall.getCandidateDescriptor();
if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS;
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && PsiUtilPackage.isExplicitSafeCall(candidateCall.getCall());
boolean isSubtypeBySmartCast = SmartCastUtils.isSubTypeBySmartCastIgnoringNullability(
receiverArgument, receiverParameter.getType(), context);
if (!isSubtypeBySmartCast) {
@@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.Call;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage;
import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace;
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
@@ -37,7 +38,9 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeProjection;
import org.jetbrains.jet.lang.types.TypeSubstitutor;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE;
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.UNKNOWN_STATUS;
@@ -74,7 +77,6 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
private final ReceiverValue dispatchReceiver; // receiver object of a method
private final ReceiverValue extensionReceiver; // receiver of an extension function
private final ExplicitReceiverKind explicitReceiverKind;
private final boolean isSafeCall;
private final Map<TypeParameterDescriptor, JetType> typeArguments = Maps.newLinkedHashMap();
private final Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = Maps.newLinkedHashMap();
@@ -99,7 +101,6 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
this.dispatchReceiver = candidate.getDispatchReceiver();
this.extensionReceiver = candidate.getExtensionReceiver();
this.explicitReceiverKind = candidate.getExplicitReceiverKind();
this.isSafeCall = candidate.isSafeCall();
this.trace = trace;
this.tracing = tracing;
this.dataFlowInfoForArguments = dataFlowInfoForArguments;
@@ -285,7 +286,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
@Override
public boolean isSafeCall() {
return isSafeCall;
return PsiUtilPackage.isSafeCall(call);
}
@NotNull
@@ -18,7 +18,6 @@ package org.jetbrains.jet.lang.resolve.calls.tasks;
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.resolve.scopes.receivers.ReceiverValue;
@@ -34,36 +33,29 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
private ReceiverValue dispatchReceiver; // receiver object of a method
private ReceiverValue extensionReceiver; // receiver of an extension function
private ExplicitReceiverKind explicitReceiverKind;
private Boolean isSafeCall;
private ResolutionCandidate(
@NotNull Call call, @NotNull D descriptor, @NotNull ReceiverValue dispatchReceiver,
@NotNull ReceiverValue extensionReceiver, @NotNull ExplicitReceiverKind explicitReceiverKind, @Nullable Boolean isSafeCall
@NotNull ReceiverValue extensionReceiver, @NotNull ExplicitReceiverKind explicitReceiverKind
) {
this.call = call;
this.candidateDescriptor = descriptor;
this.dispatchReceiver = dispatchReceiver;
this.extensionReceiver = extensionReceiver;
this.explicitReceiverKind = explicitReceiverKind;
this.isSafeCall = isSafeCall;
}
/*package*/ static <D extends CallableDescriptor> ResolutionCandidate<D> create(@NotNull Call call, @NotNull D descriptor) {
return new ResolutionCandidate<D>(call, descriptor, NO_RECEIVER, NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, null);
}
/* 'null' for isSafeCall parameter if it should be set later (with 'setSafeCall') */
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(
@NotNull Call call, @NotNull D descriptor, @Nullable Boolean isSafeCall
@NotNull Call call, @NotNull D descriptor
) {
return new ResolutionCandidate<D>(call, descriptor, NO_RECEIVER, NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, isSafeCall);
return new ResolutionCandidate<D>(call, descriptor, NO_RECEIVER, NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
}
public static <D extends CallableDescriptor> ResolutionCandidate<D> create(
@NotNull Call call, @NotNull D descriptor, @NotNull ReceiverValue dispatchReceiver,
@NotNull ReceiverValue receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind, boolean isSafeCall
@NotNull ReceiverValue receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind
) {
return new ResolutionCandidate<D>(call, descriptor, dispatchReceiver, receiverArgument, explicitReceiverKind, isSafeCall);
return new ResolutionCandidate<D>(call, descriptor, dispatchReceiver, receiverArgument, explicitReceiverKind);
}
public void setDispatchReceiver(@NotNull ReceiverValue dispatchReceiver) {
@@ -105,25 +97,15 @@ public class ResolutionCandidate<D extends CallableDescriptor> {
@NotNull
public static <D extends CallableDescriptor> List<ResolutionCandidate<D>> convertCollection(
@NotNull Call call, @NotNull Collection<? extends D> descriptors, boolean isSafeCall
@NotNull Call call, @NotNull Collection<? extends D> descriptors
) {
List<ResolutionCandidate<D>> result = Lists.newArrayList();
for (D descriptor : descriptors) {
result.add(create(call, descriptor, isSafeCall));
result.add(create(call, descriptor));
}
return result;
}
public void setSafeCall(boolean safeCall) {
assert isSafeCall == null;
isSafeCall = safeCall;
}
public boolean isSafeCall() {
assert isSafeCall != null;
return isSafeCall;
}
@Override
public String toString() {
return candidateDescriptor.toString();
@@ -29,23 +29,12 @@ public class ResolutionTaskHolder<D : CallableDescriptor, F : D>(
private val priorityProvider: ResolutionTaskHolder.PriorityProvider<ResolutionCandidate<D>>,
private val tracing: TracingStrategy
) {
private val isSafeCall = JetPsiUtil.isSafeCall(basicCallResolutionContext.call)
private val candidatesList = ArrayList<() -> Collection<ResolutionCandidate<D>>>()
private var internalTasks: List<ResolutionTask<D, F>>? = null
public fun setIsSafeCall(candidates: Collection<ResolutionCandidate<D>>): Collection<ResolutionCandidate<D>> {
for (candidate in candidates) {
candidate.setSafeCall(isSafeCall)
}
return candidates
}
public fun addCandidates(lazyCandidates: () -> Collection<ResolutionCandidate<D>>) {
assertNotFinished()
candidatesList.add(storageManager.createLazyValue {
setIsSafeCall(lazyCandidates()).toReadOnlyList()
})
candidatesList.add(storageManager.createLazyValue { lazyCandidates().toReadOnlyList() })
}
public fun addCandidates(candidatesList: List<Collection<ResolutionCandidate<D>>>) {
@@ -420,7 +420,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
Call call = CallMaker.makeCall(expression, NO_RECEIVER, null, expression, Collections.<ValueArgument>emptyList());
ResolutionCandidate<ReceiverParameterDescriptor> resolutionCandidate =
ResolutionCandidate.create(
call, descriptor, NO_RECEIVER, NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, false);
call, descriptor, NO_RECEIVER, NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
ResolvedCallImpl<ReceiverParameterDescriptor> resolvedCall =
ResolvedCallImpl.create(resolutionCandidate,
@@ -72,7 +72,7 @@ public class ControlStructureTypingUtils {
SimpleFunctionDescriptorImpl function = createFunctionDescriptorForSpecialConstruction(
constructionName.toUpperCase(), argumentNames, isArgumentNullable);
TracingStrategy tracing = createTracingForSpecialConstruction(call, constructionName);
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(call, function, null);
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(call, function);
CallResolver callResolver = expressionTypingServices.getCallResolver();
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallWithKnownCandidate(
call, tracing, context, resolutionCandidate, dataFlowInfoForArguments);
@@ -0,0 +1,12 @@
class A {
val b = B()
}
class B
fun B.invoke(i: Int) = i
fun foo(i: Int) = i
fun test(a: A?) {
a?.b(1) //should be no warning
foo(<!TYPE_MISMATCH!>a?.b(1)<!>) //no warning, only error
}
@@ -0,0 +1,57 @@
DescriptorResolver@0 {
<name not found> = JetTypeImpl@1['Int']
<name not found> = JetTypeImpl@2['Int']
<name not found> = MutableClassDescriptor$1@3
}
LazyJavaPackageFragmentProvider@4 {
packageFragments('<root>': FqName@5) = LazyJavaPackageFragment@6['<root>']
packageFragments('A': FqName@7) = null
packageFragments('B': FqName@8) = null
packageFragments('Int': FqName@9) = null
packageFragments('java': FqName@10) = LazyJavaPackageFragment@11['java']
packageFragments('java.lang': FqName@12) = LazyJavaPackageFragment@13['lang']
packageFragments('java.lang.A': FqName@14) = null
packageFragments('java.lang.B': FqName@15) = null
packageFragments('java.lang.Int': FqName@16) = null
packageFragments('kotlin': FqName@17) = null
packageFragments('kotlin.A': FqName@18) = null
packageFragments('kotlin.B': FqName@19) = null
packageFragments('kotlin.Int': FqName@20) = null
packageFragments('kotlin.io': FqName@21) = null
packageFragments('kotlin.jvm': FqName@22) = null
}
LazyJavaPackageFragment@6['<root>'] {
classes('B': Name@23) = null // through LazyPackageFragmentScopeForJavaPackage@24
classes('Int': Name@25) = null // through LazyPackageFragmentScopeForJavaPackage@24
classes('b': Name@26) = null // through LazyPackageFragmentScopeForJavaPackage@24
classes('foo': Name@27) = null // through LazyPackageFragmentScopeForJavaPackage@24
classes('invoke': Name@28) = null // through LazyPackageFragmentScopeForJavaPackage@24
deserializedPackageScope = Empty@29 // through LazyPackageFragmentScopeForJavaPackage@24
functions('B': Name@23) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@24
functions('b': Name@26) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@24
functions('foo': Name@27) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@24
functions('invoke': Name@28) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@24
memberIndex = computeMemberIndex$1@31 // through LazyPackageFragmentScopeForJavaPackage@24
}
LazyJavaPackageFragment@11['java'] {
classes('lang': Name@32) = null // through LazyPackageFragmentScopeForJavaPackage@33
deserializedPackageScope = Empty@29 // through LazyPackageFragmentScopeForJavaPackage@33
functions('lang': Name@34) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@33
memberIndex = computeMemberIndex$1@35 // through LazyPackageFragmentScopeForJavaPackage@33
}
LazyJavaPackageFragment@13['lang'] {
classes('B': Name@23) = null // through LazyPackageFragmentScopeForJavaPackage@36
classes('b': Name@26) = null // through LazyPackageFragmentScopeForJavaPackage@36
classes('foo': Name@27) = null // through LazyPackageFragmentScopeForJavaPackage@36
classes('invoke': Name@28) = null // through LazyPackageFragmentScopeForJavaPackage@36
deserializedPackageScope = Empty@29 // through LazyPackageFragmentScopeForJavaPackage@36
functions('B': Name@23) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@36
functions('b': Name@26) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@36
functions('foo': Name@27) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@36
functions('invoke': Name@28) = EmptyList@30[empty] // through LazyPackageFragmentScopeForJavaPackage@36
memberIndex = computeMemberIndex$1@37 // through LazyPackageFragmentScopeForJavaPackage@36
}
@@ -0,0 +1,20 @@
package
internal fun foo(/*0*/ i: kotlin.Int): kotlin.Int
internal fun test(/*0*/ a: A?): kotlin.Unit
internal fun B.invoke(/*0*/ i: kotlin.Int): kotlin.Int
internal final class A {
public constructor A()
internal final val b: B
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal final class B {
public constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -7124,6 +7124,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("safeCallWithInvoke.kt")
public void testSafeCallWithInvoke() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/safeCallWithInvoke.kt");
doTest(fileName);
}
@TestMetadata("uselessElvis.kt")
public void testUselessElvis() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullableTypes/uselessElvis.kt");