Forbid suspend functions annotated with @kotlin.test.Test

#KT-26200 Fixed
This commit is contained in:
Denis Zharkov
2018-08-24 15:19:42 +03:00
parent a18770fbbb
commit b9f141d4aa
7 changed files with 57 additions and 8 deletions
@@ -116,7 +116,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
DynamicReceiverChecker, DynamicReceiverChecker,
DelegationChecker(), DelegationChecker(),
KClassWithIncorrectTypeArgumentChecker, KClassWithIncorrectTypeArgumentChecker,
SuspendOperatorsCheckers, SuspendLimitationsChecker,
InlineClassDeclarationChecker, InlineClassDeclarationChecker,
PropertiesWithBackingFieldsInsideInlineClass(), PropertiesWithBackingFieldsInsideInlineClass(),
AnnotationClassTargetAndRetentionChecker(), AnnotationClassTargetAndRetentionChecker(),
@@ -18,12 +18,17 @@ package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.abbreviationFqName
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.types.getAbbreviation
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
object SuspendOperatorsCheckers : DeclarationChecker { object SuspendLimitationsChecker : DeclarationChecker {
private val UNSUPPORTED_OPERATOR_NAMES = setOf( private val UNSUPPORTED_OPERATOR_NAMES = setOf(
OperatorNameConventions.CONTAINS, OperatorNameConventions.CONTAINS,
OperatorNameConventions.GET, OperatorNameConventions.SET, OperatorNameConventions.GET, OperatorNameConventions.SET,
@@ -31,11 +36,22 @@ object SuspendOperatorsCheckers : DeclarationChecker {
) )
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor is FunctionDescriptor && descriptor.isSuspend && descriptor.isOperator && if (descriptor !is FunctionDescriptor || !descriptor.isSuspend) return
descriptor.name in UNSUPPORTED_OPERATOR_NAMES) {
if (descriptor.isOperator && descriptor.name in UNSUPPORTED_OPERATOR_NAMES) {
declaration.modifierList?.getModifier(KtTokens.OPERATOR_KEYWORD)?.let { declaration.modifierList?.getModifier(KtTokens.OPERATOR_KEYWORD)?.let {
context.trace.report(Errors.UNSUPPORTED.on(it, "suspend operator \"${descriptor.name}\"")) context.trace.report(Errors.UNSUPPORTED.on(it, "suspend operator \"${descriptor.name}\""))
} }
} }
if (descriptor.annotations.any(AnnotationDescriptor::isKotlinTestAnnotation)) {
declaration.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD)?.let {
context.trace.report(Errors.UNSUPPORTED.on(it, "suspend test functions"))
}
}
} }
} }
private val KOTLIN_TEST_TEST_FQNAME = FqName("kotlin.test.Test")
private fun AnnotationDescriptor.isKotlinTestAnnotation() =
fqName == KOTLIN_TEST_TEST_FQNAME || abbreviationFqName == KOTLIN_TEST_TEST_FQNAME
@@ -0,0 +1,20 @@
// SKIP_TXT
// FILE: test.kt
package kotlin.test
annotation class IrrelevantClass
public typealias Test = IrrelevantClass
// FILE: main.kt
import kotlin.test.Test
class A {
@Test
<!UNSUPPORTED!>suspend<!> fun test() {}
}
@Test
<!UNSUPPORTED!>suspend<!> fun test() {}
@@ -1653,6 +1653,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendOverridability.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendOverridability.kt");
} }
@TestMetadata("suspendTest.kt")
public void testSuspendTest() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendTest.kt");
}
@TestMetadata("suspesionInDefaultValue.kt") @TestMetadata("suspesionInDefaultValue.kt")
public void testSuspesionInDefaultValue() throws Exception { public void testSuspesionInDefaultValue() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspesionInDefaultValue.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspesionInDefaultValue.kt");
@@ -1653,6 +1653,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendOverridability.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendOverridability.kt");
} }
@TestMetadata("suspendTest.kt")
public void testSuspendTest() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendTest.kt");
}
@TestMetadata("suspesionInDefaultValue.kt") @TestMetadata("suspesionInDefaultValue.kt")
public void testSuspesionInDefaultValue() throws Exception { public void testSuspesionInDefaultValue() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspesionInDefaultValue.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspesionInDefaultValue.kt");
@@ -18,21 +18,24 @@ package org.jetbrains.kotlin.descriptors.annotations
import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.getAbbreviation
interface AnnotationDescriptor { interface AnnotationDescriptor {
val type: KotlinType val type: KotlinType
val fqName: FqName? val fqName: FqName?
get() = annotationClass?.takeUnless(ErrorUtils::isError)?.fqNameUnsafe?.takeIf(FqNameUnsafe::isSafe)?.toSafe() get() = annotationClass?.takeUnless(ErrorUtils::isError)?.fqNameOrNull()
val allValueArguments: Map<Name, ConstantValue<*>> val allValueArguments: Map<Name, ConstantValue<*>>
val source: SourceElement val source: SourceElement
} }
val AnnotationDescriptor.abbreviationFqName: FqName?
get() = type.getAbbreviation()?.constructor?.declarationDescriptor?.fqNameOrNull()
@@ -221,7 +221,7 @@ val DeclarationDescriptor.parents: Sequence<DeclarationDescriptor>
val CallableMemberDescriptor.propertyIfAccessor: CallableMemberDescriptor val CallableMemberDescriptor.propertyIfAccessor: CallableMemberDescriptor
get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this
fun CallableDescriptor.fqNameOrNull(): FqName? = fqNameUnsafe.takeIf { it.isSafe }?.toSafe() fun DeclarationDescriptor.fqNameOrNull(): FqName? = fqNameUnsafe.takeIf { it.isSafe }?.toSafe()
fun CallableMemberDescriptor.firstOverridden( fun CallableMemberDescriptor.firstOverridden(
useOriginal: Boolean = false, useOriginal: Boolean = false,