diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.kt new file mode 100644 index 00000000000..be11b4f8589 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.kt @@ -0,0 +1,23 @@ +package foo + +class A { + var x: Int + @JsName("get_x") get() = 23 + set(value) {} + + var y: Int + @JsName("get_y") get() = 23 + set(value) {} + + var z: Int + @JsName("get_z") get() = 23 + @JsName("set_z") set(value) {} +} + +var xx: Int + @JsName("get_xx") get() = 23 + set(value) {} + +var A.ext: Int + @JsName("get_ext") get() = 23 + set(value) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.txt new file mode 100644 index 00000000000..7568892f2eb --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.txt @@ -0,0 +1,16 @@ +package + +package foo { + public var xx: kotlin.Int + public var foo.A.ext: kotlin.Int + + public final class A { + public constructor A() + public final var x: kotlin.Int + public final var y: kotlin.Int + public final var z: 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/jsNameOnPropertyAndAccessor.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.kt new file mode 100644 index 00000000000..7d44c5f08dd --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.kt @@ -0,0 +1,24 @@ +package foo + +class A { + @JsName("x_") val x: Int + @JsName("get_x") get() = 23 + + @JsName("y_") val y = 0 + + @JsName("m_") var m: Int + @JsName("get_m") get() = 23 + @JsName("set_m") set(value) {} +} + +@JsName("xx_") val xx: Int + @JsName("get_xx") get() = 23 + +@JsName("yy_") val yy = 0 + +@JsName("mm_") var mm: Int + @JsName("get_mm") get() = 23 + @JsName("set_mm") set(value) {} + +@JsName("ext_") val A.ext: Int + @JsName("get_ext") get() = 23 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.txt new file mode 100644 index 00000000000..f17c07f5638 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.txt @@ -0,0 +1,18 @@ +package + +package foo { + @kotlin.js.JsName(name = "mm_") public var mm: kotlin.Int + @kotlin.js.JsName(name = "xx_") public val xx: kotlin.Int + @kotlin.js.JsName(name = "yy_") public val yy: kotlin.Int = 0 + @kotlin.js.JsName(name = "ext_") public val foo.A.ext: kotlin.Int + + public final class A { + public constructor A() + @kotlin.js.JsName(name = "m_") public final var m: kotlin.Int + @kotlin.js.JsName(name = "x_") public final val x: kotlin.Int + @kotlin.js.JsName(name = "y_") public final val y: kotlin.Int = 0 + 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/jsNamePrihibitedOnPrimaryConstructor.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNamePrihibitedOnPrimaryConstructor.kt new file mode 100644 index 00000000000..c317420327a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNamePrihibitedOnPrimaryConstructor.kt @@ -0,0 +1,3 @@ +package foo + +class A @JsName("B") constructor() \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNamePrihibitedOnPrimaryConstructor.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNamePrihibitedOnPrimaryConstructor.txt new file mode 100644 index 00000000000..2f4a49b4272 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNamePrihibitedOnPrimaryConstructor.txt @@ -0,0 +1,11 @@ +package + +package foo { + + public final class A { + @kotlin.js.JsName(name = "B") 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 + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index 2a5b3a8bb55..b473fc10d13 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -325,6 +325,24 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("jsNameMissingOnAccessors.kt") + public void testJsNameMissingOnAccessors() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameMissingOnAccessors.kt"); + doTest(fileName); + } + + @TestMetadata("jsNameOnPropertyAndAccessor.kt") + public void testJsNameOnPropertyAndAccessor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.kt"); + doTest(fileName); + } + + @TestMetadata("jsNamePrihibitedOnPrimaryConstructor.kt") + public void testJsNamePrihibitedOnPrimaryConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNamePrihibitedOnPrimaryConstructor.kt"); + doTest(fileName); + } + @TestMetadata("methodAndMethod.kt") public void testMethodAndMethod() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/methodAndMethod.kt"); 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 aaa518db8d1..3d7ec8c03db 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,9 +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.JsNameClashChecker import org.jetbrains.kotlin.js.resolve.diagnostics.NativeInnerClassChecker import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap import org.jetbrains.kotlin.resolve.IdentifierChecker @@ -34,7 +34,7 @@ import org.jetbrains.kotlin.types.DynamicTypesAllowed object JsPlatformConfigurator : PlatformConfigurator( DynamicTypesAllowed(), additionalDeclarationCheckers = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(), - NativeInnerClassChecker()), + NativeInnerClassChecker(), JsNameChecker), additionalCallCheckers = listOf(), additionalTypeCheckers = listOf(), additionalClassifierUsageCheckers = listOf(), @@ -48,6 +48,6 @@ object JsPlatformConfigurator : PlatformConfigurator( container.useInstance(SyntheticScopes.Empty) container.useInstance(SyntheticConstructorsProvider.Empty) container.useInstance(JsTypeSpecificityComparator) - container.useInstance(JsNameChecker()) + container.useInstance(JsNameClashChecker()) } } 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 b2bdb3039f2..888dae5a113 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 @@ -39,6 +39,9 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { 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) + put(ErrorsJs.JS_NAME_ON_PRIMARY_CONSTRUCTOR_PROHIBITED, "@JsName annotation is prohibited for primary constructors") + 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") 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 73f465dd4d0..ef3e0d118ea 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 com.intellij.psi.PsiElement; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; @@ -45,6 +46,9 @@ public interface ErrorsJs { DiagnosticFactory0 JSCODE_NO_JAVASCRIPT_PRODUCED = DiagnosticFactory0.create(ERROR, DEFAULT); DiagnosticFactory0 NATIVE_INNER_CLASS_PROHIBITED = DiagnosticFactory0.create(ERROR); DiagnosticFactory2 JS_NAME_CLASH = DiagnosticFactory2.create(ERROR); + DiagnosticFactory0 JS_NAME_ON_PRIMARY_CONSTRUCTOR_PROHIBITED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 JS_NAME_ON_ACCESSOR_AND_PROPERTY = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 JS_NAME_IS_NOT_ON_ALL_ACCESSORS = DiagnosticFactory0.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 index ba125d2e2f9..aa1b3182720 100644 --- 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 @@ -16,98 +16,39 @@ package org.jetbrains.kotlin.js.resolve.diagnostics -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor 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.psi.KtProperty import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DeclarationChecker -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.SimpleDeclarationChecker -import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension -import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.source.getPsi -class JsNameChecker : SimpleDeclarationChecker { - private val fqnGenerator = FQNGenerator() - private val scopes = mutableMapOf>() - private val clashedDescriptors = mutableSetOf() - - override fun check( - declaration: KtDeclaration, - descriptor: DeclarationDescriptor, - diagnosticHolder: DiagnosticSink, - bindingContext: BindingContext - ) { - if (declaration !is KtProperty || !descriptor.isExtension) { - checkDescriptor(descriptor, declaration, diagnosticHolder) +object JsNameChecker : SimpleDeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext) { + if (descriptor is PropertyDescriptor) { + val namedAccessorCount = descriptor.accessors.count { AnnotationsUtils.getJsName(it) != null } + if (namedAccessorCount > 0 && namedAccessorCount < descriptor.accessors.size) { + diagnosticHolder.report(ErrorsJs.JS_NAME_IS_NOT_ON_ALL_ACCESSORS.on(declaration)) + } } - } - 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)) + val jsNamePsi = AnnotationsUtils.getJsNameAnnotationPsi(bindingContext, declaration) ?: return + + when (descriptor) { + is ConstructorDescriptor -> { + if (descriptor.isPrimary) { + diagnosticHolder.report(ErrorsJs.JS_NAME_ON_PRIMARY_CONSTRUCTOR_PROHIBITED.on(jsNamePsi)) + } + } + is PropertyAccessorDescriptor -> { + if (!descriptor.isDefault && AnnotationsUtils.getJsName(descriptor.correspondingProperty) != null) { + diagnosticHolder.report(ErrorsJs.JS_NAME_ON_ACCESSOR_AND_PROPERTY.on(jsNamePsi)) } } } } - - 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()) { - collect(descriptor, target) - } - } - - private fun collect(descriptor: DeclarationDescriptor, target: MutableMap) { - if (descriptor is PropertyDescriptor && descriptor.isExtension) { - descriptor.accessors.forEach { collect(it, target) } - } - else { - 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 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 new file mode 100644 index 00000000000..976a0960eba --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt @@ -0,0 +1,113 @@ +/* + * 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.psi.KtProperty +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DeclarationChecker +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.SimpleDeclarationChecker +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.source.getPsi + +class JsNameClashChecker : SimpleDeclarationChecker { + private val fqnGenerator = FQNGenerator() + private val scopes = mutableMapOf>() + private val clashedDescriptors = mutableSetOf() + + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext + ) { + if (declaration !is KtProperty || !descriptor.isExtension) { + 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()) { + collect(descriptor, target) + } + } + + private fun collect(descriptor: DeclarationDescriptor, target: MutableMap) { + if (descriptor is PropertyDescriptor && descriptor.isExtension) { + descriptor.accessors.forEach { collect(it, target) } + } + else { + 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 diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java index 58002768d41..307f4c5b7c6 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java @@ -22,10 +22,18 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; import org.jetbrains.kotlin.descriptors.ClassDescriptor; +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.js.PredefinedAnnotation; import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.FqNameUnsafe; +import org.jetbrains.kotlin.psi.KtAnnotated; +import org.jetbrains.kotlin.psi.KtAnnotation; +import org.jetbrains.kotlin.psi.KtAnnotationEntry; +import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; +import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.constants.ConstantValue; @@ -143,6 +151,34 @@ public final class AnnotationsUtils { return (String) result; } + @Nullable + public static KtAnnotationEntry getJsNameAnnotationPsi(@NotNull BindingContext context, @NotNull KtAnnotated annotated) { + return findAnnotationPsi(context, annotated, new FqName(JS_NAME)); + } + + @Nullable + private static KtAnnotationEntry findAnnotationPsi( + @NotNull BindingContext context, @NotNull KtAnnotated annotated, + @NotNull FqName nameToFind + ) { + FqNameUnsafe nameToFindUnsafe = nameToFind.toUnsafe(); + + for (KtAnnotationEntry entry : annotated.getAnnotationEntries()) { + AnnotationDescriptor annotationDescriptor = context.get(BindingContext.ANNOTATION, entry); + assert annotationDescriptor != null : "Annotation descriptor expected for annotation entry: " + + PsiUtilsKt.getTextWithLocation(entry); + ClassifierDescriptor typeDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor(); + assert typeDescriptor instanceof ClassDescriptor : "Annotation type should be ClassDescriptor: " + + PsiUtilsKt.getTextWithLocation(entry); + FqNameUnsafe entryName = DescriptorUtils.getFqName(typeDescriptor); + if (entryName.equals(nameToFindUnsafe)) { + return entry; + } + } + + return null; + } + public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) { for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) { if (hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) { diff --git a/js/js.libraries/src/core/annotations.kt b/js/js.libraries/src/core/annotations.kt index 449649fc343..c4f1df06d4f 100644 --- a/js/js.libraries/src/core/annotations.kt +++ b/js/js.libraries/src/core/annotations.kt @@ -49,5 +49,5 @@ public annotation class enumerable() public annotation class marker @Retention(AnnotationRetention.BINARY) -@Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR) +@Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER) annotation class JsName(val name: String) \ No newline at end of file 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 9ad80220f36..091e229e372 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 @@ -4920,6 +4920,18 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiFile/functionsVisibleFromOtherFile.kt"); doTest(fileName); } + + @TestMetadata("packageAndMangledMethodDoNotClash.kt") + public void testPackageAndMangledMethodDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiFile/packageAndMangledMethodDoNotClash.kt"); + doTest(fileName); + } + + @TestMetadata("packageAndPrivateDeclarationDoNotClash.kt") + public void testPackageAndPrivateDeclarationDoNotClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiFile/packageAndPrivateDeclarationDoNotClash.kt"); + doTest(fileName); + } } @TestMetadata("js/js.translator/testData/box/multiModule") @@ -5158,6 +5170,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/nameClashes"), Pattern.compile("^([^_](.+))\\.kt$"), true); } + @TestMetadata("classAndCompanionObjectMembers.kt") + public void testClassAndCompanionObjectMembers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/classAndCompanionObjectMembers.kt"); + doTest(fileName); + } + @TestMetadata("differenceInCapitalization.kt") public void testDifferenceInCapitalization() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/differenceInCapitalization.kt"); @@ -5170,6 +5188,30 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("extensionPropertiesWithDifferentReceivers.kt") + public void testExtensionPropertiesWithDifferentReceivers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/extensionPropertiesWithDifferentReceivers.kt"); + doTest(fileName); + } + + @TestMetadata("extensionPropertyAndMethod.kt") + public void testExtensionPropertyAndMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/extensionPropertyAndMethod.kt"); + doTest(fileName); + } + + @TestMetadata("jsNameAndPrivate.kt") + public void testJsNameAndPrivate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt"); + doTest(fileName); + } + + @TestMetadata("methodAndPrivateProperty.kt") + public void testMethodAndPrivateProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/methodAndPrivateProperty.kt"); + doTest(fileName); + } + @TestMetadata("methodOverload.kt") public void testMethodOverload() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/methodOverload.kt"); @@ -5187,6 +5229,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/overloadExtension.kt"); doTest(fileName); } + + @TestMetadata("propertyAndNativeMethod.kt") + public void testPropertyAndNativeMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt"); + doTest(fileName); + } } @TestMetadata("js/js.translator/testData/box/native") @@ -5227,6 +5275,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("jsName.kt") + public void testJsName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/jsName.kt"); + doTest(fileName); + } + @TestMetadata("kt1519.kt") public void testKt1519() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/kt1519.kt"); diff --git a/js/js.translator/testData/multiFile/cases/packageAndMangledMethodDoNotClash/foo.kt b/js/js.translator/testData/box/multiFile/packageAndMangledMethodDoNotClash.kt similarity index 67% rename from js/js.translator/testData/multiFile/cases/packageAndMangledMethodDoNotClash/foo.kt rename to js/js.translator/testData/box/multiFile/packageAndMangledMethodDoNotClash.kt index 17e9af71c48..bda23425a35 100644 --- a/js/js.translator/testData/multiFile/cases/packageAndMangledMethodDoNotClash/foo.kt +++ b/js/js.translator/testData/box/multiFile/packageAndMangledMethodDoNotClash.kt @@ -1,3 +1,4 @@ +// FILE: foo.kt package foo fun bar(x: Int) = x @@ -6,4 +7,9 @@ fun box(): Boolean { assertEquals(23, bar(23)) assertEquals(42, foo.bar.x) return true -} \ No newline at end of file +} + +// FILE: foobar.kt +package foo.bar + +val x = 42 \ No newline at end of file diff --git a/js/js.translator/testData/multiFile/cases/packageAndPrivateDeclarationDoNotClash/foo.kt b/js/js.translator/testData/box/multiFile/packageAndPrivateDeclarationDoNotClash.kt similarity index 73% rename from js/js.translator/testData/multiFile/cases/packageAndPrivateDeclarationDoNotClash/foo.kt rename to js/js.translator/testData/box/multiFile/packageAndPrivateDeclarationDoNotClash.kt index daf48a4ca52..680d55c99f5 100644 --- a/js/js.translator/testData/multiFile/cases/packageAndPrivateDeclarationDoNotClash/foo.kt +++ b/js/js.translator/testData/box/multiFile/packageAndPrivateDeclarationDoNotClash.kt @@ -1,3 +1,4 @@ +// FILE: foo.kt package foo private fun bar() = 23 @@ -10,4 +11,9 @@ fun box(): Boolean { assertEquals(32, foo.bar.x) return true -} \ No newline at end of file +} + +// FILE: foobar.kt +package foo.bar + +val x = 32 diff --git a/js/js.translator/testData/nameClashes/cases/classAndCompanionObjectMembers.kt b/js/js.translator/testData/box/nameClashes/classAndCompanionObjectMembers.kt similarity index 100% rename from js/js.translator/testData/nameClashes/cases/classAndCompanionObjectMembers.kt rename to js/js.translator/testData/box/nameClashes/classAndCompanionObjectMembers.kt diff --git a/js/js.translator/testData/nameClashes/cases/extensionPropertiesWithDifferentReceivers.kt b/js/js.translator/testData/box/nameClashes/extensionPropertiesWithDifferentReceivers.kt similarity index 100% rename from js/js.translator/testData/nameClashes/cases/extensionPropertiesWithDifferentReceivers.kt rename to js/js.translator/testData/box/nameClashes/extensionPropertiesWithDifferentReceivers.kt diff --git a/js/js.translator/testData/nameClashes/cases/extensionPropertyAndMethod.kt b/js/js.translator/testData/box/nameClashes/extensionPropertyAndMethod.kt similarity index 100% rename from js/js.translator/testData/nameClashes/cases/extensionPropertyAndMethod.kt rename to js/js.translator/testData/box/nameClashes/extensionPropertyAndMethod.kt diff --git a/js/js.translator/testData/nameClashes/cases/jsNameAndPrivate.kt b/js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt similarity index 100% rename from js/js.translator/testData/nameClashes/cases/jsNameAndPrivate.kt rename to js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt diff --git a/js/js.translator/testData/nameClashes/cases/methodAndPrivateProperty.kt b/js/js.translator/testData/box/nameClashes/methodAndPrivateProperty.kt similarity index 100% rename from js/js.translator/testData/nameClashes/cases/methodAndPrivateProperty.kt rename to js/js.translator/testData/box/nameClashes/methodAndPrivateProperty.kt diff --git a/js/js.translator/testData/nameClashes/cases/propertyAndNativeMethod.kt b/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt similarity index 100% rename from js/js.translator/testData/nameClashes/cases/propertyAndNativeMethod.kt rename to js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt diff --git a/js/js.translator/testData/native/native/jsName.js b/js/js.translator/testData/box/native/jsName.js similarity index 100% rename from js/js.translator/testData/native/native/jsName.js rename to js/js.translator/testData/box/native/jsName.js diff --git a/js/js.translator/testData/native/cases/jsName.kt b/js/js.translator/testData/box/native/jsName.kt similarity index 100% rename from js/js.translator/testData/native/cases/jsName.kt rename to js/js.translator/testData/box/native/jsName.kt diff --git a/js/js.translator/testData/multiFile/cases/packageAndMangledMethodDoNotClash/foobar.kt b/js/js.translator/testData/multiFile/cases/packageAndMangledMethodDoNotClash/foobar.kt deleted file mode 100644 index 98df7475285..00000000000 --- a/js/js.translator/testData/multiFile/cases/packageAndMangledMethodDoNotClash/foobar.kt +++ /dev/null @@ -1,3 +0,0 @@ -package foo.bar - -val x = 42 \ No newline at end of file diff --git a/js/js.translator/testData/multiFile/cases/packageAndPrivateDeclarationDoNotClash/foobar.kt b/js/js.translator/testData/multiFile/cases/packageAndPrivateDeclarationDoNotClash/foobar.kt deleted file mode 100644 index dcce29f891e..00000000000 --- a/js/js.translator/testData/multiFile/cases/packageAndPrivateDeclarationDoNotClash/foobar.kt +++ /dev/null @@ -1,3 +0,0 @@ -package foo.bar - -val x = 32