Skip to content

Change variance and nullability of property type in parent #340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: var-to-val
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ external interface BaseEvent {
fun getDelegateTarget(): Shape
fun getElement(): Element
fun <T : Shape> transform(shape: T = definedExternally): T
var prop: Any
val prop: Any
fun queryByReturnType(query: String, parameters: Array<Any> = definedExternally): InvariantBox<Any>
var thisIsNullable: String?
}
Expand All @@ -39,7 +39,7 @@ external open class BoxStringEvent : BaseEvent {
override fun getDelegateTarget(): Box
override fun getElement(): HTMLElement
override fun <T : Shape> transform(shape: T): T
override var prop: String
override val prop: String
open fun queryByReturnType(query: String, parameters: Array<Any> = definedExternally): InvariantBox<String>
}

Expand All @@ -49,12 +49,12 @@ external interface NumberEvent : BaseEvent {
}

external open class ParentClass {
open var prop: Any
open val prop: Any
open fun ping(message: String)
}

external open class ChildClass : ParentClass {
override var prop: String
override val prop: String
override fun ping(message: String /* "message" */)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ external open class BoxStringEvent : BaseEvent {
override fun getElement(): HTMLElement
override fun <T : Shape> transform(shape: T): T
override fun getSortOfEventTarget(): SortOfElement
override var prop: String
override val prop: String
}

external interface NumberEvent : BaseEvent {
Expand Down Expand Up @@ -65,5 +65,5 @@ external interface BaseEvent {
fun getElement(): Element
fun <T : Shape> transform(shape: T = definedExternally): T
fun getSortOfEventTarget(): SortOfEventTarget
var prop: Any
val prop: Any
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import org.w3c.workers.*
import org.w3c.xhr.*

external interface AppEvent : Event {
override var currentTarget: Element?
override var target: Element?
override val currentTarget: Element?
override val target: Element?
fun preventDefault(): Any
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/test/data/typescript/class/override/simple.d.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import org.w3c.xhr.*
external open class Foo {
open fun bar()
open fun bar(a: Number)
open var baz: Any
open val baz: Any
}

external open class Boo : Foo {
override fun bar()
override fun bar(a: Number)
open fun bar(a: String)
override var baz: Number
override val baz: Number
}
4 changes: 2 additions & 2 deletions compiler/test/data/typescript/interface/override/simple.d.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import org.w3c.xhr.*
external interface Foo {
fun bar()
fun bar(a: Number)
var baz: Any
val baz: Any
}

external interface Boo : Foo {
override fun bar()
override fun bar(b: Number)
fun bar(c: String)
override var baz: Number
override val baz: Number
}
30 changes: 30 additions & 0 deletions compiler/test/data/typescript/override/varConflict.d.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// [test] varConflict.kt
@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS")

import kotlin.js.*
import kotlin.js.Json
import org.khronos.webgl.*
import org.w3c.dom.*
import org.w3c.dom.events.*
import org.w3c.dom.parsing.*
import org.w3c.dom.svg.*
import org.w3c.dom.url.*
import org.w3c.fetch.*
import org.w3c.files.*
import org.w3c.notifications.*
import org.w3c.performance.*
import org.w3c.workers.*
import org.w3c.xhr.*

external open class B {
open val x: Boolean
}

external interface C {
val x: Boolean?
get() = definedExternally
}

external open class A : B, C {
override val x: Boolean
}
11 changes: 11 additions & 0 deletions compiler/test/data/typescript/override/varConflict.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
declare class B {
x: boolean
}

declare interface C {
x?: boolean
}

declare class A extends B implements C {
x: boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// [test] varConflictWithoutPropertyInChild.kt
@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS")

import kotlin.js.*
import kotlin.js.Json
import org.khronos.webgl.*
import org.w3c.dom.*
import org.w3c.dom.events.*
import org.w3c.dom.parsing.*
import org.w3c.dom.svg.*
import org.w3c.dom.url.*
import org.w3c.fetch.*
import org.w3c.files.*
import org.w3c.notifications.*
import org.w3c.performance.*
import org.w3c.workers.*
import org.w3c.xhr.*

external open class B {
open val x: Boolean
}

external interface C {
val x: Boolean?
get() = definedExternally
}

external open class A : B, C {
override val x: Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
declare class B {
x: boolean
}

declare interface C {
x?: boolean
}

declare class A extends B implements C {

}
25 changes: 25 additions & 0 deletions compiler/test/data/typescript/override/variance.d.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// [test] variance.kt
@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS")

import kotlin.js.*
import kotlin.js.Json
import org.khronos.webgl.*
import org.w3c.dom.*
import org.w3c.dom.events.*
import org.w3c.dom.parsing.*
import org.w3c.dom.svg.*
import org.w3c.dom.url.*
import org.w3c.fetch.*
import org.w3c.files.*
import org.w3c.notifications.*
import org.w3c.performance.*
import org.w3c.workers.*
import org.w3c.xhr.*

external open class Observable<T> {
open val source: Observable<out Any?>
}

external open class ConnectableObservable<T> : Observable<T> {
override val source: Observable<T>
}
7 changes: 7 additions & 0 deletions compiler/test/data/typescript/override/variance.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export declare class Observable<T> {
source: Observable<any>
}

export declare class ConnectableObservable<T> extends Observable<T> {
source: Observable<T>
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ class DescriptorTests {
"misc/stringTypeInAlias",
"qualifiedNames/extendingEntityFromParentModule",
"stdlib/convertTsStdlib",
"typePredicate/simple"
"typePredicate/simple",
"override/varConflict",
"override/varConflictWithoutPropertyInChild"
).map { it.replace("/", System.getProperty("file.separator")) }

@JvmStatic
Expand Down
1 change: 1 addition & 0 deletions model-lowerings-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies {
implementation(project(":ast-common"))
implementation(project(":ast-model"))
implementation(project(":graphs"))
implementation(project(":itertools"))
implementation(project(":logging"))
implementation(project(":ownerContext"))
implementation(project(":panic"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.jetbrains.dukat.astModel.SourceSetModel
import org.jetbrains.dukat.graphs.Graph
import org.jetbrains.dukat.model.commonLowerings.overrides.InheritanceContext

private fun ModelContext.buildInheritanceGraph(): Graph<ClassLikeModel> {
internal fun ModelContext.buildInheritanceGraph(): Graph<ClassLikeModel> {
val graph = Graph<ClassLikeModel>()

getClassLikeIterable().forEach { classLike ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal class OverrideTypeChecker(
private val context: ModelContext,
private val inheritanceContext: InheritanceContext,
private val declaration: ClassLikeModel,
private val parent: ClassLikeModel
private val parent: ClassLikeModel?
) {

private fun TypeModel.isDynamic(): Boolean {
Expand Down Expand Up @@ -154,7 +154,7 @@ internal class OverrideTypeChecker(
}
}

private fun TypeModel.isOverridingReturnType(otherParameterType: TypeModel, box: TypeValueModel? = null): Boolean {
fun TypeModel.isOverridingReturnType(otherParameterType: TypeModel, box: TypeValueModel? = null): Boolean {
val inbox = (box == null || box.value == IdentifierEntity("Array"))

if (isEquivalent(otherParameterType) && inbox) {
Expand Down Expand Up @@ -192,7 +192,7 @@ internal class OverrideTypeChecker(
}
}
} else {
return fqName == otherParameterType.fqName
return fqName == otherParameterType.fqName && !(otherParameterType.nullable && !nullable)
}
}

Expand Down Expand Up @@ -287,6 +287,9 @@ internal class OverrideTypeChecker(
}

private fun TypeParameterReferenceModel.isEquivalent(otherTypeParameter: TypeParameterReferenceModel): Boolean {
if (parent == null) {
return false
}
val otherTypeParameterIndex = parent.typeParameters.indexOfFirst {
val type = it.type
type is TypeValueModel && type.value == otherTypeParameter.name
Expand All @@ -297,7 +300,7 @@ internal class OverrideTypeChecker(
return this == relevantHeritage?.typeParams?.getOrNull(otherTypeParameterIndex)
}

private fun TypeModel.isEquivalent(otherParameterType: TypeModel): Boolean {
fun TypeModel.isEquivalent(otherParameterType: TypeModel): Boolean {
if (this == otherParameterType) {
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.jetbrains.dukat.stdlib.KLIBROOT
import org.jetbrains.dukat.stdlib.TSLIBROOT
import org.jetbrains.dukat.stdlibGenerator.generated.stdlibClassMethodsMap

private data class MemberData(val fqName: NameEntity?, val memberModel: MemberModel, val ownerModel: ClassLikeModel)
internal data class MemberData(val fqName: NameEntity?, val memberModel: MemberModel, val ownerModel: ClassLikeModel)

internal enum class MemberOverrideStatus {
IS_OVERRIDE,
Expand All @@ -37,19 +37,28 @@ internal enum class MemberOverrideStatus {
IS_IMPOSSIBLE
}

private fun ModelContext.buildInheritanceGraph(): Graph<ClassLikeModel> {
val graph = Graph<ClassLikeModel>()
internal fun ClassLikeModel.getKnownParents(context: ModelContext): List<ResolvedClassLike<out ClassLikeModel>> {
return context.getAllParents(this)
}

internal fun ClassLikeModel.allParentMembers(context: ModelContext): Map<NameEntity?, List<MemberData>> {
val memberMap = mutableMapOf<NameEntity?, MutableList<MemberData>>()

getClassLikeIterable().forEach { classLike ->
getAllParents(classLike).forEach { resolvedClassLike ->
graph.addEdge(classLike, resolvedClassLike.classLike)
getKnownParents(context).forEach { resolvedClassLike ->
resolvedClassLike.classLike.members.forEach {
when (it) {
is NamedModel -> {
if (!resolvedClassLike.existsOnlyInTsStdlib(it)) {
memberMap.getOrPut(it.name) { mutableListOf() }.add(MemberData(resolvedClassLike.fqName, it, resolvedClassLike.classLike))
}
}
}
}
}

return graph
return memberMap
}


private fun <T : ClassLikeModel> ResolvedClassLike<T>.existsOnlyInTsStdlib(member: NamedModel): Boolean {
return (fqName?.startsWith(TSLIBROOT) == true && (stdlibClassMethodsMap.containsKey(classLike.name)) && (stdlibClassMethodsMap[classLike.name]?.contains(member.name) == false))
}
Expand Down Expand Up @@ -114,10 +123,6 @@ private class ClassLikeOverrideResolver(
)
}

private fun ClassLikeModel.getKnownParents(): List<ResolvedClassLike<out ClassLikeModel>> {
return context.getAllParents(this)
}

private fun MethodModel.removeDefaultParamValues(override: NameEntity?): MethodModel {
return copy(override = override, parameters = parameters.map { it.copy(initializer = null) })
}
Expand Down Expand Up @@ -236,24 +241,6 @@ private class ClassLikeOverrideResolver(
}
}

private fun ClassLikeModel.allParentMembers(): Map<NameEntity?, List<MemberData>> {
val memberMap = mutableMapOf<NameEntity?, MutableList<MemberData>>()

getKnownParents().forEach { resolvedClassLike ->
resolvedClassLike.classLike.members.forEach {
when (it) {
is NamedModel -> {
if (!resolvedClassLike.existsOnlyInTsStdlib(it)) {
memberMap.getOrPut(it.name) { mutableListOf() }.add(MemberData(resolvedClassLike.fqName, it, resolvedClassLike.classLike))
}
}
}
}
}

return memberMap
}

private fun MethodModel.isSpecialCase(): Boolean {
val returnType = type

Expand All @@ -277,7 +264,7 @@ private class ClassLikeOverrideResolver(
}

fun resolve(): ClassLikeModel {
val parentMembers = classLike.allParentMembers()
val parentMembers = classLike.allParentMembers(context)

val membersLowered = classLike.members.flatMap { member ->
member.lowerOverrides(parentMembers)
Expand Down
Loading