From 786684a1e7e9aebd761ef235ed24bf1f9809f2b8 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Sun, 12 May 2024 17:31:39 +0200 Subject: [PATCH] [kotlin] k2 extract function: rely on compiler checks to detect suspend problems - otherwise we would need to check enclosing suspend functions manually ^KTIJ-29873 fixed GitOrigin-RevId: 177244574eea1ac93381da4debab6b368b9eb300 --- .../introduce/ExtractionTestGenerated.java | 5 +++ .../suspendCallInsideSuspendContinuation.kt | 13 +++++++ ...pendCallInsideSuspendContinuation.kt.after | 17 ++++++++++ .../ExtractionDataAnalyzer.kt | 34 ++++++++++++------- .../K2IntroduceFunctionTestGenerated.java | 5 +++ 5 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt.after diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 608ded63f7c3..2430e087ec8e 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java @@ -1148,6 +1148,11 @@ public abstract class ExtractionTestGenerated extends AbstractExtractionTest { runTest("testData/refactoring/extractFunction/basic/suspendCall.kt"); } + @TestMetadata("suspendCallInsideSuspendContinuation.kt") + public void testSuspendCallInsideSuspendContinuation() throws Exception { + runTest("testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt"); + } + @TestMetadata("suspendCallWithExtractedParameter.kt") public void testSuspendCallWithExtractedParameter() throws Exception { runTest("testData/refactoring/extractFunction/basic/suspendCallWithExtractedParameter.kt"); diff --git a/plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt b/plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt new file mode 100644 index 000000000000..94d7eac146e6 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt @@ -0,0 +1,13 @@ +// PARAM_TYPES: D +// PARAM_DESCRIPTOR: value-parameter d: D defined in test1 +fun m(block: suspend () -> Unit) {} + +class D { + suspend fun await() {} +} + +fun test1(d: D) { + m { + d.await() + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt.after b/plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt.after new file mode 100644 index 000000000000..df858ce85c13 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt.after @@ -0,0 +1,17 @@ +// PARAM_TYPES: D +// PARAM_DESCRIPTOR: value-parameter d: D defined in test1 +fun m(block: suspend () -> Unit) {} + +class D { + suspend fun await() {} +} + +fun test1(d: D) { + __dummyTestFun__(d) +} + +private fun __dummyTestFun__(d: D) { + m { + d.await() + } +} diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/ExtractionDataAnalyzer.kt b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/ExtractionDataAnalyzer.kt index c81775255bf9..15b6def10787 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/ExtractionDataAnalyzer.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/ExtractionDataAnalyzer.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.analysis.api.components.KtDataFlowExitPointSnapshot import org.jetbrains.kotlin.analysis.api.components.KtDiagnosticCheckerFilter import org.jetbrains.kotlin.analysis.api.fir.diagnostics.KtFirDiagnostic import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol -import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtAnnotatedSymbol import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType import org.jetbrains.kotlin.analysis.api.types.KtType @@ -56,6 +55,7 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtBreakExpression +import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtContinueExpression import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtElement @@ -224,26 +224,34 @@ internal class ExtractionDataAnalyzer(private val extractionData: ExtractionData replacementMap = replacementMap, controlFlow = flow, returnType = returnType, - modifiers = if (hasSuspendReference(extractionData)) listOf(KtTokens.SUSPEND_KEYWORD) else emptyList(), + modifiers = emptyList(), optInMarkers = experimentalMarkers.optInMarkers, annotationClassIds = experimentalMarkers.propagatingMarkerClassIds ) + val config = ExtractionGeneratorConfiguration( + descriptor, + ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false) + ) + + val generatedDeclaration = Generator.generateDeclaration(config, null).declaration + val illegalSuspendInside = analyzeCopy(generatedDeclaration, DanglingFileResolutionMode.PREFER_SELF) { + generatedDeclaration.descendantsOfType() + .flatMap { + it.getDiagnostics(KtDiagnosticCheckerFilter.ONLY_COMMON_CHECKERS) + .map { it.diagnosticClass } + } + .any { it == KtFirDiagnostic.IllegalSuspendFunctionCall::class || it == KtFirDiagnostic.IllegalSuspendPropertyAccess::class } + } + if (illegalSuspendInside) { + descriptor = descriptor.copy(modifiers = listOf(KtTokens.SUSPEND_KEYWORD)) + } + + for (analyser in ExtractFunctionDescriptorModifier.EP_NAME.extensionList) { descriptor = analyser.modifyDescriptor(descriptor) } return descriptor } - - private fun hasSuspendReference(extractionData: ExtractionData): Boolean { - return extractionData.expressions - .flatMap { it.descendantsOfType() } - .any { nameExpression -> - analyze(nameExpression) { - val symbol = nameExpression.mainReference.resolveToSymbol() - symbol is KtFunctionSymbol && symbol.isSuspend - } - } - } } private data class ExperimentalMarkers( diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.tests.k2/test/org/jetbrains/kotlin/idea/k2/refactoring/introduce/K2IntroduceFunctionTestGenerated.java b/plugins/kotlin/refactorings/kotlin.refactorings.tests.k2/test/org/jetbrains/kotlin/idea/k2/refactoring/introduce/K2IntroduceFunctionTestGenerated.java index dc9c48586777..4a70bac4b4a8 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.tests.k2/test/org/jetbrains/kotlin/idea/k2/refactoring/introduce/K2IntroduceFunctionTestGenerated.java +++ b/plugins/kotlin/refactorings/kotlin.refactorings.tests.k2/test/org/jetbrains/kotlin/idea/k2/refactoring/introduce/K2IntroduceFunctionTestGenerated.java @@ -306,6 +306,11 @@ public abstract class K2IntroduceFunctionTestGenerated extends AbstractK2Introdu runTest("../../idea/tests/testData/refactoring/extractFunction/basic/suspendCall.kt"); } + @TestMetadata("suspendCallInsideSuspendContinuation.kt") + public void testSuspendCallInsideSuspendContinuation() throws Exception { + runTest("../../idea/tests/testData/refactoring/extractFunction/basic/suspendCallInsideSuspendContinuation.kt"); + } + @TestMetadata("suspendCallWithExtractedParameter.kt") public void testSuspendCallWithExtractedParameter() throws Exception { runTest("../../idea/tests/testData/refactoring/extractFunction/basic/suspendCallWithExtractedParameter.kt");