From f473998e8bf1d29d57f726ff0751c00ee2b9e9be Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Wed, 30 Mar 2022 00:37:37 +0200 Subject: [PATCH] FIR: Make 'suspend fun', where 'suspend' is not builtin error in 1.8 #KT-49264 --- .../diagnostics/KtFirDataClassConverters.kt | 12 ++++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 8 ++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 10 +++++ .../diagnostics/FirDiagnosticsList.kt | 3 ++ .../fir/analysis/diagnostics/FirErrors.kt | 2 + .../expression/FirSuspendCallChecker.kt | 43 ++++++++++++++----- .../diagnostics/FirErrorsDefaultMessages.kt | 5 +++ .../nonModifierFormForBuiltInWithFun.fir.kt | 29 ------------- .../nonModifierFormForBuiltInWithFun.kt | 1 + 9 files changed, 73 insertions(+), 40 deletions(-) delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 3b7922cb438..641918ecbd5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -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, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 0a11cbcda52..601d908ab64 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2699,6 +2699,14 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ModifierFormForNonBuiltInSuspend::class } + abstract class ModifierFormForNonBuiltInSuspendFunError : KtFirDiagnostic() { + override val diagnosticClass get() = ModifierFormForNonBuiltInSuspendFunError::class + } + + abstract class ModifierFormForNonBuiltInSuspendFunWarning : KtFirDiagnostic() { + override val diagnosticClass get() = ModifierFormForNonBuiltInSuspendFunWarning::class + } + abstract class ReturnForBuiltInSuspend : KtFirDiagnostic() { override val diagnosticClass get() = ReturnForBuiltInSuspend::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 70a024791db..b2c6902dee6 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3256,6 +3256,16 @@ internal class ModifierFormForNonBuiltInSuspendImpl( override val token: ValidityToken, ) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspend(), KtAbstractFirDiagnostic +internal class ModifierFormForNonBuiltInSuspendFunErrorImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspendFunError(), KtAbstractFirDiagnostic + +internal class ModifierFormForNonBuiltInSuspendFunWarningImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.ModifierFormForNonBuiltInSuspendFunWarning(), KtAbstractFirDiagnostic + internal class ReturnForBuiltInSuspendImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: ValidityToken, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 7a8a475b60f..dff2ad03099 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1399,6 +1399,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) val NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) + val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN by deprecationError( + LanguageFeature.ModifierNonBuiltinSuspendFunError, PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED + ) val RETURN_FOR_BUILT_IN_SUSPEND by error() } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 4f6744b54bb..121a714a0e0 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -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(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND_FUN by deprecationError0(ModifierNonBuiltinSuspendFunError, SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val RETURN_FOR_BUILT_IN_SUSPEND by error0() // label diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt index ac8609a129c..a4bf9ee31b7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt @@ -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 +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index c1cd47bcdbd..c18d5dd264d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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 diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.fir.kt deleted file mode 100644 index bdb68ef8e77..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.fir.kt +++ /dev/null @@ -1,29 +0,0 @@ -// SKIP_TXT - -infix fun Int.suspend(c: () -> Unit) { c() } - -fun bar() { - 1 suspend fun() { - println() - } - - 1 @Ann suspend fun() { - println() - } - - 1 suspend @Ann fun() { - println() - } -} - -@Target(AnnotationTarget.EXPRESSION) -@Retention(AnnotationRetention.SOURCE) -annotation class Ann - -fun main(suspend: WLambdaInvoke) { - 1 suspend fun() {} -} - -class WLambdaInvoke { - operator fun Int.invoke(l: () -> Unit) {} -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.kt index 97a3c9a5b29..7aa35dbe217 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/nonModifierFormForBuiltInWithFun.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT infix fun Int.suspend(c: () -> Unit) { c() }