JS: prohibit non-abstract members of external interfaces, except for nullable properties. See KT-15308
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
|||||||
|
external interface I {
|
||||||
|
<!NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE!>fun foo(): Unit<!> = noImpl
|
||||||
|
|
||||||
|
val a: Int?
|
||||||
|
get() = noImpl
|
||||||
|
|
||||||
|
var b: String?
|
||||||
|
get() = noImpl
|
||||||
|
set(value) = noImpl
|
||||||
|
|
||||||
|
<!NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE!>val c: Int<!>
|
||||||
|
get() = noImpl
|
||||||
|
|
||||||
|
<!NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE!>var d: String<!>
|
||||||
|
get() = noImpl
|
||||||
|
set(value) = noImpl
|
||||||
|
|
||||||
|
var e: dynamic
|
||||||
|
get() = noImpl
|
||||||
|
set(value) = noImpl
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public external interface I {
|
||||||
|
public open val a: kotlin.Int?
|
||||||
|
public open var b: kotlin.String?
|
||||||
|
public open val c: kotlin.Int
|
||||||
|
public open var d: kotlin.String
|
||||||
|
public open var e: dynamic
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+1
-1
@@ -11,7 +11,7 @@ external interface T {
|
|||||||
val baz: Int
|
val baz: Int
|
||||||
|
|
||||||
fun foo()
|
fun foo()
|
||||||
fun bar() {}
|
fun bar()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val baz: Int
|
val baz: Int
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ public external object O {
|
|||||||
|
|
||||||
public external interface T {
|
public external interface T {
|
||||||
public abstract val baz: kotlin.Int
|
public abstract val baz: kotlin.Int
|
||||||
public open fun bar(): kotlin.Unit
|
public abstract fun bar(): kotlin.Unit
|
||||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
public abstract fun foo(): kotlin.Unit
|
public abstract fun foo(): kotlin.Unit
|
||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@ external interface T {
|
|||||||
@nativeGetter
|
@nativeGetter
|
||||||
fun foo(d: Double): String?
|
fun foo(d: Double): String?
|
||||||
@nativeGetter
|
@nativeGetter
|
||||||
fun bar(d: Double): String? = noImpl
|
fun bar(d: Double): String?
|
||||||
}
|
}
|
||||||
+1
-1
@@ -4,7 +4,7 @@ package
|
|||||||
@kotlin.js.nativeGetter public fun kotlin.String.foo(/*0*/ n: kotlin.Int): kotlin.Int?
|
@kotlin.js.nativeGetter public fun kotlin.String.foo(/*0*/ n: kotlin.Int): kotlin.Int?
|
||||||
|
|
||||||
public external interface T {
|
public external interface T {
|
||||||
@kotlin.js.nativeGetter public open fun bar(/*0*/ d: kotlin.Double): kotlin.String?
|
@kotlin.js.nativeGetter public abstract fun bar(/*0*/ d: kotlin.Double): kotlin.String?
|
||||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
@kotlin.js.nativeGetter public abstract fun foo(/*0*/ d: kotlin.Double): kotlin.String?
|
@kotlin.js.nativeGetter public abstract fun foo(/*0*/ d: kotlin.Double): kotlin.String?
|
||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
|||||||
@@ -599,6 +599,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nonAbstractMembersOfInterface.kt")
|
||||||
|
public void testNonAbstractMembersOfInterface() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/nonAbstractMembersOfInterface.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("overrideOptionalParam.kt")
|
@TestMetadata("overrideOptionalParam.kt")
|
||||||
public void testOverrideOptionalParam() throws Exception {
|
public void testOverrideOptionalParam() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/overrideOptionalParam.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/overrideOptionalParam.kt");
|
||||||
|
|||||||
+2
@@ -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)
|
"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.IMPLEMENTING_FUNCTION_INTERFACE, "Implementing function interface is prohibited in JavaScript")
|
||||||
put(ErrorsJs.INLINE_EXTERNAL_DECLARATION, "Inline external declaration")
|
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
|
this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ public interface ErrorsJs {
|
|||||||
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
DiagnosticFactory0<KtDeclaration> INLINE_EXTERNAL_DECLARATION = DiagnosticFactory0.create(
|
DiagnosticFactory0<KtDeclaration> INLINE_EXTERNAL_DECLARATION = DiagnosticFactory0.create(
|
||||||
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
DiagnosticFactory0<KtDeclaration> NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE = DiagnosticFactory0.create(
|
||||||
|
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
Object _initializer = new Object() {
|
Object _initializer = new Object() {
|
||||||
|
|||||||
+33
-1
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.resolve.diagnostics
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.isInlineOnlyOrReified
|
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.js.PredefinedAnnotation
|
import org.jetbrains.kotlin.js.PredefinedAnnotation
|
||||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||||
@@ -80,6 +79,12 @@ object JsExternalChecker : SimpleDeclarationChecker {
|
|||||||
if (descriptor is FunctionDescriptor && descriptor.isInline) {
|
if (descriptor is FunctionDescriptor && descriptor.isInline) {
|
||||||
diagnosticHolder.report(ErrorsJs.INLINE_EXTERNAL_DECLARATION.on(declaration))
|
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 {
|
private fun isDirectlyExternal(declaration: KtDeclaration, descriptor: DeclarationDescriptor): Boolean {
|
||||||
@@ -96,4 +101,31 @@ object JsExternalChecker : SimpleDeclarationChecker {
|
|||||||
val containingDeclaration = descriptor.containingDeclaration as? ClassDescriptor ?: return false
|
val containingDeclaration = descriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||||
return AnnotationsUtils.isNativeObject(containingDeclaration)
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user