diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt index 8142f883cff..94445f4ea72 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirWasmDiagnosticsList.kt @@ -33,6 +33,9 @@ object WASM_DIAGNOSTICS_LIST : DiagnosticList("FirWasmErrors") { parameter("place") parameter("type") } + val NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { + parameter("type") + } } val JS_FUN by object : DiagnosticGroup("JsFun") { diff --git a/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt b/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt index 3e1b2303720..d5d4a5efe43 100644 --- a/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt +++ b/compiler/fir/checkers/checkers.wasm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrors.kt @@ -27,6 +27,7 @@ object FirWasmErrors { val EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION by error0() val WRONG_JS_INTEROP_TYPE by error2(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) // JsFun val WRONG_JS_FUN_TARGET by error0() diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt index cd9f5963039..7e47857627d 100644 --- a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/wasm/FirWasmErrorsDefaultMessages.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JS_MODUL import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.NESTED_JS_MODULE_PROHIBITED import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.NESTED_WASM_EXPORT import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.NESTED_WASM_IMPORT +import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.WASM_EXPORT_ON_EXTERNAL_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.WASM_IMPORT_EXPORT_PARAMETER_DEFAULT_VALUE @@ -55,6 +56,11 @@ object FirWasmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { "Type ''{0}'' cannot be used in {1}. Only external, primitive, string and function types are supported in Kotlin/Wasm JS interop.", TO_STRING, FirDiagnosticRenderers.RENDER_TYPE, ) + map.put( + NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE, + "Only external declarations are allowed in files marked with ''{0}'' annotation.", + FirDiagnosticRenderers.RENDER_TYPE + ) map.put(WRONG_JS_FUN_TARGET, "Only top-level external functions can be implemented using '@JsFun'.") diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt index 93c489aecf9..6f12f0a0e1f 100644 --- a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/WasmDeclarationCheckers.kt @@ -24,5 +24,6 @@ object WasmDeclarationCheckers : DeclarationCheckers() { FirWasmJsFunAnnotationChecker, FirJsExportAnnotationChecker, FirWasmJsModuleChecker, + FirWasmExternalFileChecker, ) } diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmExternalFileChecker.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmExternalFileChecker.kt new file mode 100644 index 00000000000..0f95d209653 --- /dev/null +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmExternalFileChecker.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.wasm.checkers.declaration + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.isTopLevel +import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId +import org.jetbrains.kotlin.fir.declarations.utils.isEffectivelyExternal +import org.jetbrains.kotlin.fir.types.resolvedType +import org.jetbrains.kotlin.name.WasmStandardClassIds + +object FirWasmExternalFileChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (!context.isTopLevel || declaration.symbol.isEffectivelyExternal(context.session)) { + return + } + + val targetAnnotations = context.containingFile + ?.annotations + ?.firstOrNull { it.toAnnotationClassId(context.session) in WasmStandardClassIds.Annotations.annotationsRequiringExternal } + + if (targetAnnotations != null) { + reporter.reportOn( + declaration.source, + FirWasmErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE, + targetAnnotations.resolvedType, + context + ) + } + } +} diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/module/jsModuleNonExternal.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/module/jsModuleNonExternal.kt new file mode 100644 index 00000000000..ef68ea41e94 --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/module/jsModuleNonExternal.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +@file:JsModule("lib") + +class A { + class B + + fun bar() {} +} + +fun foo() = "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/qualifier/jsQualifierNonExternal.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/qualifier/jsQualifierNonExternal.kt new file mode 100644 index 00000000000..3eac101d1ac --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/qualifier/jsQualifierNonExternal.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +@file:JsQualifier("a.b") + +class A { + class B + + fun bar() {} +} + +fun foo() = "OK" \ No newline at end of file diff --git a/core/compiler.common.wasm/build.gradle.kts b/core/compiler.common.wasm/build.gradle.kts index 636b5c3c31b..4ee5cdf3445 100644 --- a/core/compiler.common.wasm/build.gradle.kts +++ b/core/compiler.common.wasm/build.gradle.kts @@ -7,6 +7,7 @@ project.configureJvmToolchain(JdkMajorVersion.JDK_1_8) dependencies { api(project(":core:compiler.common")) + implementation(project(":core:compiler.common.web")) } sourceSets { diff --git a/core/compiler.common.wasm/src/org/jetbrains/kotlin/name/WasmStandardClassIds.kt b/core/compiler.common.wasm/src/org/jetbrains/kotlin/name/WasmStandardClassIds.kt index 0b3f561efd3..f1421b61abb 100644 --- a/core/compiler.common.wasm/src/org/jetbrains/kotlin/name/WasmStandardClassIds.kt +++ b/core/compiler.common.wasm/src/org/jetbrains/kotlin/name/WasmStandardClassIds.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.name import org.jetbrains.kotlin.name.StandardClassIds.BASE_KOTLIN_PACKAGE +import org.jetbrains.kotlin.name.WebCommonStandardClassIds.Annotations.JsModule +import org.jetbrains.kotlin.name.WebCommonStandardClassIds.Annotations.JsQualifier object WasmStandardClassIds { val BASE_WASM_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("wasm")) @@ -19,6 +21,9 @@ object WasmStandardClassIds { @JvmField val JsFun = "JsFun".baseId() + + @JvmField + val annotationsRequiringExternal = setOf(JsModule, JsQualifier) } } diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java index 17f5e756235..7834544b602 100644 --- a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsFirWasmTestGenerated.java @@ -138,6 +138,12 @@ public class DiagnosticsFirWasmTestGenerated extends AbstractDiagnosticsFirWasmT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop/module"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("jsModuleNonExternal.kt") + public void testJsModuleNonExternal() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/jsModuleNonExternal.kt"); + } + @Test @TestMetadata("jsVarProhibited.kt") public void testJsVarProhibited() throws Exception { @@ -157,6 +163,22 @@ public class DiagnosticsFirWasmTestGenerated extends AbstractDiagnosticsFirWasmT } } + @Nested + @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/qualifier") + @TestDataPath("$PROJECT_ROOT") + public class Qualifier { + @Test + public void testAllFilesPresentInQualifier() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop/qualifier"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("jsQualifierNonExternal.kt") + public void testJsQualifierNonExternal() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/qualifier/jsQualifierNonExternal.kt"); + } + } + @Nested @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/rtti") @TestDataPath("$PROJECT_ROOT") diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java index 63e516b7dd0..f85a121f76b 100644 --- a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/diagnostics/DiagnosticsWasmTestGenerated.java @@ -138,6 +138,12 @@ public class DiagnosticsWasmTestGenerated extends AbstractDiagnosticsWasmTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop/module"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("jsModuleNonExternal.kt") + public void testJsModuleNonExternal() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/jsModuleNonExternal.kt"); + } + @Test @TestMetadata("jsVarProhibited.kt") public void testJsVarProhibited() throws Exception { @@ -157,6 +163,22 @@ public class DiagnosticsWasmTestGenerated extends AbstractDiagnosticsWasmTest { } } + @Nested + @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/qualifier") + @TestDataPath("$PROJECT_ROOT") + public class Qualifier { + @Test + public void testAllFilesPresentInQualifier() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop/qualifier"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("jsQualifierNonExternal.kt") + public void testJsQualifierNonExternal() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/qualifier/jsQualifierNonExternal.kt"); + } + } + @Nested @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/rtti") @TestDataPath("$PROJECT_ROOT")