[ObjCExport] Implement getSuperClassSymbolNotAny util

KT-64226
This commit is contained in:
Sebastian Sellmair
2023-12-14 13:11:43 +01:00
committed by Space Team
parent 9909940b24
commit 66533141b8
2 changed files with 130 additions and 0 deletions
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.objcexport.analysisApiUtils
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.types.KtClassType
import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier
/**
* Tries to find the superclass [KtClassOrObjectSymbol] symbol which is *not* kotlin.Any
*
* e.g.
* ```kotlin
* abstract class A
*
* class B: A
*
* fun example() {
* val symbolOfB = // ...
* val symbolOfA = symbolOfB.getSuperClassSymbolNotAny()
* }
* ```
*/
context(KtAnalysisSession)
internal fun KtClassOrObjectSymbol.getSuperClassSymbolNotAny(): KtClassOrObjectSymbol? {
return superTypes.firstNotNullOfOrNull find@{ superType ->
if (superType.isAny) return@find null
if (superType is KtClassType) {
val classifier = superType.qualifiers.firstNotNullOfOrNull { qualifier ->
(qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier)?.symbol
}
if (classifier is KtClassOrObjectSymbol && classifier.classKind.isClass) return@find classifier
}
null
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.objcexport.tests
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class GetSuperClassSymbolNotAnyTest(
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
) {
@Test
fun `test - no declared superclass - returns null`() {
val file = inlineSourceCodeAnalysis.createKtFile("""class Foo""")
analyze(file) {
val foo = file.getClassOrFail("Foo")
assertNull(foo.getSuperClassSymbolNotAny())
}
}
@Test
fun `test - single abstract super class`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
abstract class Bar
class Foo: Bar()
""".trimIndent()
)
analyze(file) {
val fooSymbol = file.getClassOrFail("Foo")
val barSymbol = fooSymbol.getSuperClassSymbolNotAny()
assertEquals(barSymbol, file.getClassOrFail("Bar"))
}
}
@Test
fun `test - multiple abstract classes`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
abstract class A
abstract class B: A()
class C: B()
""".trimIndent()
)
analyze(file) {
val aSymbol = file.getClassOrFail("A")
val bSymbol = file.getClassOrFail("B")
val cSymbol = file.getClassOrFail("C")
assertEquals(cSymbol.getSuperClassSymbolNotAny(), bSymbol)
assertEquals(bSymbol.getSuperClassSymbolNotAny(), aSymbol)
assertNull(aSymbol.getSuperClassSymbolNotAny())
}
}
@Test
fun `test - abstract class and interfaces`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
abstract class A
interface I1
interface I2
class Foo: I1, I2, A()
""".trimIndent()
)
analyze(file) {
val aSymbol = file.getClassOrFail("A")
val i1Symbol = file.getClassOrFail("I1")
val i2Symbol = file.getClassOrFail("I2")
val fooSymbol = file.getClassOrFail("Foo")
assertEquals(fooSymbol.getSuperClassSymbolNotAny(), aSymbol)
assertNull(i1Symbol.getSuperClassSymbolNotAny())
assertNull(i2Symbol.getSuperClassSymbolNotAny())
}
}
}