diff --git a/compiler/testData/codegen/box/bridges/jsName.kt b/compiler/testData/codegen/box/bridges/jsName.kt new file mode 100644 index 00000000000..c024e4eed2e --- /dev/null +++ b/compiler/testData/codegen/box/bridges/jsName.kt @@ -0,0 +1,59 @@ +// TARGET_BACKEND: JS +package foo + +interface A { + @JsName("foo") fun foo(value: Int): String +} + +interface B { + @JsName("bar") fun foo(value: Int): String +} + +open class C : A, B { + override fun foo(value: Int) = "C.foo($value)" +} + +class CDerived : C() { + override fun foo(value: Int) = "CDerived.foo($value)" +} + +open class D { + open fun foo(value: Int) = "D.foo($value)" +} + +class E : D(), A, B + +fun box(): String { + val a: A = C() + assertEquals("C.foo(55)", a.foo(55)) + + val b: B = C() + assertEquals("C.foo(23)", b.foo(23)) + + val a2: A = CDerived() + assertEquals("CDerived.foo(55)", a2.foo(55)) + + val b2: B = CDerived() + assertEquals("CDerived.foo(23)", b2.foo(23)) + + val d: dynamic = C() + assertEquals("C.foo(42)", d.foo(42)) + assertEquals("C.foo(99)", d.bar(99)) + + val d2: dynamic = CDerived() + assertEquals("CDerived.foo(42)", d2.foo(42)) + assertEquals("CDerived.foo(99)", d2.bar(99)) + + val da: A = E() + assertEquals("D.foo(55)", da.foo(55)) + + val db: B = E() + assertEquals("D.foo(23)", db.foo(23)) + + val dd: dynamic = E() + assertEquals("D.foo(42)", dd.foo(42)) + assertEquals("D.foo(99)", dd.bar(99)) + assertEquals("D.foo(88)", dd.`foo_za3lpa$`(88)) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/bridges/jsNative.kt b/compiler/testData/codegen/box/bridges/jsNative.kt index 2c613e6b701..eb8cb89a9a1 100644 --- a/compiler/testData/codegen/box/bridges/jsNative.kt +++ b/compiler/testData/codegen/box/bridges/jsNative.kt @@ -13,6 +13,12 @@ class C : A, B { override fun foo(value: Int) = "C.foo($value)" } +open class D { + open fun foo(value: Int) = "D.foo($value)" +} + +class E : D(), A, B + fun box(): String { val a: A = C() assertEquals("C.foo(55)", a.foo(55)) @@ -24,5 +30,15 @@ fun box(): String { assertEquals("C.foo(42)", d.foo(42)) assertEquals("C.foo(99)", d.`foo_za3lpa$`(99)) + val da: A = E() + assertEquals("D.foo(55)", da.foo(55)) + + val db: B = E() + assertEquals("D.foo(23)", db.foo(23)) + + val dd: dynamic = E() + assertEquals("D.foo(42)", dd.foo(42)) + assertEquals("D.foo(99)", dd.`foo_za3lpa$`(99)) + return "OK" } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/conflictingNamesFromSuperclass.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/conflictingNamesFromSuperclass.kt index 313cbf781ee..3457dd8c8ec 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/name/conflictingNamesFromSuperclass.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/conflictingNamesFromSuperclass.kt @@ -20,4 +20,4 @@ open class E { open fun g() {} } -class F : E(), A, B +class F : E(), A, B diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.kt deleted file mode 100644 index d41c959ec1f..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.kt +++ /dev/null @@ -1,23 +0,0 @@ -interface A { - @JsName("f") fun foo(value: Int) -} - -interface B { - @JsName("g") fun foo(value: Int) -} - -class C : A, B { - override fun foo(value: Int) { } -} - -@native interface NA { - @JsName("f") fun foo(value: Int) -} - -@native interface NB { - @JsName("g") fun foo(value: Int) -} - -class NC : NA, NB { - override fun foo(value: Int) { } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.txt deleted file mode 100644 index 924515f9243..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.txt +++ /dev/null @@ -1,45 +0,0 @@ -package - -public interface A { - public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - @kotlin.js.JsName(name = "f") public abstract fun foo(/*0*/ value: kotlin.Int): kotlin.Unit - public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String -} - -public interface B { - public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - @kotlin.js.JsName(name = "g") public abstract fun foo(/*0*/ value: kotlin.Int): 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 C : A, B { - public constructor C() - public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*2*/ fun foo(/*0*/ value: kotlin.Int): kotlin.Unit - public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int - public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String -} - -@kotlin.js.native() public interface NA { - public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - @kotlin.js.JsName(name = "f") public abstract fun foo(/*0*/ value: kotlin.Int): kotlin.Unit - public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String -} - -@kotlin.js.native() public interface NB { - public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - @kotlin.js.JsName(name = "g") public abstract fun foo(/*0*/ value: kotlin.Int): 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 NC : NA, NB { - public constructor NC() - public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*2*/ fun foo(/*0*/ value: kotlin.Int): kotlin.Unit - public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int - public open override /*2*/ /*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 f2e0b4ab3b9..2e85e3a646f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -373,12 +373,6 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } - @TestMetadata("overriddenJsNameClash.kt") - public void testOverriddenJsNameClash() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/overriddenJsNameClash.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/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 651e38cde3b..a2ac0b06c9d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -906,6 +906,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class Bridges extends AbstractBlackBoxCodegenTest { + @TestMetadata("jsName.kt") + public void ignoredJsName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/jsName.kt"); + doTest(fileName); + } + @TestMetadata("jsNative.kt") public void ignoredJsNative() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/jsNative.kt"); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt index 2018159a9e4..db73cce4c31 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -45,8 +45,6 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.JS_NAME_ON_ACCESSOR_AND_PROPERTY, "@JsName can be either on a property or its accessors, not both of them") put(ErrorsJs.JS_NAME_IS_NOT_ON_ALL_ACCESSORS, "@JsName should be on all of the property accessors") put(ErrorsJs.JS_NAME_PROHIBITED_FOR_OVERRIDE, "@JsName is prohibited for overridden members") - put(ErrorsJs.JS_NAME_OVERRIDE_CLASH, "Conflicting names got from overridden declarations {0} and {1}", Renderers.COMPACT, - Renderers.COMPACT) this } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java index 655a37d1dc1..e91f0167745 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java @@ -49,7 +49,6 @@ public interface ErrorsJs { DiagnosticFactory0 JS_NAME_ON_ACCESSOR_AND_PROPERTY = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 JS_NAME_IS_NOT_ON_ALL_ACCESSORS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 JS_NAME_PROHIBITED_FOR_OVERRIDE = DiagnosticFactory0.create(ERROR); - DiagnosticFactory2 JS_NAME_OVERRIDE_CLASH = DiagnosticFactory2.create(ERROR); @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { 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 80dba5baaf8..47b762fcf4b 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 @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope class JsNameClashChecker : SimpleDeclarationChecker { private val fqnGenerator = FQNGenerator() private val scopes = mutableMapOf>() + private val clashedFakeOverrides = mutableMapOf>() private val clashedDescriptors = mutableSetOf() override fun check( @@ -74,6 +75,14 @@ class JsNameClashChecker : SimpleDeclarationChecker { val existing = scope[name] if (existing != null && existing != overrideFqn.descriptor) { diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH_SYNTHETIC.on(declaration, name, override, existing)) + break + } + + val clashedOverrides = clashedFakeOverrides[override] + if (clashedOverrides != null) { + val (firstExample, secondExample) = clashedOverrides + diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH_SYNTHETIC.on(declaration, name, firstExample, secondExample)) + break } } } @@ -111,6 +120,28 @@ class JsNameClashChecker : SimpleDeclarationChecker { val fqn = fqnGenerator.generate(descriptor) if (fqn.shared && isOpaque(fqn.descriptor)) { target[fqn.names.last()] = fqn.descriptor + (fqn.descriptor as? CallableMemberDescriptor)?.let { checkOverrideClashes(it, target) } + } + } + + private fun checkOverrideClashes(descriptor: CallableMemberDescriptor, target: MutableMap) { + var overridden = descriptor.overriddenDescriptors + while (overridden.isNotEmpty()) { + for (overridenDescriptor in overridden) { + val overriddenFqn = fqnGenerator.generate(overridenDescriptor) + if (overriddenFqn.shared) { + val existing = target[overriddenFqn.names.last()] + if (existing != null) { + if (existing != descriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + clashedFakeOverrides[descriptor] = Pair(existing, overridenDescriptor) + } + } + else { + target[overriddenFqn.names.last()] = descriptor + } + } + } + overridden = overridden.flatMap { it.overriddenDescriptors } } } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/OverriddenJsNameChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/OverriddenJsNameChecker.kt index 77c366bf6a7..ea81667011d 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/OverriddenJsNameChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/OverriddenJsNameChecker.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.js.resolve.diagnostics -import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -31,7 +30,7 @@ class OverriddenJsNameChecker : DeclarationChecker { private val fqnGenerator = FQNGenerator() override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, - bindingContext: BindingContext, languageFeatureSettings: LanguageFeatureSettings) { + bindingContext: BindingContext) { doCheck(descriptor) { first, second -> val psi = descriptor.findPsi() ?: declaration diagnosticHolder.report(ErrorsJs.JS_NAME_OVERRIDE_CLASH.on(psi, first, second)) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java index 9881c0d60af..2d89a65c941 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java @@ -125,6 +125,12 @@ public class BridgeTestGenerated extends AbstractBridgeTest { doTest(fileName); } + @TestMetadata("jsName.kt") + public void testJsName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/jsName.kt"); + doTest(fileName); + } + @TestMetadata("kt12416.kt") public void testKt12416() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/kt12416.kt");