diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt new file mode 100644 index 00000000000..15ae43868bd --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt @@ -0,0 +1,9 @@ +@file:JsModule("lib") + +class A { + class B + + fun bar() {} +} + +fun foo() = "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.txt new file mode 100644 index 00000000000..1ef81494e60 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.txt @@ -0,0 +1,18 @@ +package + +public fun foo(): kotlin.String + +public final class A { + public constructor A() + public final fun bar(): kotlin.Unit + 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/qualifier/jsQualifierNonExternal.kt b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.kt new file mode 100644 index 00000000000..2d7bf8e733d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.kt @@ -0,0 +1,9 @@ +@file:JsQualifier("a.b") + +class A { + class B + + fun bar() {} +} + +fun foo() = "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.txt b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.txt new file mode 100644 index 00000000000..1ef81494e60 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.txt @@ -0,0 +1,18 @@ +package + +public fun foo(): kotlin.String + +public final class A { + public constructor A() + public final fun bar(): kotlin.Unit + 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/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index e1c5a5b4adc..ec5fa64f499 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -383,6 +383,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("jsModuleNonExternal.kt") + public void testJsModuleNonExternal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt"); + doTest(fileName); + } + @TestMetadata("jsModuleWithoutParameters.kt") public void testJsModuleWithoutParameters() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleWithoutParameters.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 7da86750641..44d640cf604 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 @@ -36,7 +36,7 @@ object JsPlatformConfigurator : PlatformConfigurator( DynamicTypesAllowed(), additionalDeclarationCheckers = listOf( NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(), - JsNameChecker, JsModuleChecker, + JsNameChecker, JsModuleChecker, JsExternalFileChecker, JsExternalChecker, JsInheritanceChecker, JsRuntimeAnnotationChecker, JsDynamicDeclarationChecker, 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 1df34e4b22f..05a80096abc 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 @@ -64,6 +64,11 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { "from modular project") put(ErrorsJs.CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE, "When accessing module declarations from UMD, " + "they must be marked by both @JsModule and @JsNonModule") + + put(ErrorsJs.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE, + "Can't put non-external declarations in file marked with {0} annotation", RENDER_TYPE) + put(ErrorsJs.WRONG_JS_QUALIFIER, "Qualifier contains illegal characters") + put(ErrorsJs.CANNOT_CHECK_FOR_NATIVE_INTERFACE, "Cannot check for native interface: {0}", RENDER_TYPE) put(ErrorsJs.UNCHECKED_CAST_TO_NATIVE_INTERFACE, "Unchecked cast to native interface: {0} to {1}", RENDER_TYPE, RENDER_TYPE) put(ErrorsJs.NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT, "Cannot pass native interface {0} for reified type parameter", RENDER_TYPE) 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 7da64c8dd40..867fdabd354 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 @@ -63,6 +63,11 @@ public interface ErrorsJs { DiagnosticFactory0 CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM = DiagnosticFactory0.create(ERROR, DEFAULT); DiagnosticFactory0 CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM = DiagnosticFactory0.create(ERROR, DEFAULT); DiagnosticFactory0 CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE = DiagnosticFactory0.create(ERROR, DEFAULT); + + DiagnosticFactory1 NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE = + DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT); + DiagnosticFactory0 WRONG_JS_QUALIFIER = DiagnosticFactory0.create(ERROR, PositioningStrategies.DEFAULT); + DiagnosticFactory1 CANNOT_CHECK_FOR_NATIVE_INTERFACE = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 UNCHECKED_CAST_TO_NATIVE_INTERFACE = DiagnosticFactory2.create(WARNING); DiagnosticFactory1 NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(ERROR); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt new file mode 100644 index 00000000000..17cce6f6e67 --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +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.DescriptorUtils +import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe + +object JsExternalFileChecker : SimpleDeclarationChecker { + private val annotationFqNames = setOf(AnnotationsUtils.JS_MODULE_ANNOTATION, AnnotationsUtils.JS_QUALIFIER_ANNOTATION) + + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext + ) { + if (!AnnotationsUtils.isNativeObject(descriptor) && DescriptorUtils.isTopLevelDeclaration(descriptor)) { + AnnotationsUtils.getContainingFileAnnotations(bindingContext, descriptor).asSequence() + .map { it.type } + .firstOrNull { it.constructor.declarationDescriptor?.fqNameSafe in annotationFqNames } + ?.let { + diagnosticHolder.report(ErrorsJs.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE.on(declaration, it)) + } + } + } +} \ 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 013b0cfae24..539820adbb6 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 @@ -40,9 +40,9 @@ import java.util.List; public final class AnnotationsUtils { private static final String JS_NAME = "kotlin.js.JsName"; - private static final FqName JS_MODULE_ANNOTATION = new FqName("kotlin.js.JsModule"); + public static final FqName JS_MODULE_ANNOTATION = new FqName("kotlin.js.JsModule"); private static final FqName JS_NON_MODULE_ANNOTATION = new FqName("kotlin.js.JsNonModule"); - private static final FqName JS_QUALIFIER_ANNOTATION = new FqName("kotlin.js.JsQualifier"); + public static final FqName JS_QUALIFIER_ANNOTATION = new FqName("kotlin.js.JsQualifier"); private AnnotationsUtils() { } @@ -262,7 +262,7 @@ public final class AnnotationsUtils { } @NotNull - private static List getContainingFileAnnotations( + public static List getContainingFileAnnotations( @NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor ) { diff --git a/js/js.translator/testData/box/jsModule/externalPackage.kt b/js/js.translator/testData/box/jsModule/externalPackage.kt index 04d9a694d9c..a82713c0047 100644 --- a/js/js.translator/testData/box/jsModule/externalPackage.kt +++ b/js/js.translator/testData/box/jsModule/externalPackage.kt @@ -1,3 +1,4 @@ +// FILE: a.kt // MODULE_KIND: AMD @file:JsModule("lib") package foo @@ -24,6 +25,9 @@ external val bar: Int = definedExternally external var mbar: Int = definedExternally +// FILE: b.kt +package foo + fun box(): String { val a = A(23) assertEquals(23, a.x) diff --git a/js/js.translator/testData/box/jsModule/externalPackagePlain.kt b/js/js.translator/testData/box/jsModule/externalPackagePlain.kt index 01735269567..0ff04f99745 100644 --- a/js/js.translator/testData/box/jsModule/externalPackagePlain.kt +++ b/js/js.translator/testData/box/jsModule/externalPackagePlain.kt @@ -1,3 +1,4 @@ +// FILE: a.kt @file:JsModule("lib") @file:JsNonModule package foo @@ -24,6 +25,9 @@ external val bar: Int = definedExternally external var mbar: Int = definedExternally +// FILE: b.kt +package foo + fun box(): String { val a = A(23) assertEquals(23, a.x) diff --git a/js/js.translator/testData/box/jsModule/externalPackageUmdFallback.kt b/js/js.translator/testData/box/jsModule/externalPackageUmdFallback.kt index b4613496472..709a950bfac 100644 --- a/js/js.translator/testData/box/jsModule/externalPackageUmdFallback.kt +++ b/js/js.translator/testData/box/jsModule/externalPackageUmdFallback.kt @@ -1,3 +1,4 @@ +// FILE: a.kt // MODULE_KIND: UMD // NO_JS_MODULE_SYSTEM @file:JsModule("lib") @@ -22,6 +23,9 @@ external val bar: Int = definedExternally external var mbar: Int = definedExternally +// FILE: b.kt +package foo + fun box(): String { val a = A(23) assertEquals(23, a.x)