JS: add checker to detect non-external declarations in files marked by JsModule or JsQualifier annotation
This commit is contained in:
@@ -36,7 +36,7 @@ object JsPlatformConfigurator : PlatformConfigurator(
|
||||
DynamicTypesAllowed(),
|
||||
additionalDeclarationCheckers = listOf(
|
||||
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
|
||||
JsNameChecker, JsModuleChecker,
|
||||
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
|
||||
JsExternalChecker, JsInheritanceChecker,
|
||||
JsRuntimeAnnotationChecker,
|
||||
JsDynamicDeclarationChecker,
|
||||
|
||||
+5
@@ -64,6 +64,11 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
||||
"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.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE,
|
||||
"Can't put non-external declarations in file marked with {0} annotation", RENDER_TYPE)
|
||||
put(ErrorsJs.WRONG_JS_QUALIFIER, "Qualifier contains illegal characters")
|
||||
|
||||
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)
|
||||
|
||||
@@ -63,6 +63,11 @@ public interface ErrorsJs {
|
||||
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<KtElement, KotlinType> NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE =
|
||||
DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<KtValueArgument> WRONG_JS_QUALIFIER = DiagnosticFactory0.create(ERROR, PositioningStrategies.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);
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
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.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
|
||||
object JsExternalFileChecker : SimpleDeclarationChecker {
|
||||
private val annotationFqNames = setOf(AnnotationsUtils.JS_MODULE_ANNOTATION, AnnotationsUtils.JS_QUALIFIER_ANNOTATION)
|
||||
|
||||
override fun check(
|
||||
declaration: KtDeclaration,
|
||||
descriptor: DeclarationDescriptor,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
if (!AnnotationsUtils.isNativeObject(descriptor) && DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||
AnnotationsUtils.getContainingFileAnnotations(bindingContext, descriptor).asSequence()
|
||||
.map { it.type }
|
||||
.firstOrNull { it.constructor.declarationDescriptor?.fqNameSafe in annotationFqNames }
|
||||
?.let {
|
||||
diagnosticHolder.report(ErrorsJs.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE.on(declaration, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,9 @@ import java.util.List;
|
||||
|
||||
public final class AnnotationsUtils {
|
||||
private static final String JS_NAME = "kotlin.js.JsName";
|
||||
private static final FqName JS_MODULE_ANNOTATION = new FqName("kotlin.js.JsModule");
|
||||
public static final FqName JS_MODULE_ANNOTATION = new FqName("kotlin.js.JsModule");
|
||||
private static final FqName JS_NON_MODULE_ANNOTATION = new FqName("kotlin.js.JsNonModule");
|
||||
private static final FqName JS_QUALIFIER_ANNOTATION = new FqName("kotlin.js.JsQualifier");
|
||||
public static final FqName JS_QUALIFIER_ANNOTATION = new FqName("kotlin.js.JsQualifier");
|
||||
|
||||
private AnnotationsUtils() {
|
||||
}
|
||||
@@ -262,7 +262,7 @@ public final class AnnotationsUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<AnnotationDescriptor> getContainingFileAnnotations(
|
||||
public static List<AnnotationDescriptor> getContainingFileAnnotations(
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor descriptor
|
||||
) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FILE: a.kt
|
||||
// MODULE_KIND: AMD
|
||||
@file:JsModule("lib")
|
||||
package foo
|
||||
@@ -24,6 +25,9 @@ external val bar: Int = definedExternally
|
||||
|
||||
external var mbar: Int = definedExternally
|
||||
|
||||
// FILE: b.kt
|
||||
package foo
|
||||
|
||||
fun box(): String {
|
||||
val a = A(23)
|
||||
assertEquals(23, a.x)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FILE: a.kt
|
||||
@file:JsModule("lib")
|
||||
@file:JsNonModule
|
||||
package foo
|
||||
@@ -24,6 +25,9 @@ external val bar: Int = definedExternally
|
||||
|
||||
external var mbar: Int = definedExternally
|
||||
|
||||
// FILE: b.kt
|
||||
package foo
|
||||
|
||||
fun box(): String {
|
||||
val a = A(23)
|
||||
assertEquals(23, a.x)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FILE: a.kt
|
||||
// MODULE_KIND: UMD
|
||||
// NO_JS_MODULE_SYSTEM
|
||||
@file:JsModule("lib")
|
||||
@@ -22,6 +23,9 @@ external val bar: Int = definedExternally
|
||||
|
||||
external var mbar: Int = definedExternally
|
||||
|
||||
// FILE: b.kt
|
||||
package foo
|
||||
|
||||
fun box(): String {
|
||||
val a = A(23)
|
||||
assertEquals(23, a.x)
|
||||
|
||||
Reference in New Issue
Block a user