From 7687b866543ad0fafb3a865b8dd36b1811d9db6e Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Wed, 22 Nov 2023 11:33:31 +0100 Subject: [PATCH] [Wasm] Add JsModule checker to K2 #KT-56849 --- .../diagnostics/FirWasmDiagnosticsList.kt | 6 +++ .../diagnostics/wasm/FirWasmErrors.kt | 5 +++ .../wasm/FirWasmErrorsDefaultMessages.kt | 9 +++++ .../wasm/checkers/WasmDeclarationCheckers.kt | 3 +- .../declaration/FirWasmJsModuleChecker.kt | 38 +++++++++++++++++++ .../jsInterop/module/jsVarProhibited.kt | 10 +++++ .../jsInterop/module/nestedProhibited.kt | 17 +++++++++ .../module/prohibitedOnNonNative.fir.kt | 13 +++++++ .../jsInterop/module/prohibitedOnNonNative.kt | 13 +++++++ .../DiagnosticsFirWasmTestGenerated.java | 28 ++++++++++++++ .../DiagnosticsWasmTestGenerated.java | 28 ++++++++++++++ 11 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsModuleChecker.kt create mode 100644 compiler/testData/diagnostics/wasmTests/jsInterop/module/jsVarProhibited.kt create mode 100644 compiler/testData/diagnostics/wasmTests/jsInterop/module/nestedProhibited.kt create mode 100644 compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.fir.kt create mode 100644 compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.kt 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 cc65a26c243..8142f883cff 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 @@ -15,6 +15,12 @@ import org.jetbrains.kotlin.util.PrivateForInline @Suppress("ClassName", "unused") @OptIn(PrivateForInline::class) object WASM_DIAGNOSTICS_LIST : DiagnosticList("FirWasmErrors") { + val ANNOTATIONS by object : DiagnosticGroup("Annotations") { + val JS_MODULE_PROHIBITED_ON_VAR by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val JS_MODULE_PROHIBITED_ON_NON_EXTERNAL by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val NESTED_JS_MODULE_PROHIBITED by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + } + val EXTERNALS by object : DiagnosticGroup("Externals") { val NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { parameter("superType") 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 b5d9000af04..3e1b2303720 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 @@ -17,6 +17,11 @@ import org.jetbrains.kotlin.psi.KtElement * Generated from: [org.jetbrains.kotlin.fir.checkers.generator.diagnostics.WASM_DIAGNOSTICS_LIST] */ object FirWasmErrors { + // Annotations + val JS_MODULE_PROHIBITED_ON_VAR by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val JS_MODULE_PROHIBITED_ON_NON_EXTERNAL by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val NESTED_JS_MODULE_PROHIBITED by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + // Externals val NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) 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 0df00bdd16f..cd9f5963039 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 @@ -15,6 +15,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JSCODE_I import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JSCODE_UNSUPPORTED_FUNCTION_KIND import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JSCODE_WRONG_CONTEXT import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JS_AND_WASM_EXPORTS_ON_SAME_DECLARATION +import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JS_MODULE_PROHIBITED_ON_NON_EXTERNAL +import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.JS_MODULE_PROHIBITED_ON_VAR +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_TYPE_EXTENDS_EXTERNAL_TYPE @@ -30,6 +33,12 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors.WRONG_JS @Suppress("unused") object FirWasmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { override val MAP = KtDiagnosticFactoryToRendererMap("FIR").also { map -> + map.put(JS_MODULE_PROHIBITED_ON_VAR, "'@JsModule' annotation is prohibited for 'var' declarations. Use 'val' instead.") + map.put(JS_MODULE_PROHIBITED_ON_NON_EXTERNAL, "'@JsModule' annotation is prohibited for non-external declarations.") + map.put( + NESTED_JS_MODULE_PROHIBITED, + "'@JsModule' cannot appear here since the file is already marked by either '@JsModule'." + ) map.put( NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE, "Non-external type extends external type ''{0}''", 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 301ea803d30..93c489aecf9 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 @@ -22,6 +22,7 @@ object WasmDeclarationCheckers : DeclarationCheckers() { FirWasmExportAnnotationChecker, FirWasmExternalChecker, FirWasmJsFunAnnotationChecker, - FirJsExportAnnotationChecker + FirJsExportAnnotationChecker, + FirWasmJsModuleChecker, ) } diff --git a/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsModuleChecker.kt b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsModuleChecker.kt new file mode 100644 index 00000000000..3e1d5ada610 --- /dev/null +++ b/compiler/fir/checkers/checkers.wasm/src/org/jetbrains/kotlin/fir/analysis/wasm/checkers/declaration/FirWasmJsModuleChecker.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2022 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.closestNonLocalWith +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.FirFile +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.declarations.utils.isEffectivelyExternal +import org.jetbrains.kotlin.name.WebCommonStandardClassIds.Annotations.JsModule + +object FirWasmJsModuleChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration is FirFile || !declaration.hasAnnotation(JsModule, context.session)) return + + if (declaration is FirProperty && declaration.isVar) { + reporter.reportOn(declaration.source, FirWasmErrors.JS_MODULE_PROHIBITED_ON_VAR, context) + } + + if (!declaration.symbol.isEffectivelyExternal(context.session)) { + reporter.reportOn(declaration.source, FirWasmErrors.JS_MODULE_PROHIBITED_ON_NON_EXTERNAL, context) + } + + if (context.isTopLevel && context.containingFile?.hasAnnotation(JsModule, context.session) == true) { + reporter.reportOn(declaration.source, FirWasmErrors.NESTED_JS_MODULE_PROHIBITED, context) + } + } +} diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/module/jsVarProhibited.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/module/jsVarProhibited.kt new file mode 100644 index 00000000000..b093ac89022 --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/module/jsVarProhibited.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +package foo + +@JsModule("bar") +external var bar: Int = definedExternally + +typealias JsM = JsModule + +@JsM("bar") +external var bar2: Int = definedExternally diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/module/nestedProhibited.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/module/nestedProhibited.kt new file mode 100644 index 00000000000..8e570ffe189 --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/module/nestedProhibited.kt @@ -0,0 +1,17 @@ +// FIR_IDENTICAL +@file:JsModule("foo") +package foo + +@JsModule("A") +external class A { + class Nested +} + +@JsModule("B") +external object B + +@JsModule("foo") +external fun foo(): Int + +@JsModule("bar") +external val bar: Int \ No newline at end of file diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.fir.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.fir.kt new file mode 100644 index 00000000000..f6d358d7c6c --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.fir.kt @@ -0,0 +1,13 @@ +package foo + +@JsModule("A") +class A + +@JsModule("B") +object B + +@JsModule("foo") +fun foo() = 23 + +@JsModule("bar") +val bar = 42 diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.kt new file mode 100644 index 00000000000..2798443ef09 --- /dev/null +++ b/compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.kt @@ -0,0 +1,13 @@ +package foo + +@JsModule("A") +class A + +@JsModule("B") +object B + +@JsModule("foo") +fun foo() = 23 + +@JsModule("bar") +val bar = 42 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 2ee74d08213..17f5e756235 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 @@ -129,6 +129,34 @@ public class DiagnosticsFirWasmTestGenerated extends AbstractDiagnosticsFirWasmT runTest("compiler/testData/diagnostics/wasmTests/jsInterop/wrongQualifier.kt"); } + @Nested + @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/module") + @TestDataPath("$PROJECT_ROOT") + public class Module { + @Test + public void testAllFilesPresentInModule() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop/module"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("jsVarProhibited.kt") + public void testJsVarProhibited() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/jsVarProhibited.kt"); + } + + @Test + @TestMetadata("nestedProhibited.kt") + public void testNestedProhibited() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/nestedProhibited.kt"); + } + + @Test + @TestMetadata("prohibitedOnNonNative.kt") + public void testProhibitedOnNonNative() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.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 490ab9ab757..63e516b7dd0 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 @@ -129,6 +129,34 @@ public class DiagnosticsWasmTestGenerated extends AbstractDiagnosticsWasmTest { runTest("compiler/testData/diagnostics/wasmTests/jsInterop/wrongQualifier.kt"); } + @Nested + @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/module") + @TestDataPath("$PROJECT_ROOT") + public class Module { + @Test + public void testAllFilesPresentInModule() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/wasmTests/jsInterop/module"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("jsVarProhibited.kt") + public void testJsVarProhibited() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/jsVarProhibited.kt"); + } + + @Test + @TestMetadata("nestedProhibited.kt") + public void testNestedProhibited() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/nestedProhibited.kt"); + } + + @Test + @TestMetadata("prohibitedOnNonNative.kt") + public void testProhibitedOnNonNative() throws Exception { + runTest("compiler/testData/diagnostics/wasmTests/jsInterop/module/prohibitedOnNonNative.kt"); + } + } + @Nested @TestMetadata("compiler/testData/diagnostics/wasmTests/jsInterop/rtti") @TestDataPath("$PROJECT_ROOT")