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
@@ -0,0 +1,11 @@
external interface I {
interface J
class <!NESTED_CLASS_IN_EXTERNAL_INTERFACE!>C<!>
<!NESTED_CLASS_IN_EXTERNAL_INTERFACE!>object O<!>
enum class <!NESTED_CLASS_IN_EXTERNAL_INTERFACE!>E<!>
companion <!NESTED_CLASS_IN_EXTERNAL_INTERFACE!>object<!>
}
@@ -0,0 +1,49 @@
package
public external interface I {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class C {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final enum class E : kotlin.Enum<I.E> {
private constructor E()
public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: I.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): I.E
public final /*synthesized*/ fun values(): kotlin.Array<I.E>
}
public interface J {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public object O {
private constructor O()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -12,14 +12,6 @@ external interface T {
fun foo()
fun bar()
companion object {
val baz: Int
val boo: Int = definedExternally
fun foo()
fun bar(): String = definedExternally
}
}
external class C {
@@ -45,15 +45,4 @@ public external interface T {
public abstract fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public companion object Companion {
private constructor Companion()
public final val baz: kotlin.Int
public final val boo: kotlin.Int
public final fun bar(): kotlin.String
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -641,6 +641,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
doTest(fileName);
}
@TestMetadata("externalInterfaceNested.kt")
public void testExternalInterfaceNested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/externalInterfaceNested.kt");
doTest(fileName);
}
@TestMetadata("inheritance.kt")
public void testInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.kt");
@@ -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() }
}
}
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.khronos.webgl
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.dom.css
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.dom.events
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.dom
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.dom.parsing
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.dom.svg
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.dom.url
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.fetch
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.files
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.notifications
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.performance
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.workers
import kotlin.js.*
@@ -5,6 +5,7 @@
* See libraries/tools/idl2k for details
*/
@file:Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE")
package org.w3c.xhr
import kotlin.js.*
-83
View File
@@ -21,10 +21,6 @@ fun box(): String {
assertEquals("Object.Class.b", Object.Class.b)
assertEquals(142, Object.Class.test())
assertEquals("Object.Trait.a", Object.Trait.a)
assertEquals("Object.Trait.b", Object.Trait.b)
assertEquals(324, Object.Trait.test())
assertEquals("Object.a.a", Object.a.a)
assertEquals("Object.a.b", Object.a.b)
assertEquals(34, Object.a.test())
@@ -44,39 +40,12 @@ fun box(): String {
assertEquals("Class.Class.b", Class.Class.b)
assertEquals(88, Class.Class.test())
assertEquals("Class.Trait.a", Class.Trait.a)
assertEquals("Class.Trait.b", Class.Trait.b)
assertEquals(55, Class.Trait.test())
assertEquals("Class.a.a", Class.a.a)
assertEquals("Class.a.b", Class.a.b)
assertEquals(22, Class.a.test())
assertEquals("Class.b", Class.b)
assertEquals(77, Class.test())
// in trait
assertEquals("Trait.Object.a", Trait.Object.a)
assertEquals("Trait.Object.b", Trait.Object.b)
assertEquals(90, Trait.Object.test())
assertEquals("Trait.Class().a", Trait.Class("Trait.Class().a").a)
assertEquals("Trait.Class().b", Trait.Class("something").b)
assertEquals(66, Trait.Class("something").test())
assertEquals("Trait.Class.a", Trait.Class.a)
assertEquals("Trait.Class.b", Trait.Class.b)
assertEquals(88, Trait.Class.test())
assertEquals("Trait.Trait.a", Trait.Trait.a)
assertEquals("Trait.Trait.b", Trait.Trait.b)
assertEquals(55, Trait.Trait.test())
assertEquals("Trait.a.a", Trait.a.a)
assertEquals("Trait.a.b", Trait.a.b)
assertEquals(22, Trait.a.test())
assertEquals("Trait.b", Trait.b)
assertEquals(277, Trait.test())
return "OK";
}
@@ -116,12 +85,6 @@ external object Object {
val a: String
var b: String
fun test(): Int
companion object {
val a: String = definedExternally
var b: String = definedExternally
fun test(): Int = definedExternally
}
}
val a: Trait = definedExternally
@@ -153,12 +116,6 @@ external class Class {
val a: String
var b: String
fun test(): Int
companion object {
val a: String = definedExternally
var b: String = definedExternally
fun test(): Int = definedExternally
}
}
companion object {
@@ -168,43 +125,3 @@ external class Class {
fun test(): Int = definedExternally
}
}
external interface Trait {
@JsName("SomeObject")
object Object {
val a: String = definedExternally
var b: String = definedExternally
fun test(): Int = definedExternally
}
class Class(a: String) {
val a: String
var b: String = definedExternally
fun test(): Int = definedExternally
companion object {
val a: String = definedExternally
var b: String = definedExternally
fun test(): Int = definedExternally
}
}
@JsName("SomeTrait")
interface Trait {
val a: String
var b: String
fun test(): Int
companion object {
val a: String = definedExternally
var b: String = definedExternally
fun test(): Int = definedExternally
}
}
companion object {
val a: Trait = definedExternally
var b: String = definedExternally
fun test(): Int = definedExternally
}
}
@@ -58,6 +58,7 @@ fun main(args: Array<String>) {
w.appendln(" */")
w.appendln()
w.appendln("@file:Suppress(\"NESTED_CLASS_IN_EXTERNAL_INTERFACE\")")
w.appendln("package $pkg")
w.appendln()