From 31a1d6ca66bd94976c5de29a5cd4d05fa8752e6a Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 4 Jul 2016 16:24:07 +0300 Subject: [PATCH] KT-12877: add diagnostics on JsModule/JsNonModule: 1) for all cases of misusing of JsModule report on JsNonModule as well 2) report about non-top-level declarations with JsModule/JsNonModule 3) report about JsModule/JsNonModule declarations inside file marked with JsModule/JsNonModule 4) report about usages of declarations not marked with both JsModule and JsNonModule from UMD module --- .../module/dualModuleFromUmd.kt | 27 +++++++++++++++++++ .../module/dualModuleFromUmd.txt | 25 +++++++++++++++++ .../module/jsVarProhibited.kt | 4 ++- .../module/jsVarProhibited.txt | 3 ++- .../module/nestedProhibited.kt | 12 +++++++++ .../module/nestedProhibited.txt | 21 +++++++++++++++ .../module/nonTopLevelProhibited.kt | 15 +++++++++++ .../module/nonTopLevelProhibited.txt | 21 +++++++++++++++ .../module/prohibitedOnNonNative.kt | 4 ++- .../module/prohibitedOnNonNative.txt | 1 + .../DiagnosticsTestWithJsStdLibGenerated.java | 18 +++++++++++++ .../diagnostics/DefaultErrorMessagesJs.kt | 11 +++++++- .../js/resolve/diagnostics/ErrorsJs.java | 3 +++ .../diagnostics/JsModuleCallChecker.kt | 19 ++++++++----- .../js/resolve/diagnostics/JsModuleChecker.kt | 17 +++++++++--- 15 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.txt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.txt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.txt diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt new file mode 100644 index 00000000000..64e818e3a18 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt @@ -0,0 +1,27 @@ +// MODULE: m1 +// FILE: a.kt +package foo + +import kotlin.js.* + +@JsModule("A") @native object A { + fun f(): Int + + val g: Int +} + +@JsNonModule @native open class B { + fun foo(): Int +} + +// MODULE: m2(m1) +// MODULE_KIND: UMD +// FILE: c.kt +package bar + +import foo.* + +fun box() { + A.f()+A.g + B() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.txt new file mode 100644 index 00000000000..ea7397dbb00 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.txt @@ -0,0 +1,25 @@ +package + +package bar { + public fun box(): kotlin.Unit +} + +package foo { + + @kotlin.js.JsModule(import = "A") @kotlin.js.native public object A { + private constructor A() + public final val g: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun f(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + @kotlin.js.JsNonModule @kotlin.js.native public open class B { + public constructor B() + 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 + } +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.kt index c215d0904a5..7aecaf2755f 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.kt @@ -1,3 +1,5 @@ package foo -@JsModule("bar") @native var baz: Int = noImpl \ No newline at end of file +@JsModule("bar") @native var bar: Int = noImpl + +@JsNonModule @native var baz: Int = noImpl \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.txt index 539d422bfc5..6d4f1fa3bc4 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.txt @@ -1,5 +1,6 @@ package package foo { - @kotlin.js.JsModule(import = "bar") @kotlin.js.native public var baz: kotlin.Int + @kotlin.js.JsModule(import = "bar") @kotlin.js.native public var bar: kotlin.Int + @kotlin.js.JsNonModule @kotlin.js.native public var baz: kotlin.Int } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.kt new file mode 100644 index 00000000000..1407690d521 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.kt @@ -0,0 +1,12 @@ +@file:JsModule("foo") +package foo + +@JsModule("A") @native class A + +@JsModule("B") @native object B + +@JsModule("foo") @native fun foo() = 23 + +@JsModule("bar") @native val bar = 42 + +@JsNonModule @native val baz = 99 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.txt new file mode 100644 index 00000000000..60c6459ed4b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.txt @@ -0,0 +1,21 @@ +package + +package foo { + @kotlin.js.JsModule(import = "bar") @kotlin.js.native public val bar: kotlin.Int = 42 + @kotlin.js.JsNonModule @kotlin.js.native public val baz: kotlin.Int = 99 + @kotlin.js.JsModule(import = "foo") @kotlin.js.native public fun foo(): kotlin.Int + + @kotlin.js.JsModule(import = "A") @kotlin.js.native 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 + } + + @kotlin.js.JsModule(import = "B") @kotlin.js.native public object B { + private 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/module/nonTopLevelProhibited.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.kt new file mode 100644 index 00000000000..52da35c0c47 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.kt @@ -0,0 +1,15 @@ +package foo + +class A { + @JsModule("foo") @native fun foo(): Int +} + +object B { + @JsModule("foo") @native fun bar(): Int +} + +fun baz() { + @JsModule("bzz") @native fun bzz(): Int + + @JsModule("bzz") @native class C +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.txt new file mode 100644 index 00000000000..2e23e888d1c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.txt @@ -0,0 +1,21 @@ +package + +package foo { + public fun baz(): kotlin.Unit + + public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.js.JsModule(import = "foo") @kotlin.js.native 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 object B { + private constructor B() + @kotlin.js.JsModule(import = "foo") @kotlin.js.native 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/module/prohibitedOnNonNative.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.kt index 10bc482228d..3b2e441df57 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.kt @@ -6,4 +6,6 @@ package foo @JsModule("foo") fun foo() = 23 -@JsModule("bar") val bar = 42 \ No newline at end of file +@JsModule("bar") val bar = 42 + +@JsNonModule val baz = 99 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.txt index 988cff6076c..04c1f1a24cf 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.txt @@ -2,6 +2,7 @@ package package foo { @kotlin.js.JsModule(import = "bar") public val bar: kotlin.Int = 42 + @kotlin.js.JsNonModule public val baz: kotlin.Int = 99 @kotlin.js.JsModule(import = "foo") public fun foo(): kotlin.Int @kotlin.js.JsModule(import = "A") public final class A { diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index e71093a4158..0b83f0917dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -305,12 +305,30 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJsStdLib/module"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("dualModuleFromUmd.kt") + public void testDualModuleFromUmd() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt"); + doTest(fileName); + } + @TestMetadata("jsVarProhibited.kt") public void testJsVarProhibited() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/jsVarProhibited.kt"); doTest(fileName); } + @TestMetadata("nestedProhibited.kt") + public void testNestedProhibited() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/nestedProhibited.kt"); + doTest(fileName); + } + + @TestMetadata("nonTopLevelProhibited.kt") + public void testNonTopLevelProhibited() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/nonTopLevelProhibited.kt"); + doTest(fileName); + } + @TestMetadata("prohibitedOnNonNative.kt") public void testProhibitedOnNonNative() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/module/prohibitedOnNonNative.kt"); 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 197cf34e77b..0b3c0643ad4 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 @@ -50,10 +50,19 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.JS_NAME_PROHIBITED_FOR_NAMED_NATIVE, "@JsName is prohibited for @native declaration with explicit name") put(ErrorsJs.JS_MODULE_PROHIBITED_ON_VAR, "@JsModule annotation prohibited for 'var' declarations. Use 'val' instead.") put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE, "@JsModule annotation prohibited for non-@native declarations.") + put(ErrorsJs.JS_MODULE_PROHIBITED_ON_VAR, "@JsModule and @JsNonModule annotations prohibited for 'var' declarations. " + + "Use 'val' instead.") + put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE, "@JsModule and @JsNonModule annotations prohibited for non-@native declarations.") + put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL, "@JsModule and @JsNonModule annotations are only allowed to appear " + + "on packages and top-level declarations") + put(ErrorsJs.NESTED_JS_MODULE_PROHIBITED, "@JsModule and @JsNonModule can't appear on here since the file is already " + + "marked by either @JsModule or @JsNonModule") put(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM, "Can't access declaration marked with @JsModule annotation " + "from non-modular project") put(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM, "Can't access declaration marked with @JsNonModule annotation " + - "from modular project") + "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.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 9bb8e9e3d76..04990023916 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 @@ -54,8 +54,11 @@ public interface ErrorsJs { DiagnosticFactory0 JS_NAME_PROHIBITED_FOR_NAMED_NATIVE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 JS_MODULE_PROHIBITED_ON_VAR = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); DiagnosticFactory0 JS_MODULE_PROHIBITED_ON_NON_NATIVE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); + DiagnosticFactory0 JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); + DiagnosticFactory0 NESTED_JS_MODULE_PROHIBITED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); 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 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/JsModuleCallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCallChecker.kt index db4c2503ce6..e42b129c5ed 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCallChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCallChecker.kt @@ -42,14 +42,21 @@ object JsModuleCallChecker : CallChecker { AnnotationsUtils.getFileModuleName(bindingContext, callee) != null val callToNonModule = AnnotationsUtils.isNonModule(callee) || AnnotationsUtils.isFromNonModuleFile(bindingContext, callee) - if (moduleKind == ModuleKind.PLAIN || moduleKind == ModuleKind.UMD) { - if (!callToNonModule && callToModule) { - context.trace.report(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM.on(reportOn)) + if (moduleKind == ModuleKind.UMD) { + if (!callToNonModule && callToModule || callToNonModule && !callToModule) { + context.trace.report(ErrorsJs.CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE.on(reportOn)) } } - if (moduleKind != ModuleKind.PLAIN) { - if (!callToModule && callToNonModule) { - context.trace.report(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM.on(reportOn)) + else { + if (moduleKind == ModuleKind.PLAIN) { + if (!callToNonModule && callToModule) { + context.trace.report(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM.on(reportOn)) + } + } + else { + if (!callToModule && callToNonModule) { + context.trace.report(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM.on(reportOn)) + } } } } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleChecker.kt index 063ccc56002..d14874d08da 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleChecker.kt @@ -23,6 +23,7 @@ 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.checkers.SimpleDeclarationChecker +import org.jetbrains.kotlin.resolve.DescriptorUtils object JsModuleChecker : SimpleDeclarationChecker { override fun check( @@ -31,14 +32,24 @@ object JsModuleChecker : SimpleDeclarationChecker { diagnosticHolder: DiagnosticSink, bindingContext: BindingContext ) { - val moduleName = AnnotationsUtils.getModuleName(descriptor) + if (AnnotationsUtils.getModuleName(descriptor) == null && !AnnotationsUtils.isNonModule(descriptor)) return - if (moduleName != null && descriptor is PropertyDescriptor && descriptor.isVar) { + if (descriptor is PropertyDescriptor && descriptor.isVar) { diagnosticHolder.report(ErrorsJs.JS_MODULE_PROHIBITED_ON_VAR.on(declaration)) } - if (moduleName != null && !AnnotationsUtils.isNativeObject(descriptor)) { + if (!AnnotationsUtils.isNativeObject(descriptor)) { diagnosticHolder.report(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE.on(declaration)) } + + if (!DescriptorUtils.isTopLevelDeclaration(descriptor)) { + diagnosticHolder.report(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL.on(declaration)) + } + + val isFileModuleOrNonModule = AnnotationsUtils.getFileModuleName(bindingContext, descriptor) != null || + AnnotationsUtils.isFromNonModuleFile(bindingContext, descriptor) + if (isFileModuleOrNonModule) { + diagnosticHolder.report(ErrorsJs.NESTED_JS_MODULE_PROHIBITED.on(declaration)) + } } }