diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.kt new file mode 100644 index 00000000000..6ee21b20fe9 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.kt @@ -0,0 +1,7 @@ +package foo + +class A { + fun bar() = 23 + + val bar = 23 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.txt new file mode 100644 index 00000000000..6f5083c11f2 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.txt @@ -0,0 +1,13 @@ +package + +package foo { + + public final class A { + public constructor A() + public final val bar: kotlin.Int = 23 + public final fun bar(): kotlin.Int + 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 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.kt new file mode 100644 index 00000000000..1fbc24f8073 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.kt @@ -0,0 +1,11 @@ +package foo + +class A + +class B + +val A.foo: Int + get() = 32 + +val B.foo: Int + get() = 42 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.txt new file mode 100644 index 00000000000..8221186c213 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.txt @@ -0,0 +1,20 @@ +package + +package foo { + public val foo.A.foo: kotlin.Int + public val foo.B.foo: kotlin.Int + + public final class A { + public constructor A() + 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 + } + + public 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 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.kt new file mode 100644 index 00000000000..0be8d4cfdc5 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.kt @@ -0,0 +1,5 @@ +package foo + +fun bar() = 23 + +private val bar = 32 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.txt new file mode 100644 index 00000000000..942f6deaa0b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.txt @@ -0,0 +1,6 @@ +package + +package foo { + private val bar: kotlin.Int = 32 + public fun bar(): kotlin.Int +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.kt new file mode 100644 index 00000000000..2a47e165ab2 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.kt @@ -0,0 +1,11 @@ +// FILE: foo.kt + +package foo + +fun bar(x: Int) = x + +// FILE: foobar.kt + +package foo.bar + +val x = 42 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.txt new file mode 100644 index 00000000000..62873f6bfec --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.txt @@ -0,0 +1,9 @@ +package + +package foo { + public fun bar(/*0*/ x: kotlin.Int): kotlin.Int + + package foo.bar { + public val x: kotlin.Int = 42 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.kt new file mode 100644 index 00000000000..006b7e2273a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.kt @@ -0,0 +1,11 @@ +// FILE: foo.kt + +package foo + +fun bar() = 23 + +// FILE: foobar.kt + +package foo.bar + +val x = 42 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.txt new file mode 100644 index 00000000000..09db0cc4037 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.txt @@ -0,0 +1,9 @@ +package + +package foo { + public fun bar(): kotlin.Int + + package foo.bar { + public val x: kotlin.Int = 42 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.kt new file mode 100644 index 00000000000..5334f1ad88f --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.kt @@ -0,0 +1,13 @@ +// FILE: foo.kt + +package foo + +private fun bar() = 23 + +private val bar = 42 + +// FILE: foobar.kt + +package foo.bar + +val x = 42 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.txt new file mode 100644 index 00000000000..16ee05a24f7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.txt @@ -0,0 +1,10 @@ +package + +package foo { + private val bar: kotlin.Int = 42 + private fun bar(): kotlin.Int + + package foo.bar { + public val x: kotlin.Int = 42 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.kt new file mode 100644 index 00000000000..cdd5085777c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.kt @@ -0,0 +1,11 @@ +// FILE: foo.kt + +package foo + +val bar = 23 + +// FILE: foobar.kt + +package foo.bar + +val x = 42 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.txt new file mode 100644 index 00000000000..8ee7f43b4e6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.txt @@ -0,0 +1,9 @@ +package + +package foo { + public val bar: kotlin.Int = 23 + + package foo.bar { + public val x: kotlin.Int = 42 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.kt new file mode 100644 index 00000000000..c02e60b608b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.kt @@ -0,0 +1,9 @@ +package foo + +open class Super { + val foo = 23 +} + +class Sub : Super() { + fun foo() = 42 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.txt new file mode 100644 index 00000000000..83539cee81e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.txt @@ -0,0 +1,21 @@ +package + +package foo { + + public final class Sub : foo.Super { + public constructor Sub() + public final override /*1*/ /*fake_override*/ val foo: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class Super { + public constructor Super() + public final val foo: kotlin.Int = 23 + 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 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.kt new file mode 100644 index 00000000000..8be66dee682 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.kt @@ -0,0 +1,5 @@ +package foo + +@native fun bar() = 23 + +val bar = 32 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.txt new file mode 100644 index 00000000000..1c452ee3c26 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.txt @@ -0,0 +1,6 @@ +package + +package foo { + public val bar: kotlin.Int = 32 + @kotlin.js.native() public fun bar(): kotlin.Int +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.kt new file mode 100644 index 00000000000..d313c5f3373 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.kt @@ -0,0 +1,5 @@ +package foo + +fun bar() = 23 + +val bar = 32 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.txt new file mode 100644 index 00000000000..ba0ca726cd5 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.txt @@ -0,0 +1,6 @@ +package + +package foo { + public val bar: kotlin.Int = 32 + public fun bar(): kotlin.Int +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index 00921aef65d..8dcfb226bc5 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -287,6 +287,75 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes } } + @TestMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Name extends AbstractDiagnosticsTestWithJsStdLib { + public void testAllFilesPresentInName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJsStdLib/name"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("classLevelMethodAndProperty.kt") + public void testClassLevelMethodAndProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.kt"); + doTest(fileName); + } + + @TestMetadata("extensionPropertiesWithDifferentReceiversDoNotClash.kt") + public void testExtensionPropertiesWithDifferentReceiversDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/extensionPropertiesWithDifferentReceiversDoNotClash.kt"); + doTest(fileName); + } + + @TestMetadata("methodAndPrivatePropertyDoNotClash.kt") + public void testMethodAndPrivatePropertyDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndPrivatePropertyDoNotClash.kt"); + doTest(fileName); + } + + @TestMetadata("packageAndMangledMethodDoNotClash.kt") + public void testPackageAndMangledMethodDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMangledMethodDoNotClash.kt"); + doTest(fileName); + } + + @TestMetadata("packageAndMethod.kt") + public void testPackageAndMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndMethod.kt"); + doTest(fileName); + } + + @TestMetadata("packageAndPrivateDeclarationsDoNotClash.kt") + public void testPackageAndPrivateDeclarationsDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndPrivateDeclarationsDoNotClash.kt"); + doTest(fileName); + } + + @TestMetadata("packageAndProperty.kt") + public void testPackageAndProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/packageAndProperty.kt"); + doTest(fileName); + } + + @TestMetadata("propertyAndMethodInSubclass.kt") + public void testPropertyAndMethodInSubclass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndMethodInSubclass.kt"); + doTest(fileName); + } + + @TestMetadata("propertyAndNativeMethodDoNotClash.kt") + public void testPropertyAndNativeMethodDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/propertyAndNativeMethodDoNotClash.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelMethodAndProperty.kt") + public void testTopLevelMethodAndProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/topLevelMethodAndProperty.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt index 18b5df00110..2e34d471da7 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt @@ -24,9 +24,10 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject +import java.util.* class FQNGenerator { - private val cache = mutableMapOf() + private val cache: MutableMap = WeakHashMap() fun generate(descriptor: DeclarationDescriptor) = cache.getOrPut(descriptor) { generateCacheMiss(descriptor.original) } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt index e09913d8fd9..aaa518db8d1 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt @@ -19,7 +19,9 @@ package org.jetbrains.kotlin.js.resolve import org.jetbrains.kotlin.container.StorageComponentContainer import org.jetbrains.kotlin.container.useImpl import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.js.naming.FQNGenerator import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallChecker +import org.jetbrains.kotlin.js.resolve.diagnostics.JsNameChecker import org.jetbrains.kotlin.js.resolve.diagnostics.NativeInnerClassChecker import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap import org.jetbrains.kotlin.resolve.IdentifierChecker @@ -46,5 +48,6 @@ object JsPlatformConfigurator : PlatformConfigurator( container.useInstance(SyntheticScopes.Empty) container.useInstance(SyntheticConstructorsProvider.Empty) container.useInstance(JsTypeSpecificityComparator) + container.useInstance(JsNameChecker()) } } 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 c6131397f96..b2bdb3039f2 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 @@ -37,6 +37,8 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED, "Callable references for builtin members are not supported yet: ''{0}''", RenderFirstLineOfElementText) put(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED, "Argument must be non-empty JavaScript code") put(ErrorsJs.NATIVE_INNER_CLASS_PROHIBITED, "Native inner classes are prohibited") + put(ErrorsJs.JS_NAME_CLASH, "JavaScript name ({0}) generated for this declaration clashes with another declaration: {1}", + Renderers.STRING, 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 fbf04bbf806..73f465dd4d0 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.js.resolve.diagnostics; +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2; @@ -43,6 +44,7 @@ public interface ErrorsJs { DiagnosticFactory1 REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED = DiagnosticFactory1.create(ERROR, DEFAULT); DiagnosticFactory0 JSCODE_NO_JAVASCRIPT_PRODUCED = DiagnosticFactory0.create(ERROR, DEFAULT); DiagnosticFactory0 NATIVE_INNER_CLASS_PROHIBITED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory2 JS_NAME_CLASH = DiagnosticFactory2.create(ERROR); @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt new file mode 100644 index 00000000000..46ccf5af505 --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.resolve.diagnostics + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.js.naming.FQNGenerator +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DeclarationChecker +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.source.getPsi + +class JsNameChecker : DeclarationChecker { + private val fqnGenerator = FQNGenerator() + private val scopes = mutableMapOf>() + private val clashedDescriptors = mutableSetOf() + + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext + ) { + checkDescriptor(descriptor, declaration, diagnosticHolder) + } + + private fun checkDescriptor(descriptor: DeclarationDescriptor, declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) { + val fqn = fqnGenerator.generate(descriptor) + if (fqn.shared && fqn.scope is ClassOrPackageFragmentDescriptor && isOpaque(fqn.descriptor)) { + val scope = getScope(fqn.scope) + val name = fqn.names.last() + val existing = scope[name] + if (existing != null && existing != fqn.descriptor) { + diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH.on(declaration, name, existing)) + val existingDeclaration = findPsi(existing) ?: declaration + if (clashedDescriptors.add(existing) && existingDeclaration is KtDeclaration && existingDeclaration != declaration) { + diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH.on(existingDeclaration, name, descriptor)) + } + } + } + } + + private fun findPsi(descriptor: DeclarationDescriptor): PsiElement? { + val psi = (descriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() + return if (psi == null && descriptor is CallableMemberDescriptor && + descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE + ) { + descriptor.overriddenDescriptors.mapNotNull { findPsi(it) }.firstOrNull() + } + else { + psi + } + } + + private fun getScope(descriptor: DeclarationDescriptor) = scopes.getOrPut(descriptor) { + val scope = mutableMapOf() + when (descriptor) { + is PackageFragmentDescriptor -> { + collect(descriptor.getMemberScope(), scope) + val module = DescriptorUtils.getContainingModule(descriptor) + module.getSubPackagesOf(descriptor.fqName) { true } + .flatMap { module.getPackage(it).fragments } + .forEach { collect(it, scope) } + } + is ClassDescriptor -> collect(descriptor.defaultType.memberScope, scope) + } + scope + } + + private fun collect(scope: MemberScope, target: MutableMap) { + for (descriptor in scope.getContributedDescriptors()) { + val fqn = fqnGenerator.generate(descriptor) + if (fqn.shared && isOpaque(fqn.descriptor)) { + target[fqn.names.last()] = fqn.descriptor + } + } + } + + private fun collect(descriptor: DeclarationDescriptor, target: MutableMap) { + val fqn = fqnGenerator.generate(descriptor) + if (fqn.shared && isOpaque(fqn.descriptor)) { + target[fqn.names.last()] = fqn.descriptor + } + } + + private fun isOpaque(descriptor: DeclarationDescriptor) = + !AnnotationsUtils.isNativeObject(descriptor) && !AnnotationsUtils.isLibraryObject(descriptor) +} \ No newline at end of file