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:
Alexey Andreev
2016-07-04 16:24:07 +03:00
committed by Alexey Andreev
parent 08a7f9c298
commit 31a1d6ca66
15 changed files with 188 additions and 13 deletions
@@ -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
}
}
@@ -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<!>
}
@@ -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
}
}
@@ -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
@@ -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 {
@@ -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");
@@ -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);
@@ -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))
}
}
}