FIR: Make 'suspend fun', where 'suspend' is not builtin error in 1.8

#KT-49264
This commit is contained in:
Ilmir Usmanov
2022-03-30 00:37:37 +02:00
committed by teamcity
parent f75a8109d2
commit f473998e8b
9 changed files with 73 additions and 40 deletions
@@ -3872,6 +3872,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN.errorFactory) { firDiagnostic ->
ModifierFormForNonBuiltInSuspendFunErrorImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN.warningFactory) { firDiagnostic ->
ModifierFormForNonBuiltInSuspendFunWarningImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.RETURN_FOR_BUILT_IN_SUSPEND) { firDiagnostic ->
ReturnForBuiltInSuspendImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -2699,6 +2699,14 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ModifierFormForNonBuiltInSuspend::class
}
abstract class ModifierFormForNonBuiltInSuspendFunError : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ModifierFormForNonBuiltInSuspendFunError::class
}
abstract class ModifierFormForNonBuiltInSuspendFunWarning : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ModifierFormForNonBuiltInSuspendFunWarning::class
}
abstract class ReturnForBuiltInSuspend : KtFirDiagnostic<KtReturnExpression>() {
override val diagnosticClass get() = ReturnForBuiltInSuspend::class
}
@@ -3256,6 +3256,16 @@ internal class ModifierFormForNonBuiltInSuspendImpl(
override val token: ValidityToken,
) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspend(), KtAbstractFirDiagnostic<PsiElement>
internal class ModifierFormForNonBuiltInSuspendFunErrorImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspendFunError(), KtAbstractFirDiagnostic<PsiElement>
internal class ModifierFormForNonBuiltInSuspendFunWarningImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspendFunWarning(), KtAbstractFirDiagnostic<PsiElement>
internal class ReturnForBuiltInSuspendImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: ValidityToken,
@@ -1399,6 +1399,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
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)
val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN by deprecationError<PsiElement>(
LanguageFeature.ModifierNonBuiltinSuspendFunError, PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED
)
val RETURN_FOR_BUILT_IN_SUSPEND by error<KtReturnExpression>()
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeature.ForbidExposingTypesInPrimaryConstructorProperties
import org.jetbrains.kotlin.config.LanguageFeature.ForbidUsingExtensionPropertyTypeParameterInDelegate
import org.jetbrains.kotlin.config.LanguageFeature.ModifierNonBuiltinSuspendFunError
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitAssigningSingleElementsToVarargsInNamedForm
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitConfusingSyntaxInWhenBranches
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitCyclesInAnnotations
@@ -726,6 +727,7 @@ object FirErrors {
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)
val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN by deprecationError0<PsiElement>(ModifierNonBuiltinSuspendFunError, SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val RETURN_FOR_BUILT_IN_SUSPEND by error0<KtReturnExpression>()
// label
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
@@ -30,8 +31,12 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
@@ -81,38 +86,50 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
if (symbol.callableId == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_CALLABLE_ID) {
if (reference.name != BUILTIN_SUSPEND_NAME ||
expression.explicitReceiver != null ||
!expression.hasFormOfSuspendModifierForLambda()
expression.formOfSuspendModifierForLambdaOrFun() == null
) {
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)
} else if (reference.name == BUILTIN_SUSPEND_NAME) {
when (expression.formOfSuspendModifierForLambdaOrFun()) {
SuspendCallArgumentKind.FUN -> {
reporter.reportOn(expression.source, FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN, context)
}
SuspendCallArgumentKind.LAMBDA -> {
reporter.reportOn(expression.source, FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND, context)
}
null -> {
// Nothing to do
}
}
}
}
private fun FirQualifiedAccessExpression.hasFormOfSuspendModifierForLambda(): Boolean {
if (this !is FirFunctionCall) return false
private fun FirQualifiedAccessExpression.formOfSuspendModifierForLambdaOrFun(): SuspendCallArgumentKind? {
if (this !is FirFunctionCall) return null
val reference = this.calleeReference
if (reference is FirResolvedCallableReference) return false
if (typeArguments.any { it.source != null }) return false
if (reference is FirResolvedCallableReference) return null
if (typeArguments.any { it.source != null }) return null
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
return SuspendCallArgumentKind.LAMBDA
}
}
if (origin == FirFunctionCallOrigin.Infix) {
val lastArgument = arguments.lastOrNull()
if (lastArgument is FirAnonymousFunctionExpression && source?.getChild(KtNodeTypes.PARENTHESIZED, depth = 1) == null) {
return true
return if (lastArgument.source?.lighterASTNode?.tokenType == KtStubElementTypes.FUNCTION) {
SuspendCallArgumentKind.FUN
} else {
SuspendCallArgumentKind.LAMBDA
}
}
}
return false
return null
}
private fun findEnclosingSuspendFunction(context: CheckerContext): FirFunction? {
@@ -228,3 +245,7 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
)
}
}
private enum class SuspendCallArgumentKind {
FUN, LAMBDA
}
@@ -296,6 +296,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_STDLIB_CL
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_ARGUMENTS_APPLICABLE_FOR_CONTEXT_RECEIVER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN
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
@@ -1987,6 +1988,10 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
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() { ... }''"
)
map.put(
MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN,
"Calls having a form of ''suspend fun'' are deprecated because ''suspend'' in the context will have a meaning of a modifier. Surround the argument of the call with parens: ''suspend(fun() { ... })''"
)
map.put(RETURN_FOR_BUILT_IN_SUSPEND, "Using implicit label for this lambda is prohibited")
// Label
@@ -1,29 +0,0 @@
// SKIP_TXT
infix fun Int.suspend(c: () -> Unit) { c() }
fun bar() {
1 <!MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND!>suspend<!> fun() {
println()
}
1 <!SYNTAX!>@Ann suspend fun()<!> {
println()
}
1 <!MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND!>suspend<!> @Ann fun() {
println()
}
}
@Target(AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
annotation class Ann
fun main(suspend: WLambdaInvoke) {
1 <!MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND!>suspend<!> fun() {}
}
class WLambdaInvoke {
operator fun Int.invoke(l: () -> Unit) {}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// SKIP_TXT
infix fun Int.suspend(c: () -> Unit) { c() }