Add Native diagnostics for @Throws suspend fun
`@Throws suspend fun` must have CancellationException accepted. Also add more tests.
This commit is contained in:
+6
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.konan.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
|
||||
private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
||||
@@ -20,6 +21,11 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
||||
ErrorsNative.INCOMPATIBLE_THROWS_INHERITED, "Member inherits different @Throws filters from {0}",
|
||||
Renderers.commaSeparated(Renderers.NAME)
|
||||
)
|
||||
put(
|
||||
ErrorsNative.MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND,
|
||||
"@Throws on suspend declaration must have {0} (or any of its superclasses) listed",
|
||||
Renderer { it.shortName().asString() }
|
||||
)
|
||||
put(
|
||||
ErrorsNative.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY,
|
||||
"@SharedImmutable is applicable only to val with backing field or to property with delegation"
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
@@ -21,6 +22,8 @@ object ErrorsNative {
|
||||
@JvmField
|
||||
val INCOMPATIBLE_THROWS_INHERITED = DiagnosticFactory1.create<KtDeclaration, Collection<DeclarationDescriptor>>(Severity.ERROR)
|
||||
@JvmField
|
||||
val MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND = DiagnosticFactory1.create<KtElement, FqName>(Severity.ERROR)
|
||||
@JvmField
|
||||
val INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
|
||||
@JvmField
|
||||
val INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
|
||||
|
||||
+36
-1
@@ -6,9 +6,13 @@
|
||||
package org.jetbrains.kotlin.resolve.konan.diagnostics
|
||||
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
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.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
@@ -17,20 +21,45 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
|
||||
object NativeThrowsChecker : DeclarationChecker {
|
||||
private val throwsFqName = KOTLIN_THROWS_ANNOTATION_FQ_NAME
|
||||
|
||||
private val cancellationExceptionFqName = FqName("kotlin.coroutines.cancellation.CancellationException")
|
||||
|
||||
// Note: can't use subtyping, because CancellationException can be missing (e.g. for common code).
|
||||
private val cancellationExceptionAndSupersClassIds = sequenceOf(
|
||||
KotlinBuiltIns.FQ_NAMES.throwable,
|
||||
FqName("kotlin.Exception"),
|
||||
FqName("kotlin.RuntimeException"),
|
||||
FqName("kotlin.IllegalStateException"),
|
||||
cancellationExceptionFqName
|
||||
).map { ClassId.topLevel(it) }.toSet()
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
val throwsAnnotation = descriptor.annotations.findAnnotation(throwsFqName)
|
||||
val reportLocation = throwsAnnotation?.let { DescriptorToSourceUtils.getSourceFromAnnotation(it) } ?: declaration
|
||||
|
||||
if (!checkInheritance(declaration, descriptor, context, throwsAnnotation, reportLocation)) return
|
||||
|
||||
if (throwsAnnotation != null && throwsAnnotation.getVariadicArguments().isEmpty()) {
|
||||
if (throwsAnnotation == null) return
|
||||
|
||||
val classes = throwsAnnotation.getVariadicArguments()
|
||||
if (classes.isEmpty()) {
|
||||
context.trace.report(ErrorsNative.THROWS_LIST_EMPTY.on(reportLocation))
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration.hasModifier(SUSPEND_KEYWORD) && classes.none { it.isGlobalClassWithId(cancellationExceptionAndSupersClassIds) }) {
|
||||
context.trace.report(
|
||||
ErrorsNative.MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND.on(
|
||||
reportLocation,
|
||||
cancellationExceptionFqName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,4 +127,10 @@ object NativeThrowsChecker : DeclarationChecker {
|
||||
|
||||
private data class ThrowsFilter(val classes: Set<ConstantValue<*>>?)
|
||||
|
||||
private fun ConstantValue<*>.isGlobalClassWithId(classIds: Set<ClassId>): Boolean =
|
||||
this is KClassValue && when (val value = this.value) {
|
||||
is KClassValue.Value.NormalClass -> value.classId in classIds
|
||||
is KClassValue.Value.LocalClass -> false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user