diff --git a/compiler/frontend/src/org/jetbrains/jet/checkers/DebugInfoUtil.java b/compiler/frontend/src/org/jetbrains/jet/checkers/DebugInfoUtil.java index 24ed2e80f30..03019849f40 100644 --- a/compiler/frontend/src/org/jetbrains/jet/checkers/DebugInfoUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/checkers/DebugInfoUtil.java @@ -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 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; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.kt index c838ae45d17..d230e441f13 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.kt @@ -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> { override fun getPriority(candidate: ResolutionCandidate) - = (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?): Boolean { - if (call == null) return false - val candidateDescriptor = call.getDescriptor() + private fun isVisible(candidate: ResolutionCandidate?): 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): Boolean { - val descriptor = call.getDescriptor() + private fun isSynthesized(candidate: ResolutionCandidate): Boolean { + val descriptor = candidate.getDescriptor() return descriptor is CallableMemberDescriptor && isOrOverridesSynthesized(descriptor : CallableMemberDescriptor) } + + fun hasImplicitDynamicReceiver(candidate: ResolutionCandidate): Boolean { + return (!candidate.getExplicitReceiverKind().isDispatchReceiver() || !candidate.getCall().getExplicitReceiver().exists()) + && candidate.getDescriptor().isDynamic() + } } private class TaskPrioritizerContext( diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt b/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt new file mode 100644 index 00000000000..bdb1d328c85 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt @@ -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 dynamic.test() { + sam(null) + sam( + name = null, + name = null + ) + } + + fun test() { + sam(null) + } + +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.txt b/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.txt new file mode 100644 index 00000000000..e911cfc7c31 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt index e477e6d5dd6..616b2f42fa0 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt @@ -1,6 +1,6 @@ // !MARK_DYNAMIC_CALLS -fun foo() { +fun test() { dynamic { foo() bar.baz(0) diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.txt index 69284db5cd5..e16ac887b02 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.txt @@ -1,4 +1,4 @@ package internal fun dynamic(/*0*/ body: dynamic.() -> T): T -internal fun foo(): kotlin.Unit +internal fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt new file mode 100644 index 00000000000..deb516ef8d3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt @@ -0,0 +1,74 @@ +// !MARK_DYNAMIC_CALLS +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun dynamic.test() { + foo() + ext() + + bar() + this.bar() + + baz = 2 + this.baz = 2 + + "".ext() + ext() + + "".extValFun() + extValFun() + + "".extVal + extVal + + baz.extExtVal() + extExtVal() + + ""() + this() + + C() + C() + +C() + + this + C() + + 0.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() + this() + + s.foo() + this.foo() + + withInvoke() + this@C.withInvoke() + } +} + +class WithInvoke { + fun invoke() {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.txt new file mode 100644 index 00000000000..ba25e365640 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.txt @@ -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 +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/AbstractJetDiagnosticsTest.java b/compiler/tests/org/jetbrains/jet/checkers/AbstractJetDiagnosticsTest.java index 17ae086cbdb..8a5db1ba4f1 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/AbstractJetDiagnosticsTest.java +++ b/compiler/tests/org/jetbrains/jet/checkers/AbstractJetDiagnosticsTest.java @@ -84,7 +84,6 @@ public abstract class AbstractJetDiagnosticsTest extends BaseDiagnosticsTest { } ); - List allJetFiles = new ArrayList(); Map modules = createModules(groupedByModule); Map moduleBindings = new HashMap(); @@ -95,7 +94,6 @@ public abstract class AbstractJetDiagnosticsTest extends BaseDiagnosticsTest { List testFilesInModule = entry.getValue(); List jetFiles = getJetFiles(testFilesInModule, true); - allJetFiles.addAll(jetFiles); ModuleDescriptorImpl module = modules.get(testModule); BindingTrace moduleTrace = new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(); diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index d6aad1e23db..1c353a85339 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithJsStdLibGenerated.java index 9871abe70c1..3e1dbbec8a0 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestWithJsStdLibGenerated.java @@ -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"); diff --git a/spec-docs/dynamic-types.md b/spec-docs/dynamic-types.md index 4c9dac77928..836d156317e 100644 --- a/spec-docs/dynamic-types.md +++ b/spec-docs/dynamic-types.md @@ -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()`.