FIR: introduce builtin suspend related diagnostics
This commit is contained in:
committed by
teamcityserver
parent
391c4db87c
commit
229dfd3f5f
+2
@@ -1192,6 +1192,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
}
|
||||
val NON_LOCAL_SUSPENSION_POINT by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -612,5 +612,7 @@ object FirErrors {
|
||||
val ILLEGAL_SUSPEND_PROPERTY_ACCESS by error1<PsiElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val NON_LOCAL_SUSPENSION_POINT by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
|
||||
}
|
||||
|
||||
+56
-4
@@ -5,17 +5,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_2_20_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_2_30_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_3_FQ_NAME
|
||||
import org.jetbrains.kotlin.serialization.deserialization.KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||
|
||||
object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
|
||||
@@ -44,13 +45,20 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
|
||||
private val RESTRICTS_SUSPENSION_CLASS_ID =
|
||||
ClassId(StandardNames.COROUTINES_PACKAGE_FQ_NAME_RELEASE, Name.identifier("RestrictsSuspension"))
|
||||
|
||||
private val BUILTIN_SUSPEND_NAME = KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME.shortName()
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
|
||||
if (reference is FirResolvedCallableReference) return
|
||||
val symbol = reference.resolvedSymbol as? FirCallableSymbol ?: return
|
||||
symbol.ensureResolved(FirResolvePhase.STATUS)
|
||||
val fir = symbol.fir as? FirCallableDeclaration ?: return
|
||||
if (reference.name == BUILTIN_SUSPEND_NAME ||
|
||||
fir is FirSimpleFunction && fir.name == BUILTIN_SUSPEND_NAME
|
||||
) {
|
||||
checkSuspendModifierForm(expression, reference, symbol, context, reporter)
|
||||
}
|
||||
if (reference is FirResolvedCallableReference) return
|
||||
when (fir) {
|
||||
is FirSimpleFunction -> if (!fir.isSuspend) return
|
||||
is FirProperty -> if (symbol.callableId.asSingleFqName() !in SUSPEND_PROPERTIES_FQ_NAMES) return
|
||||
@@ -74,6 +82,50 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSuspendModifierForm(
|
||||
expression: FirQualifiedAccessExpression,
|
||||
reference: FirResolvedNamedReference,
|
||||
symbol: FirCallableSymbol<*>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (symbol.callableId.asSingleFqName() == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME) {
|
||||
if (reference.name != BUILTIN_SUSPEND_NAME ||
|
||||
expression.explicitReceiver != null ||
|
||||
!expression.hasFormOfSuspendModifierForLambda()
|
||||
) {
|
||||
reporter.reportOn(expression.source, FirErrors.NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND, context)
|
||||
}
|
||||
} else {
|
||||
if (reference.name == BUILTIN_SUSPEND_NAME && expression.hasFormOfSuspendModifierForLambda()) {
|
||||
reporter.reportOn(expression.source, FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccessExpression.hasFormOfSuspendModifierForLambda(): Boolean {
|
||||
if (this !is FirFunctionCall) return false
|
||||
val reference = this.calleeReference
|
||||
if (reference is FirResolvedCallableReference) return false
|
||||
if (typeArguments.any { it.source != null }) return false
|
||||
if (arguments.singleOrNull() is FirLambdaArgumentExpression) {
|
||||
// No brackets should be in a selector call
|
||||
val callExpressionSource =
|
||||
if (explicitReceiver == null) source
|
||||
else source?.getChild(KtNodeTypes.CALL_EXPRESSION, index = 1, depth = 1)
|
||||
if (callExpressionSource?.getChild(KtNodeTypes.VALUE_ARGUMENT_LIST, depth = 1) == null) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (source?.elementType == KtNodeTypes.BINARY_EXPRESSION) {
|
||||
val lastArgument = arguments.lastOrNull()
|
||||
if (lastArgument is FirAnonymousFunctionExpression && source?.getChild(KtNodeTypes.PARENTHESIZED, depth = 1) == null) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findEnclosingSuspendFunction(context: CheckerContext): FirFunction? {
|
||||
return context.containingDeclarations.lastOrNull {
|
||||
when (it) {
|
||||
|
||||
+10
@@ -247,6 +247,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_LAMBDA_EXPRE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MULTIPLE_VARARG_PARAMETERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT
|
||||
@@ -264,6 +265,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_FINAL_MEMBER_
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_LOCAL_RETURN_NOT_ALLOWED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_LOCAL_SUSPENSION_POINT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_MEMBER_FUNCTION_NO_BODY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE
|
||||
@@ -1497,6 +1499,14 @@ class FirDefaultErrorMessages {
|
||||
ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL,
|
||||
"Restricted suspending functions can only invoke member or extension suspending functions on their restricted coroutine scope"
|
||||
)
|
||||
map.put(
|
||||
NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND,
|
||||
"''suspend'' function can only be called in a form of modifier of a lambda: suspend { ... }"
|
||||
)
|
||||
map.put(
|
||||
MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND,
|
||||
"Calls having a form of ''suspend {}'' are deprecated because ''suspend'' in the context will have a meaning of a modifier. Add empty argument list to the call: ''suspend() { ... }''"
|
||||
)
|
||||
|
||||
// Extended checkers group
|
||||
map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier")
|
||||
|
||||
Vendored
-76
@@ -1,76 +0,0 @@
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
// SKIP_TXT
|
||||
|
||||
fun <R> suspend(block: suspend () -> R): suspend () -> R = block
|
||||
|
||||
class A {
|
||||
infix fun <R> suspend(block: suspend () -> R): suspend () -> R = block
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Ann
|
||||
|
||||
fun bar() {
|
||||
suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
@Ann suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend @Ann {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend({ println() })
|
||||
|
||||
suspend<Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
val w: (suspend () -> Int) -> Any? = ::suspend
|
||||
|
||||
A().suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
A().suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
A().suspend({ println() })
|
||||
|
||||
A().suspend<Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
with(A()) {
|
||||
suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend({ println() })
|
||||
|
||||
suspend<Unit> {
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
A() suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
A() suspend ({
|
||||
println()
|
||||
})
|
||||
}
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
// SKIP_TXT
|
||||
|
||||
fun <R> suspend(block: R) = block
|
||||
|
||||
class A {
|
||||
infix fun <R> suspend(block: R) = block
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Ann
|
||||
|
||||
fun bar() {
|
||||
suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
@Ann suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend @Ann {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend({ println() })
|
||||
|
||||
suspend<suspend () -> Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend<Nothing?>(null)
|
||||
|
||||
val w: (Any?) -> Any? = ::suspend
|
||||
|
||||
A().suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
A().suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
A().suspend({ println() })
|
||||
|
||||
A().suspend<suspend () -> Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
A().suspend<Nothing?>(null)
|
||||
|
||||
with(A()) {
|
||||
suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend({ println() })
|
||||
|
||||
suspend<suspend () -> Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend<Nothing?>(null)
|
||||
}
|
||||
|
||||
A() suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
A() suspend ({
|
||||
println()
|
||||
})
|
||||
|
||||
A() suspend ""
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
// SKIP_TXT
|
||||
|
||||
fun bar() {
|
||||
suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
@Ann suspend {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend @Ann {
|
||||
println()
|
||||
}
|
||||
|
||||
kotlin.suspend {
|
||||
|
||||
}
|
||||
|
||||
suspend() {
|
||||
println()
|
||||
}
|
||||
|
||||
suspend({ println() })
|
||||
|
||||
suspend<Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
val w: (suspend () -> Int) -> Any? = ::suspend
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Ann
|
||||
|
||||
fun main(suspend: WLambdaInvoke) {
|
||||
|
||||
suspend {}
|
||||
}
|
||||
|
||||
class WLambdaInvoke {
|
||||
operator fun invoke(l: () -> Unit) {}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
+6
-6
@@ -8,23 +8,23 @@ fun bar() {
|
||||
println()
|
||||
}
|
||||
|
||||
kotlin.suspend {
|
||||
kotlin.<!NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND!>suspend<!> {
|
||||
|
||||
}
|
||||
|
||||
suspendLambda {
|
||||
<!NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND!>suspendLambda<!> {
|
||||
println()
|
||||
}
|
||||
|
||||
suspendLambda() {
|
||||
<!NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND!>suspendLambda<!>() {
|
||||
println()
|
||||
}
|
||||
|
||||
suspendLambda({ println() })
|
||||
<!NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND!>suspendLambda<!>({ println() })
|
||||
|
||||
suspendLambda<Unit> {
|
||||
<!NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND!>suspendLambda<!><Unit> {
|
||||
println()
|
||||
}
|
||||
|
||||
val w: (suspend () -> Int) -> Any? = ::suspendLambda
|
||||
val w: (suspend () -> Int) -> Any? = ::<!NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND!>suspendLambda<!>
|
||||
}
|
||||
|
||||
+12
@@ -3168,6 +3168,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND) { firDiagnostic ->
|
||||
NonModifierFormForBuiltInSuspendImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND) { firDiagnostic ->
|
||||
ModifierFormForNonBuiltInSuspendImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic ->
|
||||
ConflictingJvmDeclarationsImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
|
||||
+8
@@ -2215,6 +2215,14 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = IllegalRestrictedSuspendingFunctionCall::class
|
||||
}
|
||||
|
||||
abstract class NonModifierFormForBuiltInSuspend : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = NonModifierFormForBuiltInSuspend::class
|
||||
}
|
||||
|
||||
abstract class ModifierFormForNonBuiltInSuspend : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ModifierFormForNonBuiltInSuspend::class
|
||||
}
|
||||
|
||||
abstract class ConflictingJvmDeclarations : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ConflictingJvmDeclarations::class
|
||||
}
|
||||
|
||||
+14
@@ -3575,6 +3575,20 @@ internal class IllegalRestrictedSuspendingFunctionCallImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class NonModifierFormForBuiltInSuspendImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.NonModifierFormForBuiltInSuspend(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ModifierFormForNonBuiltInSuspendImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspend(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ConflictingJvmDeclarationsImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
|
||||
Reference in New Issue
Block a user