JS: prohibit non-abstract members of external interfaces, except for nullable properties. See KT-15308

This commit is contained in:
Alexey Andreev
2016-12-19 14:39:26 +03:00
parent 403753f5b5
commit 55d4c0e439
10 changed files with 81 additions and 5 deletions
@@ -73,6 +73,8 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
"Overriding `external` function with optional parameters by declaration from superclass: {0}", Renderers.COMPACT)
put(ErrorsJs.IMPLEMENTING_FUNCTION_INTERFACE, "Implementing function interface is prohibited in JavaScript")
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")
this
}
@@ -78,6 +78,8 @@ public interface ErrorsJs {
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtDeclaration> INLINE_EXTERNAL_DECLARATION = DiagnosticFactory0.create(
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtDeclaration> NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE = DiagnosticFactory0.create(
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.resolve.diagnostics
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.isInlineOnlyOrReified
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
@@ -80,6 +79,12 @@ object JsExternalChecker : SimpleDeclarationChecker {
if (descriptor is FunctionDescriptor && descriptor.isInline) {
diagnosticHolder.report(ErrorsJs.INLINE_EXTERNAL_DECLARATION.on(declaration))
}
if (descriptor is CallableMemberDescriptor && descriptor.isNonAbstractMemberOfInterface() &&
!descriptor.isNullableProperty()
) {
diagnosticHolder.report(ErrorsJs.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE.on(declaration))
}
}
private fun isDirectlyExternal(declaration: KtDeclaration, descriptor: DeclarationDescriptor): Boolean {
@@ -96,4 +101,31 @@ object JsExternalChecker : SimpleDeclarationChecker {
val containingDeclaration = descriptor.containingDeclaration as? ClassDescriptor ?: return false
return AnnotationsUtils.isNativeObject(containingDeclaration)
}
private fun CallableMemberDescriptor.isNonAbstractMemberOfInterface() =
modality != Modality.ABSTRACT && DescriptorUtils.isInterface(containingDeclaration) &&
this !is PropertyAccessorDescriptor
private fun CallableMemberDescriptor.isNullableProperty() = this is PropertyDescriptor && TypeUtils.isNullableType(type)
private fun KtDeclarationWithBody.hasValidExternalBody(bindingContext: BindingContext): Boolean {
if (!hasBody()) return true
val body = bodyExpression!!
return if (!hasBlockBody()) {
body.isNoImplExpression(bindingContext)
}
else if (body is KtBlockExpression) {
val statement = body.statements.singleOrNull() ?: return false
statement.isNoImplExpression(bindingContext)
}
else {
false
}
}
private fun KtExpression.isNoImplExpression(bindingContext: BindingContext): Boolean {
val descriptor = getResolvedCall(bindingContext)?.resultingDescriptor as? PropertyDescriptor ?: return false
val container = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
return container.fqNameUnsafe == NO_IMPL_PROPERTY_NAME.parent() && descriptor.name == NO_IMPL_PROPERTY_NAME.shortName()
}
}