FIR: introduce RETURN_FOR_BUILT_IN_SUSPEND diagnostic

This commit is contained in:
Mikhail Glukhikh
2021-07-27 19:30:46 +03:00
committed by teamcityserver
parent 229dfd3f5f
commit 807f031dcc
11 changed files with 65 additions and 8 deletions
@@ -1194,6 +1194,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL 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 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 by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
val RETURN_FOR_BUILT_IN_SUSPEND by error<KtReturnExpression>()
} }
} }
@@ -614,5 +614,6 @@ object FirErrors {
val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL 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 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 by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val RETURN_FOR_BUILT_IN_SUSPEND by error0<KtReturnExpression>()
} }
@@ -6,14 +6,18 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
object FirReturnAllowedChecker : FirReturnExpressionChecker() { object FirReturnAllowedChecker : FirReturnExpressionChecker() {
override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -26,6 +30,25 @@ object FirReturnAllowedChecker : FirReturnExpressionChecker() {
reporter.reportOn(source, FirErrors.RETURN_NOT_ALLOWED, context) reporter.reportOn(source, FirErrors.RETURN_NOT_ALLOWED, context)
} }
if (targetSymbol is FirAnonymousFunctionSymbol) {
val label = targetSymbol.label
if (label?.source?.kind !is FirRealSourceElementKind) {
val functionCall = context.qualifiedAccessOrAnnotationCalls.asReversed().find {
it is FirFunctionCall &&
((it.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol)?.callableId ==
FirSuspendCallChecker.KOTLIN_SUSPEND_BUILT_IN_FUNCTION_CALLABLE_ID
}
if (functionCall is FirFunctionCall &&
functionCall.arguments.any {
it is FirLambdaArgumentExpression &&
(it.expression as? FirAnonymousFunctionExpression)?.anonymousFunction?.symbol == targetSymbol
}
) {
reporter.reportOn(source, FirErrors.RETURN_FOR_BUILT_IN_SUSPEND, context)
}
}
}
val containingDeclaration = context.containingDeclarations.last() val containingDeclaration = context.containingDeclarations.last()
if (containingDeclaration is FirFunction && containingDeclaration.body is FirSingleExpressionBlock) { if (containingDeclaration is FirFunction && containingDeclaration.body is FirSingleExpressionBlock) {
reporter.reportOn(source, FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY, context) reporter.reportOn(source, FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY, context)
@@ -29,8 +29,10 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeParameterType import org.jetbrains.kotlin.fir.types.ConeTypeParameterType
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
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_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_2_30_FQ_NAME
import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_3_FQ_NAME import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_3_FQ_NAME
@@ -47,6 +49,8 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
private val BUILTIN_SUSPEND_NAME = KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME.shortName() private val BUILTIN_SUSPEND_NAME = KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME.shortName()
internal val KOTLIN_SUSPEND_BUILT_IN_FUNCTION_CALLABLE_ID = CallableId(StandardClassIds.BASE_KOTLIN_PACKAGE, BUILTIN_SUSPEND_NAME)
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
@@ -89,7 +93,7 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter reporter: DiagnosticReporter
) { ) {
if (symbol.callableId.asSingleFqName() == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME) { if (symbol.callableId == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_CALLABLE_ID) {
if (reference.name != BUILTIN_SUSPEND_NAME || if (reference.name != BUILTIN_SUSPEND_NAME ||
expression.explicitReceiver != null || expression.explicitReceiver != null ||
!expression.hasFormOfSuspendModifierForLambda() !expression.hasFormOfSuspendModifierForLambda()
@@ -339,6 +339,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESOLUTION_TO_CLASSIFIER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESOLUTION_TO_CLASSIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESULT_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESULT_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_FOR_BUILT_IN_SUSPEND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH
@@ -1507,6 +1508,7 @@ class FirDefaultErrorMessages {
MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND, 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() { ... }''" "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(RETURN_FOR_BUILT_IN_SUSPEND, "Using implicit label for this lambda is prohibited")
// Extended checkers group // Extended checkers group
map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier") map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier")
@@ -143,7 +143,10 @@ class ExpressionsConverter(
symbol = FirAnonymousFunctionSymbol() symbol = FirAnonymousFunctionSymbol()
isLambda = true isLambda = true
label = context.firLabels.pop() ?: context.calleeNamesForLambda.lastOrNull()?.let { label = context.firLabels.pop() ?: context.calleeNamesForLambda.lastOrNull()?.let {
buildLabel { name = it.asString() } buildLabel {
source = expressionSource.fakeElement(FirFakeSourceElementKind.GeneratedLambdaLabel)
name = it.asString()
}
} }
target = FirFunctionTarget(labelName = label?.name, isLambda = true) target = FirFunctionTarget(labelName = label?.name, isLambda = true)
context.firFunctionTargets += target context.firFunctionTargets += target
@@ -332,7 +335,10 @@ class ExpressionsConverter(
var firExpression: FirElement? = null var firExpression: FirElement? = null
labeledExpression.forEachChildren { labeledExpression.forEachChildren {
when (it.tokenType) { when (it.tokenType) {
LABEL_QUALIFIER -> context.firLabels += buildLabel { name = it.toString().replace("@", "") } LABEL_QUALIFIER -> context.firLabels += buildLabel {
source = labeledExpression.toFirLightSourceElement(tree)
name = it.toString().replace("@", "")
}
BLOCK -> firExpression = declarationsConverter.convertBlock(it) BLOCK -> firExpression = declarationsConverter.convertBlock(it)
PROPERTY -> firExpression = declarationsConverter.convertPropertyDeclaration(it) PROPERTY -> firExpression = declarationsConverter.convertPropertyDeclaration(it)
else -> if (it.isExpression()) firExpression = getAsFirExpression(it) else -> if (it.isExpression()) firExpression = getAsFirExpression(it)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.symbols.impl package org.jetbrains.kotlin.fir.symbols.impl
import org.jetbrains.kotlin.fir.FirLabel
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.symbols.AccessorSymbol import org.jetbrains.kotlin.fir.symbols.AccessorSymbol
@@ -63,7 +64,9 @@ sealed class FirFunctionWithoutNameSymbol<F : FirFunction>(
stubName: Name stubName: Name
) : FirFunctionSymbol<F>(CallableId(FqName("special"), stubName)) ) : FirFunctionSymbol<F>(CallableId(FqName("special"), stubName))
class FirAnonymousFunctionSymbol : FirFunctionWithoutNameSymbol<FirAnonymousFunction>(Name.identifier("anonymous")) class FirAnonymousFunctionSymbol : FirFunctionWithoutNameSymbol<FirAnonymousFunction>(Name.identifier("anonymous")) {
val label: FirLabel? get() = fir.label
}
class FirPropertyAccessorSymbol : FirFunctionWithoutNameSymbol<FirPropertyAccessor>(Name.identifier("accessor")) class FirPropertyAccessorSymbol : FirFunctionWithoutNameSymbol<FirPropertyAccessor>(Name.identifier("accessor"))
@@ -1,12 +1,12 @@
// SKIP_TXT // SKIP_TXT
fun bar() { fun bar() {
suspend { suspend {
return@suspend <!RETURN_FOR_BUILT_IN_SUSPEND!>return@suspend<!>
} }
suspend { suspend {
run { run {
return@suspend <!RETURN_FOR_BUILT_IN_SUSPEND!>return@suspend<!>
} }
} }
@@ -21,7 +21,7 @@ fun bar() {
val x = suspend@{ val x = suspend@{
suspend { suspend {
// Might be resolved to outer lambda, but doesn't make sense because suspend-lambdas here is noinline // Might be resolved to outer lambda, but doesn't make sense because suspend-lambdas here is noinline
return@suspend <!RETURN_FOR_BUILT_IN_SUSPEND!>return@suspend<!>
} }
} }
} }
@@ -3180,6 +3180,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.RETURN_FOR_BUILT_IN_SUSPEND) { firDiagnostic ->
ReturnForBuiltInSuspendImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic -> add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic ->
ConflictingJvmDeclarationsImpl( ConflictingJvmDeclarationsImpl(
firDiagnostic as FirPsiDiagnostic, firDiagnostic as FirPsiDiagnostic,
@@ -2223,6 +2223,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ModifierFormForNonBuiltInSuspend::class override val diagnosticClass get() = ModifierFormForNonBuiltInSuspend::class
} }
abstract class ReturnForBuiltInSuspend : KtFirDiagnostic<KtReturnExpression>() {
override val diagnosticClass get() = ReturnForBuiltInSuspend::class
}
abstract class ConflictingJvmDeclarations : KtFirDiagnostic<PsiElement>() { abstract class ConflictingJvmDeclarations : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ConflictingJvmDeclarations::class override val diagnosticClass get() = ConflictingJvmDeclarations::class
} }
@@ -3589,6 +3589,13 @@ internal class ModifierFormForNonBuiltInSuspendImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
} }
internal class ReturnForBuiltInSuspendImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ReturnForBuiltInSuspend(), KtAbstractFirDiagnostic<KtReturnExpression> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ConflictingJvmDeclarationsImpl( internal class ConflictingJvmDeclarationsImpl(
firDiagnostic: FirPsiDiagnostic, firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken, override val token: ValidityToken,