[ObjCExport] Fix suspend function and @Throws translations
KT-66115, KT-66239
This commit is contained in:
committed by
Space Team
parent
b7d1126861
commit
42d8db1c52
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.annotations.KtArrayAnnotationValue
|
||||||
|
import org.jetbrains.kotlin.analysis.api.annotations.KtKClassAnnotationValue
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [Throws] classIds defined only for current function
|
||||||
|
*
|
||||||
|
* See [effectiveThrows]
|
||||||
|
* See K1: org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.getDefinedThrows
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
internal val KtFunctionLikeSymbol.definedThrows: List<ClassId>
|
||||||
|
get() {
|
||||||
|
if (isSuspend) return listOf(ClassId.topLevel(KonanFqNames.cancellationException))
|
||||||
|
if (!hasThrowsAnnotation) return emptyList()
|
||||||
|
|
||||||
|
val throwsAnnotations = annotationsList.annotations
|
||||||
|
.filter { annotation -> annotation.classId?.asSingleFqName() == KonanFqNames.throws }
|
||||||
|
.asSequence()
|
||||||
|
|
||||||
|
return throwsAnnotations
|
||||||
|
.flatMap { annotation -> annotation.arguments }
|
||||||
|
.map { argument -> argument.expression }
|
||||||
|
.filterIsInstance<KtArrayAnnotationValue>()
|
||||||
|
.flatMap { arrayAnnotationValue -> arrayAnnotationValue.values }
|
||||||
|
.filterIsInstance<KtKClassAnnotationValue.KtNonLocalKClassAnnotationValue>()
|
||||||
|
.map { it.classId }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns root [Throws] classIds
|
||||||
|
*
|
||||||
|
* ```kotlin
|
||||||
|
* class A {
|
||||||
|
* @Throws(ExcA)
|
||||||
|
* foo()
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class B: A {
|
||||||
|
* @Throws(ExcB)
|
||||||
|
* override foo()
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class C: B {
|
||||||
|
* @Throws(ExcC)
|
||||||
|
* override foo()
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* C.foo.effectiveThrows = [ExcA]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* See [definedThrows]
|
||||||
|
* See K1: [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.getEffectiveThrows]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
internal val KtFunctionLikeSymbol.effectiveThrows: List<ClassId>
|
||||||
|
get() {
|
||||||
|
getAllOverriddenSymbols().firstOrNull()?.let { return (it as KtFunctionLikeSymbol).effectiveThrows }
|
||||||
|
return definedThrows
|
||||||
|
}
|
||||||
+20
-7
@@ -20,8 +20,14 @@ internal fun KtFunctionLikeSymbol.getFunctionMethodBridge(): MethodBridge {
|
|||||||
valueParameters += bridgeParameter(it.returnType)
|
valueParameters += bridgeParameter(it.returnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this is KtFunctionSymbol && isSuspend) {
|
if (isSuspend) {
|
||||||
valueParameters += MethodBridgeValueParameter.SuspendCompletion(false)
|
valueParameters += MethodBridgeValueParameter.SuspendCompletion(true)
|
||||||
|
} else if (hasThrowsAnnotation) {
|
||||||
|
// Add error out parameter before tail block parameters. The convention allows this.
|
||||||
|
// Placing it after would trigger https://bugs.swift.org/browse/SR-12201
|
||||||
|
// (see also https://github.com/JetBrains/kotlin-native/issues/3825).
|
||||||
|
val tailBlocksCount = valueParameters.reversed().takeWhile { it.isBlockPointer() }.count()
|
||||||
|
valueParameters.add(valueParameters.size - tailBlocksCount, MethodBridgeValueParameter.ErrorOutParameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MethodBridge(
|
return MethodBridge(
|
||||||
@@ -125,13 +131,11 @@ private fun bridgeFunctionType(type: KtType): TypeBridge {
|
|||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
||||||
|
|
||||||
val convertExceptionsToErrors = false // TODO: Add exception handling and return MethodBridge.ReturnValue.WithError.ZeroForError
|
|
||||||
|
|
||||||
if (isArrayConstructor) {
|
if (isArrayConstructor) {
|
||||||
return MethodBridge.ReturnValue.Instance.FactoryResult
|
return MethodBridge.ReturnValue.Instance.FactoryResult
|
||||||
} else if (isConstructor) {
|
} else if (isConstructor) {
|
||||||
val result = MethodBridge.ReturnValue.Instance.InitResult
|
val result = MethodBridge.ReturnValue.Instance.InitResult
|
||||||
if (convertExceptionsToErrors) {
|
if (hasThrowsAnnotation) {
|
||||||
MethodBridge.ReturnValue.WithError.ZeroForError(result, successMayBeZero = false)
|
MethodBridge.ReturnValue.WithError.ZeroForError(result, successMayBeZero = false)
|
||||||
} else {
|
} else {
|
||||||
return result
|
return result
|
||||||
@@ -151,7 +155,7 @@ private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if (returnType.isUnit || returnType.isNothing) {
|
if (returnType.isUnit || returnType.isNothing) {
|
||||||
return if (convertExceptionsToErrors) {
|
return if (hasThrowsAnnotation) {
|
||||||
MethodBridge.ReturnValue.WithError.Success
|
MethodBridge.ReturnValue.WithError.Success
|
||||||
} else {
|
} else {
|
||||||
MethodBridge.ReturnValue.Void
|
MethodBridge.ReturnValue.Void
|
||||||
@@ -162,7 +166,7 @@ private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
|||||||
val returnTypeBridge = bridgeType(returnType)
|
val returnTypeBridge = bridgeType(returnType)
|
||||||
val successReturnValueBridge = MethodBridge.ReturnValue.Mapped(returnTypeBridge)
|
val successReturnValueBridge = MethodBridge.ReturnValue.Mapped(returnTypeBridge)
|
||||||
|
|
||||||
return if (convertExceptionsToErrors) {
|
return if (hasThrowsAnnotation) {
|
||||||
val canReturnZero = !returnTypeBridge.isReferenceOrPointer() || returnType.canBeNull
|
val canReturnZero = !returnTypeBridge.isReferenceOrPointer() || returnType.canBeNull
|
||||||
MethodBridge.ReturnValue.WithError.ZeroForError(
|
MethodBridge.ReturnValue.WithError.ZeroForError(
|
||||||
successReturnValueBridge,
|
successReturnValueBridge,
|
||||||
@@ -180,3 +184,12 @@ private fun TypeBridge.isReferenceOrPointer(): Boolean = when (this) {
|
|||||||
ReferenceBridge, is BlockPointerBridge -> true
|
ReferenceBridge, is BlockPointerBridge -> true
|
||||||
is ValueTypeBridge -> this.objCValueType == ObjCValueType.POINTER
|
is ValueTypeBridge -> this.objCValueType == ObjCValueType.POINTER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MethodBridgeValueParameter.isBlockPointer(): Boolean = when (this) {
|
||||||
|
is MethodBridgeValueParameter.Mapped -> when (this.bridge) {
|
||||||
|
ReferenceBridge, is ValueTypeBridge -> false
|
||||||
|
is BlockPointerBridge -> true
|
||||||
|
}
|
||||||
|
MethodBridgeValueParameter.ErrorOutParameter -> false
|
||||||
|
is MethodBridgeValueParameter.SuspendCompletion -> true
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
|
|
||||||
|
internal val KtCallableSymbol.hasThrowsAnnotation: Boolean
|
||||||
|
get() {
|
||||||
|
return annotationsList.hasAnnotation(ClassId.topLevel(KonanFqNames.throws))
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
||||||
|
|
||||||
|
internal val KtFunctionLikeSymbol.isSuspend: Boolean
|
||||||
|
get() = if (this is KtFunctionSymbol) isSuspend else false
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
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.backend.konan.objcexport.ObjCClassType
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCMethod
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
internal val KtClassOrObjectSymbol.isThrowable: Boolean
|
||||||
|
get() {
|
||||||
|
val classId = classIdIfNonLocal ?: return false
|
||||||
|
return classId.isThrowable
|
||||||
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
internal val ClassId.isThrowable: Boolean
|
||||||
|
get() {
|
||||||
|
return StandardNames.FqNames.throwable == this.asSingleFqName()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun buildThrowableAsErrorMethod(): ObjCMethod {
|
||||||
|
return ObjCMethod(
|
||||||
|
comment = null,
|
||||||
|
isInstanceMethod = true,
|
||||||
|
returnType = ObjCClassType("NSError"),
|
||||||
|
selectors = listOf("asError"),
|
||||||
|
parameters = emptyList(),
|
||||||
|
attributes = listOf(swiftNameAttribute("asError()")),
|
||||||
|
origin = null
|
||||||
|
)
|
||||||
|
}
|
||||||
+7
@@ -6,6 +6,9 @@ import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
|
|||||||
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
|
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.buildThrowableAsErrorMethod
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isThrowable
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
@@ -40,6 +43,10 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
|||||||
if (classKind == KtClassKind.ENUM_CLASS) {
|
if (classKind == KtClassKind.ENUM_CLASS) {
|
||||||
this += translateEnumMembers()
|
this += translateEnumMembers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isThrowable) {
|
||||||
|
this += buildThrowableAsErrorMethod()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val categoryName: String? = null
|
val categoryName: String? = null
|
||||||
|
|||||||
+7
-13
@@ -6,9 +6,11 @@
|
|||||||
package org.jetbrains.kotlin.objcexport
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.annotations.*
|
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo
|
||||||
|
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList
|
||||||
|
import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue
|
||||||
|
import org.jetbrains.kotlin.analysis.api.annotations.renderAsSourceCode
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
|
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
|
||||||
@@ -19,7 +21,9 @@ import org.jetbrains.kotlin.backend.konan.objcexport.plus
|
|||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.effectiveThrows
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getObjCDocumentedAnnotations
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getObjCDocumentedAnnotations
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isSuspend
|
||||||
|
|
||||||
|
|
||||||
context(KtAnalysisSession)
|
context(KtAnalysisSession)
|
||||||
@@ -36,10 +40,8 @@ internal fun KtAnnotationsList.translateToObjCComment(): ObjCComment? {
|
|||||||
*/
|
*/
|
||||||
context(KtAnalysisSession)
|
context(KtAnalysisSession)
|
||||||
internal fun KtFunctionLikeSymbol.translateToObjCComment(bridge: MethodBridge, parameters: List<ObjCParameter>): ObjCComment? {
|
internal fun KtFunctionLikeSymbol.translateToObjCComment(bridge: MethodBridge, parameters: List<ObjCParameter>): ObjCComment? {
|
||||||
val isSuspend: Boolean = if (this is KtFunctionSymbol) this.isSuspend else false
|
|
||||||
|
|
||||||
val throwsComments = if (isSuspend || bridge.returnsError) {
|
val throwsComments = if (isSuspend || bridge.returnsError) {
|
||||||
val effectiveThrows = getEffectiveThrows(this).toSet()
|
val effectiveThrows = effectiveThrows.toSet()
|
||||||
when {
|
when {
|
||||||
effectiveThrows.contains(StandardClassIds.Throwable) -> {
|
effectiveThrows.contains(StandardClassIds.Throwable) -> {
|
||||||
listOf("@note This method converts all Kotlin exceptions to errors.")
|
listOf("@note This method converts all Kotlin exceptions to errors.")
|
||||||
@@ -105,14 +107,6 @@ private fun KtNamedAnnotationValue.render(): String {
|
|||||||
return "$name=${expression.renderAsSourceCode()}"
|
return "$name=${expression.renderAsSourceCode()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Not implemented [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.getEffectiveThrows]
|
|
||||||
*/
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
private fun getEffectiveThrows(method: KtFunctionLikeSymbol): Sequence<ClassId> {
|
|
||||||
return emptySequence()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.visibilityComments]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.visibilityComments]
|
||||||
*/
|
*/
|
||||||
|
|||||||
+7
-2
@@ -20,6 +20,7 @@ fun KtClassOrObjectSymbol.translateToObjCConstructors(): List<ObjCMethod> {
|
|||||||
result += getDeclaredMemberScope().getConstructors()
|
result += getDeclaredMemberScope().getConstructors()
|
||||||
.filter { !it.hasExportForCompilerAnnotation }
|
.filter { !it.hasExportForCompilerAnnotation }
|
||||||
.filter { it.isVisibleInObjC() }
|
.filter { it.isVisibleInObjC() }
|
||||||
|
.sortedWith(StableCallableOrder)
|
||||||
.map { it.buildObjCMethod() }
|
.map { it.buildObjCMethod() }
|
||||||
|
|
||||||
/* Create special 'alloc' constructors */
|
/* Create special 'alloc' constructors */
|
||||||
@@ -61,9 +62,13 @@ fun KtClassOrObjectSymbol.translateToObjCConstructors(): List<ObjCMethod> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.size == 1 && result.first().name == "init") {
|
val initIndex = result.indexOfFirst { it.name == "init" }
|
||||||
|
if (initIndex > -1) {
|
||||||
|
/**
|
||||||
|
* If there is "init" we need always add this special constructor
|
||||||
|
*/
|
||||||
result.add(
|
result.add(
|
||||||
ObjCMethod(
|
initIndex + 1, ObjCMethod(
|
||||||
comment = null,
|
comment = null,
|
||||||
origin = getObjCExportStubOrigin(),
|
origin = getObjCExportStubOrigin(),
|
||||||
isInstanceMethod = false,
|
isInstanceMethod = false,
|
||||||
|
|||||||
+7
@@ -28,6 +28,12 @@ internal val StableFunctionOrder: Comparator<KtFunctionSymbol>
|
|||||||
*/
|
*/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val StableConstructorOrder: Comparator<KtConstructorSymbol>
|
||||||
|
get() = compareBy(
|
||||||
|
{ it.valueParameters.size },
|
||||||
|
// TODO NOW! { KonanManglerDesc.run { it.signatureString(false) } }
|
||||||
|
)
|
||||||
|
|
||||||
internal val StableClassifierOrder: Comparator<KtClassifierSymbol> =
|
internal val StableClassifierOrder: Comparator<KtClassifierSymbol> =
|
||||||
compareBy<KtClassifierSymbol> { classifier ->
|
compareBy<KtClassifierSymbol> { classifier ->
|
||||||
if (classifier !is KtClassOrObjectSymbol) return@compareBy 0
|
if (classifier !is KtClassOrObjectSymbol) return@compareBy 0
|
||||||
@@ -45,6 +51,7 @@ internal val StableCallableOrder: Comparator<KtCallableSymbol> = compareBy<KtCal
|
|||||||
else -> 3
|
else -> 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.thenComparing(StableConstructorOrder)
|
||||||
.thenComparing(StablePropertyOrder)
|
.thenComparing(StablePropertyOrder)
|
||||||
.thenComparing(StableFunctionOrder)
|
.thenComparing(StableFunctionOrder)
|
||||||
|
|
||||||
|
|||||||
+139
@@ -0,0 +1,139 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.tests
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.analyze
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.definedThrows
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.effectiveThrows
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasThrowsAnnotation
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.getFunctionOrFail
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class ThrowsAnnotationTest(private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis) {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - has throws annotation`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
@Throws
|
||||||
|
fun foo() = Unit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
assertTrue(file.getFunctionOrFail("foo").hasThrowsAnnotation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - override has no throws annotation`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
open class A {
|
||||||
|
@Throws
|
||||||
|
open fun foo() = Unit
|
||||||
|
}
|
||||||
|
class B: A() {
|
||||||
|
override fun foo() = Unit
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
assertTrue(file.getClassOrFail("A").getFunctionOrFail("foo").hasThrowsAnnotation)
|
||||||
|
assertFalse(file.getClassOrFail("B").getFunctionOrFail("foo").hasThrowsAnnotation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - defined function throws`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
@Throws(IllegalStateException::class, RuntimeException::class)
|
||||||
|
fun foo() = Unit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
val foo = file.getFunctionOrFail("foo")
|
||||||
|
assertEquals(listOf("IllegalStateException", "RuntimeException"), foo.definedThrows.mapName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - effective and defined classes throws`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
abstract class A {
|
||||||
|
@Throws(IllegalStateException::class)
|
||||||
|
abstract fun foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B : A() {
|
||||||
|
@Throws(RuntimeException::class)
|
||||||
|
override fun foo() = Unit
|
||||||
|
}
|
||||||
|
class C : B() {
|
||||||
|
@Throws(IndexOutOfBoundsException::class)
|
||||||
|
override fun foo() = Unit
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
|
||||||
|
val fooA = file.getClassOrFail("A").getMemberScope().getFunctionOrFail("foo")
|
||||||
|
assertEquals(listOf("IllegalStateException"), fooA.effectiveThrows.mapName())
|
||||||
|
assertEquals(listOf("IllegalStateException"), fooA.definedThrows.mapName())
|
||||||
|
|
||||||
|
val fooB = file.getClassOrFail("B").getMemberScope().getFunctionOrFail("foo")
|
||||||
|
assertEquals(listOf("IllegalStateException"), fooB.effectiveThrows.mapName())
|
||||||
|
assertEquals(listOf("RuntimeException"), fooB.definedThrows.mapName())
|
||||||
|
|
||||||
|
val fooC = file.getClassOrFail("C").getMemberScope().getFunctionOrFail("foo")
|
||||||
|
assertEquals(listOf("IllegalStateException"), fooC.effectiveThrows.mapName())
|
||||||
|
assertEquals(listOf("IndexOutOfBoundsException"), fooC.definedThrows.mapName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - constructor throws`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
class Foo @Throws(IllegalStateException::class) constructor()
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
val foo = file.getClassOrFail("Foo").getMemberScope().getConstructors().first()
|
||||||
|
assertEquals(listOf("IllegalStateException"), foo.effectiveThrows.mapName())
|
||||||
|
assertEquals(listOf("IllegalStateException"), foo.definedThrows.mapName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - non throws annotation`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
annotation class NonThrows(vararg val exceptionClasses: KClass<out Throwable>)
|
||||||
|
|
||||||
|
@Throws(RuntimeException::class)
|
||||||
|
@NonThrows(IllegalStateException::class)
|
||||||
|
fun foo() = Unit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
val foo = file.getFunctionOrFail("foo")
|
||||||
|
assertEquals(listOf("RuntimeException"), foo.definedThrows.mapName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<ClassId>.mapName(): List<String> {
|
||||||
|
return map { it.shortClassName.asString() }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user