From fa152593eff708880a4b0d253501e28ec117b570 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 25 May 2020 16:30:22 +0300 Subject: [PATCH] Fix false-positive `@Throws` error diagnostics in Native Don't report THROWS_LIST_EMPTY and MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND caused by unresolved arguments. --- .../diagnostics/nativeTests/throws.kt | 68 +++++++++++++++++++ .../diagnostics/nativeTests/throws.txt | 23 +++++++ .../konan/diagnostics/NativeThrowsChecker.kt | 16 ++++- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/compiler/testData/diagnostics/nativeTests/throws.kt b/compiler/testData/diagnostics/nativeTests/throws.kt index 2bac57550f6..41586534ac0 100644 --- a/compiler/testData/diagnostics/nativeTests/throws.kt +++ b/compiler/testData/diagnostics/nativeTests/throws.kt @@ -37,6 +37,41 @@ fun foo() {} @Throws() fun throwsEmptyParens() {} +@Throws(UnresolvedException::class) +fun throwsUnresolved() {} + +@Throws(exceptionClasses = UnresolvedException::class) +fun throwsNamedUnresolved() {} + +@Throws(exceptionClasses = []) +fun throwsNamedEmptyLiteral() {} + +@Throws(exceptionClasses = arrayOf()) +fun throwsNamedEmptyArrayOf() {} + +@Throws(*[]) +fun throwsSpreadEmptyLiteral() {} + +@Throws(*arrayOf()) +fun throwsSpreadEmptyArrayOf() {} + +@Throws(exceptionClasses = [UE::class]) +fun throwsNamedLiteralWithUnresolved() {} + +@Throws(exceptionClasses = arrayOf(UE::class)) +fun throwsNamedArrayOfUnresolved() {} + +@Throws(*[UE::class]) +fun throwsSpreadLiteralWithUnresolved() {} + +@Throws(*arrayOf(UE::class)) +fun throwsSpreadArrayOfUnresolved() {} + +typealias UEAlias = UE + +@Throws(UEAlias::class) +fun throwsTypealiasToUnresolved() {} + interface Base0 { fun foo() } @@ -244,6 +279,39 @@ suspend fun suspendDoesNotThrowCancellationException1() {} @Throws(Exception1::class, Exception2::class) suspend fun suspendDoesNotThrowCancellationException2() {} +@Throws(UE::class) +suspend fun suspendThrowsUnresolved() {} + +@Throws(exceptionClasses = UE::class) +suspend fun suspendThrowsNamedUnresolved() {} + +@Throws(exceptionClasses = []) +suspend fun suspendThrowsNamedEmptyLiteral() {} + +@Throws(exceptionClasses = arrayOf()) +suspend fun suspendThrowsNamedEmptyArrayOf() {} + +@Throws(*[]) +suspend fun suspendThrowsSpreadEmptyLiteral() {} + +@Throws(*arrayOf()) +suspend fun suspendThrowsSpreadEmptyArrayOf() {} + +@Throws(exceptionClasses = [UE::class]) +suspend fun suspendThrowsNamedLiteralWithUnresolved() {} + +@Throws(exceptionClasses = arrayOf(UE::class)) +suspend fun suspendThrowsNamedArrayOfUnresolved() {} + +@Throws(*[UE::class]) +suspend fun suspendThrowsSpreadLiteralWithUnresolved() {} + +@Throws(*arrayOf(UE::class)) +suspend fun suspendThrowsSpreadArrayOfUnresolved() {} + +@Throws(UEAlias::class) +suspend fun suspendThrowsTypealiasToUnresolved() {} + @Throws(Exception1::class, CancellationException::class) suspend fun suspendThrowsCancellationException1() {} diff --git a/compiler/testData/diagnostics/nativeTests/throws.txt b/compiler/testData/diagnostics/nativeTests/throws.txt index 70d66771eac..8315d0adb8c 100644 --- a/compiler/testData/diagnostics/nativeTests/throws.txt +++ b/compiler/testData/diagnostics/nativeTests/throws.txt @@ -12,15 +12,37 @@ package @kotlin.Throws(exceptionClasses = {kotlin.IllegalStateException::class}) public suspend fun suspendThrowsIllegalStateException1(): kotlin.Unit @kotlin.Throws(exceptionClasses = {Exception2::class, kotlin.IllegalStateException::class}) public suspend fun suspendThrowsIllegalStateException2(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.IllegalStateException::class}) public suspend fun suspendThrowsIllegalStateExceptionTypealias(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsNamedArrayOfUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsNamedEmptyArrayOf(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsNamedEmptyLiteral(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsNamedLiteralWithUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsNamedUnresolved(): kotlin.Unit @kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsNothing(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.RuntimeException::class}) public suspend fun suspendThrowsRuntimeException1(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.RuntimeException::class, Exception3::class}) public suspend fun suspendThrowsRuntimeException2(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.RuntimeException::class}) public suspend fun suspendThrowsRuntimeExceptionTypealias(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsSpreadArrayOfUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsSpreadEmptyArrayOf(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsSpreadEmptyLiteral(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsSpreadLiteralWithUnresolved(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.Throwable::class}) public suspend fun suspendThrowsThrowable1(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.Throwable::class, Exception2::class}) public suspend fun suspendThrowsThrowable2(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.Throwable::class, kotlin.coroutines.cancellation.CancellationException::class}) public suspend fun suspendThrowsThrowable3(): kotlin.Unit @kotlin.Throws(exceptionClasses = {kotlin.Throwable::class}) public suspend fun suspendThrowsThrowableTypealias(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsTypealiasToUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public suspend fun suspendThrowsUnresolved(): kotlin.Unit @kotlin.Throws(exceptionClasses = {}) public fun throwsEmptyParens(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsNamedArrayOfUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsNamedEmptyArrayOf(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsNamedEmptyLiteral(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsNamedLiteralWithUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsNamedUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsSpreadArrayOfUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsSpreadEmptyArrayOf(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsSpreadEmptyLiteral(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsSpreadLiteralWithUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsTypealiasToUnresolved(): kotlin.Unit +@kotlin.Throws(exceptionClasses = {}) public fun throwsUnresolved(): kotlin.Unit public fun withLocalClass(): kotlin.Unit public interface Base0 { @@ -411,6 +433,7 @@ public typealias ExceptionAlias = kotlin.Exception public typealias IllegalStateExceptionAlias = kotlin.IllegalStateException public typealias RuntimeExceptionAlias = kotlin.RuntimeException public typealias ThrowableAlias = kotlin.Throwable +public typealias UEAlias = [ERROR : UE] package kotlin { diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeThrowsChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeThrowsChecker.kt index 7274c4e2124..ae75c0ee95e 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeThrowsChecker.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeThrowsChecker.kt @@ -13,10 +13,15 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.lexer.KtTokens.SUSPEND_KEYWORD import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.Call import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.StatementFilter import org.jetbrains.kotlin.resolve.annotations.KOTLIN_THROWS_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.callUtil.hasUnresolvedArguments import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext import org.jetbrains.kotlin.resolve.constants.ArrayValue @@ -41,12 +46,16 @@ object NativeThrowsChecker : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { val throwsAnnotation = descriptor.annotations.findAnnotation(throwsFqName) - val reportLocation = throwsAnnotation?.let { DescriptorToSourceUtils.getSourceFromAnnotation(it) } ?: declaration + val throwsAnnotationEntry = throwsAnnotation?.let { DescriptorToSourceUtils.getSourceFromAnnotation(it) } + val reportLocation = throwsAnnotationEntry ?: declaration if (!checkInheritance(declaration, descriptor, context, throwsAnnotation, reportLocation)) return if (throwsAnnotation == null) return + val bindingContext = context.trace.bindingContext + if (throwsAnnotationEntry?.getCall(bindingContext)?.hasUnresolvedArgumentsRecursive(bindingContext) == true) return + val classes = throwsAnnotation.getVariadicArguments() if (classes.isEmpty()) { context.trace.report(ErrorsNative.THROWS_LIST_EMPTY.on(reportLocation)) @@ -134,3 +143,8 @@ object NativeThrowsChecker : DeclarationChecker { } } + +private fun Call.hasUnresolvedArgumentsRecursive(context: BindingContext): Boolean { + return this.hasUnresolvedArguments(context, StatementFilter.NONE) || + valueArguments.any { it.getArgumentExpression()?.getCall(context)?.hasUnresolvedArgumentsRecursive(context) == true } +}