KT-1436 Allow break/continue in inlined lambdas

This commit is contained in:
Pavel Mikhailovskii
2022-08-11 00:12:43 +02:00
committed by teamcity
parent ba7df005a1
commit 8ba80b4b7b
52 changed files with 1851 additions and 88 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeature.BreakContinueInInlineLambdas
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitQualifiedAccessToUninitializedEnumEntry
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
@@ -926,9 +927,9 @@ class ControlFlowProcessor(
private fun jumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtLoopExpression): Boolean {
val bindingContext = trace.bindingContext
val labelExprEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpExpression)
val labelTargetEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpTarget)
val skipInlineFunctions = languageVersionSettings.supportsFeature(BreakContinueInInlineLambdas)
val labelExprEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpExpression, skipInlineFunctions)
val labelTargetEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpTarget, skipInlineFunctions)
return if (labelExprEnclosingFunc !== labelTargetEnclosingFunc) {
// Check to report only once
if (builder.getLoopExitPoint(jumpTarget) != null ||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtPsiUtil.deparenthesizeOnce
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.takeSnapshot
import org.jetbrains.kotlin.types.KotlinType
@@ -154,12 +156,49 @@ fun getEnclosingDescriptor(context: BindingContext, element: KtElement): Declara
}
}
fun getEnclosingFunctionDescriptor(context: BindingContext, element: KtElement): FunctionDescriptor? {
val functionOrClass = element.getParentOfTypeCodeFragmentAware(KtFunction::class.java, KtClassOrObject::class.java)
val descriptor = context.get(DECLARATION_TO_DESCRIPTOR, functionOrClass)
return if (functionOrClass is KtFunction) {
if (descriptor is FunctionDescriptor) descriptor else null
} else {
if (descriptor is ClassDescriptor) descriptor.unsubstitutedPrimaryConstructor else null
fun getEnclosingFunctionDescriptor(context: BindingContext, element: KtElement, skipInlineFunctionLiterals: Boolean): FunctionDescriptor? {
var current = element
while (true) {
val functionOrClass = current.getParentOfTypeCodeFragmentAware(KtFunction::class.java, KtClassOrObject::class.java)
val descriptor = context.get(DECLARATION_TO_DESCRIPTOR, functionOrClass)
if (functionOrClass is KtFunction) {
if (descriptor is FunctionDescriptor) {
if (skipInlineFunctionLiterals && isInlineableFunctionLiteral(
((functionOrClass as? KtFunctionLiteral)?.parent as? KtExpression) ?: functionOrClass,
context
)) {
current = functionOrClass
} else {
return descriptor
}
} else {
return null
}
} else {
return if (descriptor is ClassDescriptor) descriptor.unsubstitutedPrimaryConstructor else null
}
}
}
fun isInlineableFunctionLiteral(expression: KtExpression, context: BindingContext): Boolean {
if (expression !is KtLambdaExpression && !(expression is KtNamedFunction && expression.name == null)) {
return false
}
var wrapper: PsiElement = expression
while (deparenthesizeOnce(wrapper.parent as? KtExpression) == wrapper) {
wrapper = wrapper.parent
}
val argument = (wrapper.parent as? KtValueArgument) ?: return false
val call = (((argument.parent as? KtValueArgumentList) ?: argument).parent as? KtCallExpression) ?: return false
val resolvedCall = call.getResolvedCall(context) ?: return false
val descriptor = (resolvedCall.resultingDescriptor as? FunctionDescriptor) ?: return false
if (descriptor.isInline) {
val parameter = resolvedCall.valueArguments.entries.find { (_, valueArgument) ->
valueArgument.arguments.any { it.asElement() == argument }
}?.key ?: return false
return !parameter.isNoinline
}
return false
}