Discriminate calls with implicit dynamic receivers
This commit is contained in:
@@ -22,6 +22,7 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.JetNodeTypes;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
@@ -33,6 +34,7 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.tasks.TasksPackage;
|
||||
import org.jetbrains.jet.lang.types.ErrorUtils;
|
||||
@@ -128,6 +130,13 @@ public class DebugInfoUtil {
|
||||
super.visitProperty(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitThisExpression(@NotNull JetThisExpression expression) {
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getResolvedCall(expression, bindingContext);
|
||||
reportIfDynamic(expression, resolvedCall.getResultingDescriptor(), debugInfoReporter);
|
||||
super.visitThisExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull JetReferenceExpression expression) {
|
||||
super.visitReferenceExpression(expression);
|
||||
@@ -218,8 +227,8 @@ public class DebugInfoUtil {
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean reportIfDynamic(JetElement element, DeclarationDescriptor declarationDescriptor, DebugInfoReporter debugInfoReporter) {
|
||||
if (TasksPackage.isDynamic(declarationDescriptor)) {
|
||||
private static boolean reportIfDynamic(JetElement element, @Nullable DeclarationDescriptor declarationDescriptor, DebugInfoReporter debugInfoReporter) {
|
||||
if (declarationDescriptor != null && TasksPackage.isDynamic(declarationDescriptor)) {
|
||||
debugInfoReporter.reportDynamicCall(element);
|
||||
return true;
|
||||
}
|
||||
|
||||
+13
-14
@@ -19,12 +19,8 @@ package org.jetbrains.jet.lang.resolve.calls.tasks
|
||||
import com.google.common.collect.Lists
|
||||
import com.google.common.collect.Sets
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||
import kotlin.Function0
|
||||
import kotlin.Function1
|
||||
import org.jetbrains.jet.lang.descriptors.*
|
||||
import org.jetbrains.jet.lang.psi.Call
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.psi.JetSuperExpression
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.jet.lang.resolve.calls.smartcasts.SmartCastUtils
|
||||
import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorCollector
|
||||
@@ -32,11 +28,8 @@ import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorC
|
||||
import org.jetbrains.jet.lang.resolve.name.Name
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScopeUtils
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.QualifierReceiver
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.jet.lang.types.ErrorUtils
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.*
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils
|
||||
@@ -405,22 +398,28 @@ public class TaskPrioritizer(private val storageManager: StorageManager) {
|
||||
ResolutionTaskHolder.PriorityProvider<ResolutionCandidate<D>> {
|
||||
|
||||
override fun getPriority(candidate: ResolutionCandidate<D>)
|
||||
= (if (isVisible(candidate)) 2 else 0) + (if (isSynthesized(candidate)) 0 else 1)
|
||||
= if (hasImplicitDynamicReceiver(candidate)) 0
|
||||
else (if (isVisible(candidate)) 2 else 0) + (if (isSynthesized(candidate)) 0 else 1)
|
||||
|
||||
override fun getMaxPriority() = 3
|
||||
|
||||
private fun isVisible(call: ResolutionCandidate<D>?): Boolean {
|
||||
if (call == null) return false
|
||||
val candidateDescriptor = call.getDescriptor()
|
||||
private fun isVisible(candidate: ResolutionCandidate<D>?): Boolean {
|
||||
if (candidate == null) return false
|
||||
val candidateDescriptor = candidate.getDescriptor()
|
||||
if (ErrorUtils.isError(candidateDescriptor)) return true
|
||||
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(call.getDispatchReceiver(), context.trace.getBindingContext())
|
||||
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), context.trace.getBindingContext())
|
||||
return Visibilities.isVisible(receiverValue, candidateDescriptor, context.scope.getContainingDeclaration())
|
||||
}
|
||||
|
||||
private fun isSynthesized(call: ResolutionCandidate<D>): Boolean {
|
||||
val descriptor = call.getDescriptor()
|
||||
private fun isSynthesized(candidate: ResolutionCandidate<D>): Boolean {
|
||||
val descriptor = candidate.getDescriptor()
|
||||
return descriptor is CallableMemberDescriptor && isOrOverridesSynthesized(descriptor : CallableMemberDescriptor)
|
||||
}
|
||||
|
||||
fun hasImplicitDynamicReceiver(candidate: ResolutionCandidate<D>): Boolean {
|
||||
return (!candidate.getExplicitReceiverKind().isDispatchReceiver() || !candidate.getCall().getExplicitReceiver().exists())
|
||||
&& candidate.getDescriptor().isDynamic()
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskPrioritizerContext<D : CallableDescriptor, F : D>(
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// !MARK_DYNAMIC_CALLS
|
||||
|
||||
// FILE: p/J.java
|
||||
|
||||
package p;
|
||||
|
||||
public class J {
|
||||
public static class C {
|
||||
private void sam(Sam sam) {}
|
||||
}
|
||||
|
||||
|
||||
public interface Sam {
|
||||
void sam();
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: k.kt
|
||||
|
||||
import p.*
|
||||
|
||||
class K: J.C() {
|
||||
fun <!UNSUPPORTED!>dynamic<!>.test() {
|
||||
<!DEBUG_INFO_DYNAMIC!>sam<!>(null)
|
||||
<!INVISIBLE_MEMBER!>sam<!>(
|
||||
<!NAMED_ARGUMENTS_NOT_ALLOWED, NAMED_PARAMETER_NOT_FOUND!>name<!> = null,
|
||||
<!NAMED_ARGUMENTS_NOT_ALLOWED, NAMED_PARAMETER_NOT_FOUND!>name<!> = null
|
||||
<!NO_VALUE_FOR_PARAMETER!>)<!>
|
||||
}
|
||||
|
||||
fun test() {
|
||||
<!INVISIBLE_MEMBER!>sam<!>(null)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
internal final class K : p.J.C {
|
||||
public constructor K()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
invisible_fake final override /*1*/ /*fake_override*/ fun sam(/*0*/ sam: (() -> kotlin.Unit)!): kotlin.Unit
|
||||
invisible_fake open override /*1*/ /*fake_override*/ fun sam(/*0*/ sam: p.J.Sam!): kotlin.Unit
|
||||
internal final fun test(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
internal final fun dynamic.test(): kotlin.Unit
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// !MARK_DYNAMIC_CALLS
|
||||
|
||||
fun foo() {
|
||||
fun test() {
|
||||
dynamic {
|
||||
<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
<!DEBUG_INFO_DYNAMIC!>bar<!>.<!DEBUG_INFO_DYNAMIC!>baz<!>(0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun </*0*/ T> dynamic(/*0*/ body: dynamic.() -> T): T
|
||||
internal fun foo(): kotlin.Unit
|
||||
internal fun test(): kotlin.Unit
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
// !MARK_DYNAMIC_CALLS
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun dynamic.test() {
|
||||
<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
<!DEBUG_INFO_DYNAMIC!>ext<!>()
|
||||
|
||||
bar()
|
||||
this.<!DEBUG_INFO_DYNAMIC!>bar<!>()
|
||||
|
||||
baz = 2
|
||||
this.<!DEBUG_INFO_DYNAMIC!>baz<!> = 2
|
||||
|
||||
"".ext()
|
||||
<!DEBUG_INFO_DYNAMIC!>ext<!>()
|
||||
|
||||
"".extValFun()
|
||||
<!DEBUG_INFO_DYNAMIC!>extValFun<!>()
|
||||
|
||||
"".extVal
|
||||
<!DEBUG_INFO_DYNAMIC!>extVal<!>
|
||||
|
||||
baz.extExtVal()
|
||||
<!DEBUG_INFO_DYNAMIC!>extExtVal<!>()
|
||||
|
||||
""()
|
||||
<!DEBUG_INFO_DYNAMIC!>this<!>()
|
||||
|
||||
C() + C()
|
||||
<!NO_VALUE_FOR_PARAMETER!>+<!>C()
|
||||
|
||||
this <!DEBUG_INFO_DYNAMIC!>+<!> C()
|
||||
|
||||
0.<!UNRESOLVED_REFERENCE!>missing<!>()
|
||||
}
|
||||
|
||||
fun bar() {}
|
||||
var baz = 1
|
||||
|
||||
fun Any.ext() {}
|
||||
|
||||
val Any.extValFun: () -> Unit get() = null!!
|
||||
val Any.extVal: () -> Unit get() = null!!
|
||||
|
||||
val Any.extExtVal: Any.() -> Unit get() = null!!
|
||||
|
||||
fun Any.invoke() {}
|
||||
|
||||
fun Any.plus(a: Any) {}
|
||||
|
||||
class C {
|
||||
|
||||
fun String.invoke() {}
|
||||
val foo: String.() -> Unit = null!!
|
||||
|
||||
val s: String = ""
|
||||
|
||||
val withInvoke = WithInvoke()
|
||||
|
||||
fun dynamic.test() {
|
||||
s()
|
||||
<!DEBUG_INFO_DYNAMIC!>this<!>()
|
||||
|
||||
s.foo()
|
||||
this.<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
|
||||
withInvoke()
|
||||
this@C.withInvoke()
|
||||
}
|
||||
}
|
||||
|
||||
class WithInvoke {
|
||||
fun invoke() {}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package
|
||||
|
||||
internal var baz: kotlin.Int
|
||||
internal val kotlin.Any.extExtVal: kotlin.Any.() -> kotlin.Unit
|
||||
internal val kotlin.Any.extVal: () -> kotlin.Unit
|
||||
internal val kotlin.Any.extValFun: () -> kotlin.Unit
|
||||
internal fun bar(): kotlin.Unit
|
||||
internal fun kotlin.Any.ext(): kotlin.Unit
|
||||
internal fun kotlin.Any.invoke(): kotlin.Unit
|
||||
internal fun kotlin.Any.plus(/*0*/ a: kotlin.Any): kotlin.Unit
|
||||
internal fun dynamic.test(): kotlin.Unit
|
||||
|
||||
internal final class C {
|
||||
public constructor C()
|
||||
internal final val foo: kotlin.String.() -> kotlin.Unit
|
||||
internal final val s: kotlin.String = ""
|
||||
internal final val withInvoke: WithInvoke
|
||||
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 fun kotlin.String.invoke(): kotlin.Unit
|
||||
internal final fun dynamic.test(): kotlin.Unit
|
||||
}
|
||||
|
||||
internal final class WithInvoke {
|
||||
public constructor WithInvoke()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal final fun invoke(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -84,7 +84,6 @@ public abstract class AbstractJetDiagnosticsTest extends BaseDiagnosticsTest {
|
||||
}
|
||||
);
|
||||
|
||||
List<JetFile> allJetFiles = new ArrayList<JetFile>();
|
||||
Map<TestModule, ModuleDescriptorImpl> modules = createModules(groupedByModule);
|
||||
Map<TestModule, BindingContext> moduleBindings = new HashMap<TestModule, BindingContext>();
|
||||
|
||||
@@ -95,7 +94,6 @@ public abstract class AbstractJetDiagnosticsTest extends BaseDiagnosticsTest {
|
||||
List<? extends TestFile> testFilesInModule = entry.getValue();
|
||||
|
||||
List<JetFile> jetFiles = getJetFiles(testFilesInModule, true);
|
||||
allJetFiles.addAll(jetFiles);
|
||||
|
||||
ModuleDescriptorImpl module = modules.get(testModule);
|
||||
BindingTrace moduleTrace = new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace();
|
||||
|
||||
@@ -3781,6 +3781,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/unsupported.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withInvisibleSynthesized.kt")
|
||||
public void testWithInvisibleSynthesized() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/enum")
|
||||
|
||||
@@ -195,6 +195,12 @@ public class JetDiagnosticsTestWithJsStdLibGenerated extends AbstractJetDiagnost
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("staticCallsInDynamicContext.kt")
|
||||
public void testStaticCallsInDynamicContext() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("substitution.kt")
|
||||
public void testSubstitution() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt");
|
||||
|
||||
@@ -81,6 +81,8 @@ If a receiver of a call is dynamic, the following resolution rules apply:
|
||||
this permits calling them on vals (e.g. those holding collection-like objects)
|
||||
- The invoke convention is limited so that for calls like `dyn.foo()` we do not look for property `foo` that has `invoke` defined on it
|
||||
(same for other cases like `+dyn` etc)
|
||||
- dynamic candidates with no explicit dispatch receiver are discriminated against all other candidate, i.e. for a call `foo()`, we first try
|
||||
to match static candidates, and only then dynamic ones.
|
||||
|
||||
NOTE: we do not even try to resolve extensions declared for static types if the receiver is dynamic. As a workaround, one may use an upcast
|
||||
to a static type: `(dyn as Foo).extensionForFoo()`.
|
||||
|
||||
Reference in New Issue
Block a user