JS: prohibit nested classes, objects and enums in external interface. See KT-16012

This commit is contained in:
Alexey Andreev
2017-01-31 16:08:39 +03:00
parent cc28fecacd
commit 0f049a90aa
24 changed files with 93 additions and 106 deletions
@@ -89,6 +89,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
put(ErrorsJs.INLINE_EXTERNAL_DECLARATION, "Inline external declaration")
put(ErrorsJs.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE,
"Only nullable properties of external interfaces are allowed to be non-abstract")
put(ErrorsJs.NESTED_CLASS_IN_EXTERNAL_INTERFACE, "Interface can't contain nested classes and objects")
put(ErrorsJs.WRONG_BODY_OF_EXTERNAL_DECLARATION,
"Wrong body of external declaration. Must be either ' = definedExternally' or { definedExternally }")
@@ -94,6 +94,8 @@ public interface ErrorsJs {
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtExpression> NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE = DiagnosticFactory0.create(
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtExpression> NESTED_CLASS_IN_EXTERNAL_INTERFACE = DiagnosticFactory0.create(
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
// Diagnostics about exposing implementation detail in external declarations
DiagnosticFactory0<KtExpression> WRONG_BODY_OF_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR);
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.js.resolve.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.js.resolve.diagnostics.JsExternalChecker.DEFINED_EXTENRALLY_PROPERTY_NAMES
import org.jetbrains.kotlin.js.resolve.diagnostics.JsExternalChecker.DEFINED_EXTERNALLY_PROPERTY_NAMES
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
object JsDefinedExternallyCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
if (resolvedCall.resultingDescriptor.fqNameUnsafe !in DEFINED_EXTENRALLY_PROPERTY_NAMES) return
if (resolvedCall.resultingDescriptor.fqNameUnsafe !in DEFINED_EXTERNALLY_PROPERTY_NAMES) return
val ownerDescriptor = context.scope.ownerDescriptor
if (!AnnotationsUtils.isNativeObject(ownerDescriptor) && !AnnotationsUtils.isPredefinedObject(ownerDescriptor)) {
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.singletonOrEmptyList
object JsExternalChecker : SimpleDeclarationChecker {
val DEFINED_EXTENRALLY_PROPERTY_NAMES = setOf(FqNameUnsafe("kotlin.js.noImpl"), FqNameUnsafe("kotlin.js.definedExternally"))
val DEFINED_EXTERNALLY_PROPERTY_NAMES = setOf(FqNameUnsafe("kotlin.js.noImpl"), FqNameUnsafe("kotlin.js.definedExternally"))
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext) {
@@ -61,6 +61,12 @@ object JsExternalChecker : SimpleDeclarationChecker {
diagnosticHolder.report(ErrorsJs.WRONG_EXTERNAL_DECLARATION.on(declaration, "private member of class"))
}
if (descriptor is ClassDescriptor && descriptor.kind != ClassKind.INTERFACE &&
descriptor.containingDeclaration.let { it is ClassDescriptor && it.kind == ClassKind.INTERFACE }
) {
diagnosticHolder.report(ErrorsJs.NESTED_CLASS_IN_EXTERNAL_INTERFACE.on(declaration))
}
if (descriptor !is PropertyAccessorDescriptor && descriptor.isExtension) {
val target = when (descriptor) {
is FunctionDescriptor -> "extension function"
@@ -213,6 +219,6 @@ object JsExternalChecker : SimpleDeclarationChecker {
private fun KtExpression.isDefinedExternallyExpression(bindingContext: BindingContext): Boolean {
val descriptor = getResolvedCall(bindingContext)?.resultingDescriptor as? PropertyDescriptor ?: return false
val container = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
return DEFINED_EXTENRALLY_PROPERTY_NAMES.any { container.fqNameUnsafe == it.parent() && descriptor.name == it.shortName() }
return DEFINED_EXTERNALLY_PROPERTY_NAMES.any { container.fqNameUnsafe == it.parent() && descriptor.name == it.shortName() }
}
}