JS: add checker to detect non-external declarations in files marked by JsModule or JsQualifier annotation

This commit is contained in:
Alexey Andreev
2017-01-24 15:34:57 +03:00
parent cc67f6c9f7
commit ba65e58fdd
13 changed files with 132 additions and 4 deletions
@@ -36,7 +36,7 @@ object JsPlatformConfigurator : PlatformConfigurator(
DynamicTypesAllowed(),
additionalDeclarationCheckers = listOf(
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
JsNameChecker, JsModuleChecker,
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
JsExternalChecker, JsInheritanceChecker,
JsRuntimeAnnotationChecker,
JsDynamicDeclarationChecker,
@@ -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);
@@ -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)