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
This commit is contained in:
committed by
Alexey Andreev
parent
08a7f9c298
commit
31a1d6ca66
@@ -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.<!CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE!>f<!>()+A.<!CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE!>g<!>
|
||||
<!CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE!>B<!>()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
package foo
|
||||
|
||||
<!JS_MODULE_PROHIBITED_ON_VAR!>@JsModule("bar") @native var baz: Int<!> = noImpl
|
||||
<!JS_MODULE_PROHIBITED_ON_VAR!>@JsModule("bar") @native var bar: Int<!> = noImpl
|
||||
|
||||
<!JS_MODULE_PROHIBITED_ON_VAR!>@JsNonModule @native var baz: Int<!> = noImpl
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
@file:JsModule("foo")
|
||||
package foo
|
||||
|
||||
@JsModule("A") @native class <!NESTED_JS_MODULE_PROHIBITED!>A<!>
|
||||
|
||||
@JsModule("B") @native <!NESTED_JS_MODULE_PROHIBITED!>object B<!>
|
||||
|
||||
<!NESTED_JS_MODULE_PROHIBITED!>@JsModule("foo") @native fun foo()<!> = 23
|
||||
|
||||
<!NESTED_JS_MODULE_PROHIBITED!>@JsModule("bar") @native val bar<!> = 42
|
||||
|
||||
<!NESTED_JS_MODULE_PROHIBITED!>@JsNonModule @native val baz<!> = 99
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package foo
|
||||
|
||||
class A {
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL!>@JsModule("foo") @native fun foo(): Int<!>
|
||||
}
|
||||
|
||||
object B {
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL!>@JsModule("foo") @native fun bar(): Int<!>
|
||||
}
|
||||
|
||||
fun baz() {
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL!>@JsModule("bzz") @native fun bzz(): Int<!>
|
||||
|
||||
@JsModule("bzz") @native class <!JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL!>C<!>
|
||||
}
|
||||
+21
@@ -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
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -6,4 +6,6 @@ package foo
|
||||
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_NATIVE!>@JsModule("foo") fun foo()<!> = 23
|
||||
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_NATIVE!>@JsModule("bar") val bar<!> = 42
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_NATIVE!>@JsModule("bar") val bar<!> = 42
|
||||
|
||||
<!JS_MODULE_PROHIBITED_ON_NON_NATIVE!>@JsNonModule val baz<!> = 99
|
||||
+1
@@ -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 {
|
||||
|
||||
+18
@@ -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");
|
||||
|
||||
+10
-1
@@ -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)
|
||||
|
||||
@@ -54,8 +54,11 @@ public interface ErrorsJs {
|
||||
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_NAMED_NATIVE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_VAR = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_NON_NATIVE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_NON_TOPLEVEL = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<KtElement> NESTED_JS_MODULE_PROHIBITED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<PsiElement> CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM = DiagnosticFactory0.create(ERROR, DEFAULT);
|
||||
DiagnosticFactory0<PsiElement> CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM = DiagnosticFactory0.create(ERROR, DEFAULT);
|
||||
DiagnosticFactory0<PsiElement> CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE = DiagnosticFactory0.create(ERROR, DEFAULT);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> CANNOT_CHECK_FOR_NATIVE_INTERFACE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> UNCHECKED_CAST_TO_NATIVE_INTERFACE = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
+13
-6
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user