From 9000d54d8acf6e47bd3d469e1c6d8f38d718eda9 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 23 Jan 2017 19:19:11 +0300 Subject: [PATCH] JS: when checking calls to JsModule declarations, check arguments passed to reified type parameters. See KT-15253 --- .../module/wrongCallToModule.kt | 10 +++++++++- .../module/wrongCallToModule.txt | 1 + .../module/wrongCallToNonModule.kt | 10 +++++++++- .../module/wrongCallToNonModule.txt | 1 + .../diagnostics/DefaultErrorMessagesJs.kt | 8 ++++---- .../kotlin/js/resolve/diagnostics/ErrorsJs.java | 6 ++++-- .../resolve/diagnostics/JsModuleCallChecker.kt | 17 +++++++++++++++++ .../js/resolve/diagnostics/JsModuleCheckUtil.kt | 4 ++-- 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt index 2755a055c97..015f3f17fc0 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt @@ -41,6 +41,14 @@ fun box() { baz() B.Nested() + + boo<B?>(null) + boo(null as B?) + boo<B.Nested?>(null) } -external class DerivedB : B \ No newline at end of file +external class DerivedB : B + +inline fun boo(x: T) { + println("${T::class.simpleName}: $x") +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.txt index a776193f8de..4a0587339c1 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.txt @@ -34,6 +34,7 @@ package foo { package package bar { + public inline fun boo(/*0*/ x: T): kotlin.Unit public fun box(): kotlin.Unit public external final class DerivedB : foo.B { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt index 4a3e7f3aeb4..0cd5e6254b4 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt @@ -34,6 +34,14 @@ fun box() { B() bar() B.Nested() + + boo<B?>(null) + boo(null as B?) + boo<B.Nested?>(null) } -external class DerivedB : B \ No newline at end of file +external class DerivedB : B + +inline fun boo(x: T) { + println("${T::class.simpleName}: $x") +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.txt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.txt index 0c451fcdb39..5ccc1697ee0 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.txt @@ -1,6 +1,7 @@ package package bar { + public inline fun boo(/*0*/ x: T): kotlin.Unit public fun box(): kotlin.Unit public external final class DerivedB : foo.B { 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 05a80096abc..f64a81e00b9 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 @@ -58,10 +58,10 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE, "@JsModule and @JsNonModule annotations prohibited for non-external 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") + put(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM, + "Can't access {0} marked with @JsModule annotation from non-modular project", Renderers.DECLARATION_NAME_WITH_KIND) + put(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM, + "Can't access {0} marked with @JsNonModule annotation from modular project", Renderers.DECLARATION_NAME_WITH_KIND) 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") 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 867fdabd354..4cd214adb47 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 @@ -60,8 +60,10 @@ public interface ErrorsJs { 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 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); + DiagnosticFactory1 CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM = + DiagnosticFactory1.create(ERROR, DEFAULT); + DiagnosticFactory1 CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM = + DiagnosticFactory1.create(ERROR, DEFAULT); DiagnosticFactory0 CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE = DiagnosticFactory0.create(ERROR, DEFAULT); DiagnosticFactory1 NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE = 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 1dd1632f205..520f35e8150 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 @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.js.resolve.diagnostics import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker @@ -26,6 +27,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ClassValueReceiver object JsModuleCallChecker : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + checkReifieidTypeParameters(resolvedCall, reportOn, context) + val callee = extractModuleCallee(resolvedCall) ?: return val bindingContext = context.trace.bindingContext val containingDescriptor = context.scope.ownerDescriptor @@ -41,4 +44,18 @@ object JsModuleCallChecker : CallChecker { return null } + + private fun checkReifieidTypeParameters(call: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + val containingDescriptor = context.scope.ownerDescriptor + val typeParams = call.candidateDescriptor.typeParameters.map { it.original }.withIndex().filter { (_, param) -> param.isReified } + val typeArguments = call.call.typeArgumentList + ?.let { args -> typeParams.associate { (index, param) -> param.original to args.arguments[index].typeReference }} + .orEmpty() + for (typeParam in typeParams.map { (_, param) -> param.original }) { + val argPsi = typeArguments[typeParam] ?: reportOn + val typeArgument = call.typeArguments[typeParam] ?: continue + val typeArgumentClass = typeArgument.constructor.declarationDescriptor as? ClassDescriptor ?: continue + checkJsModuleUsage(context.trace.bindingContext, context.trace, containingDescriptor, typeArgumentClass, argPsi) + } + } } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCheckUtil.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCheckUtil.kt index 18d544d4128..c7577c8116e 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCheckUtil.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsModuleCheckUtil.kt @@ -50,12 +50,12 @@ fun checkJsModuleUsage( else { if (moduleKind == ModuleKind.PLAIN) { if (!callToNonModule && callToModule) { - diagnosticSink.report(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM.on(reportOn)) + diagnosticSink.report(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM.on(reportOn, callee)) } } else { if (!callToModule && callToNonModule) { - diagnosticSink.report(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM.on(reportOn)) + diagnosticSink.report(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM.on(reportOn, callee)) } } }