From fff1af4ff6f0ab934724f9ad884b6e0bf9cec68e Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 28 Nov 2016 18:00:32 +0300 Subject: [PATCH] JS: allow to inherit external classes with overloaded functions in case we don't override them (see KT-13910). --- .../name/overrideOverloadedNativeFunction.kt | 9 +++++ .../name/overrideOverloadedNativeFunction.txt | 19 ++++++++++ .../DiagnosticsTestWithJsStdLibGenerated.java | 6 ++++ .../resolve/diagnostics/JsNameClashChecker.kt | 9 +++-- .../js/test/semantics/BoxJsTestGenerated.java | 6 ++++ .../overrideNativeOverloadedFunction.js | 5 +++ .../overrideNativeOverloadedFunction.kt | 36 +++++++++++++++++++ 7 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.txt create mode 100644 js/js.translator/testData/box/native/overrideNativeOverloadedFunction.js create mode 100644 js/js.translator/testData/box/native/overrideNativeOverloadedFunction.kt diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.kt new file mode 100644 index 00000000000..a7c8c194498 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.kt @@ -0,0 +1,9 @@ +external open class A { + open fun f(x: Int): Unit + + open fun f(x: String): Unit +} + +class InheritClass : A() { + override fun f(x: Int): Unit { } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.txt new file mode 100644 index 00000000000..3b44259eb27 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.txt @@ -0,0 +1,19 @@ +package + +public external open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun f(/*0*/ x: kotlin.Int): kotlin.Unit + public open fun f(/*0*/ x: kotlin.String): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class InheritClass : A { + public constructor InheritClass() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun f(/*0*/ x: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun f(/*0*/ x: kotlin.String): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index 696da523ef3..e3d1d3f1a7d 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -476,6 +476,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("overrideOverloadedNativeFunction.kt") + public void testOverrideOverloadedNativeFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/overrideOverloadedNativeFunction.kt"); + doTest(fileName); + } + @TestMetadata("packageAndMethod.kt") public void testPackageAndMethod() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.kt"); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt index 00d38b3b4b2..1573bd5bf7a 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt @@ -73,8 +73,8 @@ class JsNameClashChecker : SimpleDeclarationChecker { val overrideFqn = nameSuggestion.suggest(override)!! val scope = getScope(overrideFqn.scope) val name = overrideFqn.names.last() - val existing = scope[name] - if (existing != null && existing != overrideFqn.descriptor) { + val existing = scope[name] as? CallableMemberDescriptor + if (existing != null && existing != overrideFqn.descriptor && !isFakeOverridingNative(existing)) { diagnosticHolder.report(ErrorsJs.JS_FAKE_NAME_CLASH.on(declaration, name, override, existing)) break } @@ -89,6 +89,11 @@ class JsNameClashChecker : SimpleDeclarationChecker { } } + private fun isFakeOverridingNative(descriptor: CallableMemberDescriptor): Boolean { + return descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE && + descriptor.overriddenDescriptors.all { !presentsInGeneratedCode(it) } + } + private fun getScope(descriptor: DeclarationDescriptor) = scopes.getOrPut(descriptor) { val scope = mutableMapOf() when (descriptor) { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index db43bbb664f..efe446bc8c5 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -5681,6 +5681,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("overrideNativeOverloadedFunction.kt") + public void testOverrideNativeOverloadedFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/overrideNativeOverloadedFunction.kt"); + doTest(fileName); + } + @TestMetadata("passExtLambdaFromNative.kt") public void testPassExtLambdaFromNative() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/passExtLambdaFromNative.kt"); diff --git a/js/js.translator/testData/box/native/overrideNativeOverloadedFunction.js b/js/js.translator/testData/box/native/overrideNativeOverloadedFunction.js new file mode 100644 index 00000000000..d9ad921a37c --- /dev/null +++ b/js/js.translator/testData/box/native/overrideNativeOverloadedFunction.js @@ -0,0 +1,5 @@ +function A() { +} +A.prototype.f = function(x) { + return typeof x; +}; \ No newline at end of file diff --git a/js/js.translator/testData/box/native/overrideNativeOverloadedFunction.kt b/js/js.translator/testData/box/native/overrideNativeOverloadedFunction.kt new file mode 100644 index 00000000000..8996efe1f34 --- /dev/null +++ b/js/js.translator/testData/box/native/overrideNativeOverloadedFunction.kt @@ -0,0 +1,36 @@ +external open class A { + open fun f(x: Int) = "number" + + open fun f(x: String) = "string" +} + +class B : A() { + fun g(x: Int) = "[${f(x)}]" + + fun g(x: String) = "[${f(x)}]" +} + +interface I { + fun f(x: Int): String +} + +class C : A(), I + +external interface J { + fun f(x: Int): String +} + +class D : A(), J + +fun box(): String { + var result = B().g(23) + B().g("foo") + if (result != "[number][string]") return "fail1: $result" + + result = C().f(42) + if (result != "number") return "fail2: $result" + + result = D().f("bar") + if (result != "string") return "fail3: $result" + + return "OK" +} \ No newline at end of file