From ad1fb07a9589ceaabcefb6f9d366fdee5bd22171 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Thu, 23 Jun 2022 18:32:11 +0300 Subject: [PATCH] [FIR] Implement FirNativeThrowsChecker --- .../diagnostics/FirNativeDiagnosticsList.kt | 14 ++ .../diagnostics/native/FirNativeErrors.kt | 8 + .../native/FirNativeErrorsDefaultMessages.kt | 22 ++- .../native/checkers/FirNativeThrowsChecker.kt | 184 ++++++++++++++++++ .../checkers/NativeDeclarationCheckers.kt | 5 + .../diagnostics/nativeTests/throws.fir.kt | 80 ++++---- 6 files changed, 271 insertions(+), 42 deletions(-) create mode 100644 compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt index 6d0f83bb2bc..86c0ae6fb35 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt @@ -7,10 +7,24 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtElement @Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused") @OptIn(PrivateForInline::class) object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") { val ALL by object : DiagnosticGroup("All") { + val THROWS_LIST_EMPTY by error() + val INCOMPATIBLE_THROWS_OVERRIDE by error { + parameter("containingClass") + } + val INCOMPATIBLE_THROWS_INHERITED by error { + parameter>("containingClasses") + } + val MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND by error { + parameter("exceptionName") + } } } \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt index 29df22941dd..82f5f06d732 100644 --- a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt +++ b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt @@ -8,6 +8,10 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics.native import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory import org.jetbrains.kotlin.fir.analysis.diagnostics.* +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtElement /* * This file was generated automatically @@ -16,6 +20,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.* object FirNativeErrors { // All + val THROWS_LIST_EMPTY by error0() + val INCOMPATIBLE_THROWS_OVERRIDE by error1() + val INCOMPATIBLE_THROWS_INHERITED by error1>() + val MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND by error1() init { RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages) diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt index fa372a22a39..6d7e96a9086 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt @@ -6,8 +6,26 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics.native import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap +import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers.TO_STRING import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS +import org.jetbrains.kotlin.fir.analysis.diagnostics.checkMissingMessages +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_THROWS_INHERITED +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_THROWS_OVERRIDE +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.THROWS_LIST_EMPTY object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() { - override val MAP = KtDiagnosticFactoryToRendererMap("FIR") -} \ No newline at end of file + override val MAP = KtDiagnosticFactoryToRendererMap("FIR").also { map -> + map.put(THROWS_LIST_EMPTY, "Throws must have non-empty class list") + map.put(INCOMPATIBLE_THROWS_OVERRIDE, "Member overrides different @Throws filter from {0}", SYMBOL) + map.put(INCOMPATIBLE_THROWS_INHERITED, "Member inherits different @Throws filters from {0}", SYMBOLS) + map.put( + MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND, "@Throws on suspend declaration must have {0} (or any of its superclasses) listed", + TO_STRING + ) + + map.checkMissingMessages(FirNativeErrors) + } +} diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt new file mode 100644 index 00000000000..7f301fab7f2 --- /dev/null +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt @@ -0,0 +1,184 @@ +/* + * Copyright 2010-2022 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.fir.analysis.native.checkers + +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier +import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.references.FirErrorNamedReference +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol +import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.annotations.KOTLIN_THROWS_ANNOTATION_FQ_NAME + +object FirNativeThrowsChecker : FirBasicDeclarationChecker() { + private val throwsFqName = ClassId.topLevel(KOTLIN_THROWS_ANNOTATION_FQ_NAME) + + private val cancellationExceptionFqName = FqName("kotlin.coroutines.cancellation.CancellationException") + + private val cancellationExceptionAndSupersClassIds = setOf( + ClassId.topLevel(StandardNames.FqNames.throwable), + ClassId.topLevel(FqName("kotlin.Exception")), + ClassId.topLevel(FqName("kotlin.RuntimeException")), + ClassId.topLevel(FqName("kotlin.IllegalStateException")), + ClassId.topLevel(cancellationExceptionFqName) + ) + + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + val throwsAnnotation = declaration.getAnnotationByClassId(throwsFqName) as? FirAnnotationCall + + if (!checkInheritance(declaration, throwsAnnotation, context, reporter)) return + + if (throwsAnnotation.hasUnresolvedArgument()) return + + val classTypes = throwsAnnotation?.getClassTypes(context.session) ?: return + + if (classTypes.isEmpty()) { + reporter.reportOn(throwsAnnotation.source, FirNativeErrors.THROWS_LIST_EMPTY, context) + return + } + + if (declaration.hasModifier(KtTokens.SUSPEND_KEYWORD) && classTypes.none { it.classId in cancellationExceptionAndSupersClassIds }) { + reporter.reportOn( + throwsAnnotation.source, + FirNativeErrors.MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND, + cancellationExceptionFqName, + context + ) + } + } + + private fun checkInheritance( + declaration: FirDeclaration, + throwsAnnotation: FirAnnotationCall?, + context: CheckerContext, + reporter: DiagnosticReporter + ): Boolean { + if (declaration !is FirSimpleFunction) return true + + val inherited = getInheritedThrows(declaration, throwsAnnotation, context).entries.distinctBy { it.value } + + if (inherited.size >= 2) { + reporter.reportOn( + declaration.source, + FirNativeErrors.INCOMPATIBLE_THROWS_INHERITED, + inherited.mapNotNull { it.key.containingClass()?.toFirRegularClassSymbol(context.session) }, + context + ) + return false + } + + val (overriddenMember, overriddenThrows) = inherited.firstOrNull() + ?: return true // Should not happen though. + + if (decodeThrowsFilter(throwsAnnotation, context.session) != overriddenThrows) { + val containingClassSymbol = overriddenMember.containingClass()?.toFirRegularClassSymbol(context.session) + if (containingClassSymbol != null) { + reporter.reportOn(throwsAnnotation?.source, FirNativeErrors.INCOMPATIBLE_THROWS_OVERRIDE, containingClassSymbol, context) + } + return false + } + + return true + } + + private fun getInheritedThrows( + function: FirSimpleFunction, + throwsAnnotation: FirAnnotationCall?, + context: CheckerContext + ): Map { + val visited = mutableSetOf() + val result = mutableMapOf() + + fun getInheritedThrows(localThrowsAnnotation: FirAnnotationCall?, localFunctionSymbol: FirNamedFunctionSymbol) { + if (!visited.add(localFunctionSymbol)) return + val containingClassSymbol = localFunctionSymbol.containingClass()?.toFirRegularClassSymbol(context.session) + + if (containingClassSymbol != null) { + val unsubstitutedScope = containingClassSymbol.unsubstitutedScope(context) + unsubstitutedScope.processFunctionsByName(localFunctionSymbol.name) {} + val overriddenFunctions = unsubstitutedScope.getDirectOverriddenFunctions(localFunctionSymbol) + if (localFunctionSymbol == function.symbol || localThrowsAnnotation == null && overriddenFunctions.isNotEmpty()) { + for (overriddenFunction in overriddenFunctions) { + val annotation = if (overriddenFunction.isSubstitutionOrIntersectionOverride) { + null + } else { + overriddenFunction.getAnnotationByClassId(throwsFqName) as? FirAnnotationCall + } + getInheritedThrows(annotation, overriddenFunction) + } + } else { + result[localFunctionSymbol] = decodeThrowsFilter(localThrowsAnnotation, context.session) + } + } + } + + getInheritedThrows(throwsAnnotation, function.symbol) + + return result + } + + private fun FirElement?.hasUnresolvedArgument(): Boolean { + if (this is FirWrappedArgumentExpression) { + return expression.hasUnresolvedArgument() + } + + if (this is FirResolvable) { + if (this.calleeReference is FirErrorNamedReference) { + return true + } + } + + if (this is FirVarargArgumentsExpression) { + for (argument in this.arguments) { + if (argument.hasUnresolvedArgument()) { + return true + } + } + } + + if (this is FirCall) { + for (argument in this.argumentList.arguments) { + if (argument.hasUnresolvedArgument()) { + return true + } + } + } + + return false + } + + private fun decodeThrowsFilter(throwsAnnotation: FirAnnotationCall?, session: FirSession): ThrowsFilter { + return ThrowsFilter(throwsAnnotation?.getClassTypes(session)?.toSet()) + } + + private fun FirAnnotationCall.getClassTypes(session: FirSession): List { + val arguments = argumentList.arguments + return (arguments.firstOrNull() as? FirVarargArgumentsExpression)?.arguments + ?.filterIsInstance() + ?.map { it.arguments.first().typeRef } + ?.filterIsInstance() + ?.map { it.type.fullyExpandedType(session) } + ?: emptyList() + } + + private data class ThrowsFilter(val classes: Set?) +} \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt index 360dbea43b6..f90ec895714 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt @@ -6,6 +6,11 @@ package org.jetbrains.kotlin.fir.analysis.native.checkers import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker object NativeDeclarationCheckers : DeclarationCheckers() { + override val basicDeclarationCheckers: Set + get() = setOf( + FirNativeThrowsChecker + ) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/nativeTests/throws.fir.kt b/compiler/testData/diagnostics/nativeTests/throws.fir.kt index 19bd221be31..b0b4579400f 100644 --- a/compiler/testData/diagnostics/nativeTests/throws.fir.kt +++ b/compiler/testData/diagnostics/nativeTests/throws.fir.kt @@ -31,10 +31,10 @@ class Exception1 : Throwable() class Exception2 : Throwable() class Exception3 : Throwable() -@Throws +@Throws fun foo() {} -@Throws() +@Throws() fun throwsEmptyParens() {} @Throws(UnresolvedException::class) @@ -43,16 +43,16 @@ fun throwsUnresolved() {} @Throws(exceptionClasses = UnresolvedException::class) fun throwsNamedUnresolved() {} -@Throws(exceptionClasses = []) +@Throws(exceptionClasses = []) fun throwsNamedEmptyLiteral() {} -@Throws(exceptionClasses = arrayOf()) +@Throws(exceptionClasses = arrayOf()) fun throwsNamedEmptyArrayOf() {} -@Throws(*[]) +@Throws(*[]) fun throwsSpreadEmptyLiteral() {} -@Throws(*arrayOf()) +@Throws(*arrayOf()) fun throwsSpreadEmptyArrayOf() {} @Throws(exceptionClasses = [UE::class]) @@ -77,7 +77,7 @@ interface Base0 { } class ThrowsOnOverride : Base0 { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } interface Base1 { @@ -85,11 +85,11 @@ interface Base1 { } class InheritsThrowsAndNoThrows : Base0, Base1 { - override fun foo() {} + override fun foo() {} } class OverridesThrowsAndNoThrows : Base0, Base1 { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } class SameThrowsOnOverride : Base1 { @@ -97,11 +97,11 @@ class SameThrowsOnOverride : Base1 { } class DifferentThrowsOnOverride : Base1 { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } class HasThrowsWithEmptyListOnOverride : Base1 { - @Throws override fun foo() {} + @Throws override fun foo() {} } interface Base2 { @@ -109,31 +109,31 @@ interface Base2 { } open class InheritsDifferentThrows1 : Base1, Base2 { - override fun foo() {} + override fun foo() {} } open class OverridesDifferentThrows1_1 : Base1, Base2 { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } open class OverridesDifferentThrows1_2 : Base1, Base2 { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } open class OverridesDifferentThrows1_3 : Base1, Base2 { - @Throws(Exception1::class, Exception2::class) override fun foo() {} + @Throws(Exception1::class, Exception2::class) override fun foo() {} } class InheritsDifferentThrowsThroughSameClass1 : InheritsDifferentThrows1() { - override fun foo() {} + override fun foo() {} } class OverridesDifferentThrowsThroughSameClass1 : InheritsDifferentThrows1() { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } class OverridesDifferentThrowsThroughSameClass2 : InheritsDifferentThrows1() { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } interface Base3 { @@ -141,15 +141,15 @@ interface Base3 { } class InheritsDifferentThrows2 : InheritsDifferentThrows1(), Base3 { - override fun foo() {} + override fun foo() {} } class OverridesDifferentThrows2 : InheritsDifferentThrows1(), Base3 { - @Throws(Exception3::class) override fun foo() {} + @Throws(Exception3::class) override fun foo() {} } open class OverridesDifferentThrows3 : Base1, Base2 { - @Throws(Exception3::class) override fun foo() {} + @Throws(Exception3::class) override fun foo() {} } class InheritsDifferentThrows3 : OverridesDifferentThrows3() { @@ -165,7 +165,7 @@ class OverrideDifferentThrows5 : OverridesDifferentThrows3() { } class OverrideDifferentThrows6 : OverridesDifferentThrows3() { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } interface Base4 { @@ -181,11 +181,11 @@ class OverridesSameThrows : Base1, Base4 { } class OverrideDifferentThrows7 : Base1, Base4 { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } class OverrideDifferentThrows8 : Base1, Base3 { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } interface Base5 { @@ -209,7 +209,7 @@ class OverridesSameThrowsMultiple2 : Base5, Base6 { } class OverridesDifferentThrowsMultiple : Base5, Base6 { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } fun withLocalClass() { @@ -220,11 +220,11 @@ fun withLocalClass() { } class InheritsDifferentThrowsLocal : Base1, Base7() { - override fun foo() {} + override fun foo() {} } class OverridesDifferentThrowsLocal : Base1, Base7() { - @Throws(Exception1::class, LocalException::class) override fun foo() {} + @Throws(Exception1::class, LocalException::class) override fun foo() {} } } @@ -235,24 +235,24 @@ class InheritThrowsOnFakeOverride : ThrowsOnFakeOverride { } class OverrideDifferentThrowsOnFakeOverride : ThrowsOnFakeOverride { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } interface IncompatibleThrowsOnFakeOverride : Base1, Base2 class OverrideIncompatibleThrowsOnFakeOverride1 : IncompatibleThrowsOnFakeOverride { - @Throws(Exception1::class) override fun foo() {} + @Throws(Exception1::class) override fun foo() {} } class OverrideIncompatibleThrowsOnFakeOverride2 : IncompatibleThrowsOnFakeOverride { - @Throws(Exception2::class) override fun foo() {} + @Throws(Exception2::class) override fun foo() {} } class InheritIncompatibleThrowsOnFakeOverride : IncompatibleThrowsOnFakeOverride { - override fun foo() {} + override fun foo() {} } -@Throws +@Throws suspend fun suspendThrowsNothing() {} interface SuspendFun { @@ -262,7 +262,7 @@ interface SuspendFun { class OverrideImplicitThrowsOnSuspendWithExplicit : SuspendFun { // Although `SuspendFun.foo` effectively has `@Throws(CancellationException::class)`, // overriding it with equal explicit `@Throws` is forbidden: - @Throws(CancellationException::class) override suspend fun foo() {} + @Throws(CancellationException::class) override suspend fun foo() {} } interface SuspendFunThrows { @@ -273,10 +273,10 @@ class InheritExplicitThrowsOnSuspend : SuspendFunThrows { override suspend fun foo() {} } -@Throws(Exception1::class) +@Throws(Exception1::class) suspend fun suspendDoesNotThrowCancellationException1() {} -@Throws(Exception1::class, Exception2::class) +@Throws(Exception1::class, Exception2::class) suspend fun suspendDoesNotThrowCancellationException2() {} @Throws(UE::class) @@ -285,16 +285,16 @@ suspend fun suspendThrowsUnresolved() {} @Throws(exceptionClasses = UE::class) suspend fun suspendThrowsNamedUnresolved() {} -@Throws(exceptionClasses = []) +@Throws(exceptionClasses = []) suspend fun suspendThrowsNamedEmptyLiteral() {} -@Throws(exceptionClasses = arrayOf()) +@Throws(exceptionClasses = arrayOf()) suspend fun suspendThrowsNamedEmptyArrayOf() {} -@Throws(*[]) +@Throws(*[]) suspend fun suspendThrowsSpreadEmptyLiteral() {} -@Throws(*arrayOf()) +@Throws(*arrayOf()) suspend fun suspendThrowsSpreadEmptyArrayOf() {} @Throws(exceptionClasses = [UE::class]) @@ -309,7 +309,7 @@ suspend fun suspendThrowsSpreadLiteralWithUnresolved() {} @Throws(*arrayOf(UE::class)) suspend fun suspendThrowsSpreadArrayOfUnresolved() {} -@Throws(UEAlias::class) +@Throws(UEAlias::class) suspend fun suspendThrowsTypealiasToUnresolved() {} @Throws(Exception1::class, CancellationException::class)