From 55c932f04f476a7fe050190ff485a4515e63f41d Mon Sep 17 00:00:00 2001 From: Maksim Zuev Date: Thu, 10 Oct 2024 00:25:05 +0200 Subject: [PATCH] [debugger, kotlin] IDEA-346083 Fix smart-step-into methods with context receivers GitOrigin-RevId: c7c1ee9ad93b21be2508669aeb4ca33791d40b44 --- .../idea/codeinsight/utils/InlineUtils.kt | 14 ++++++- .../smartStepInto/CallableMemberInfo.kt | 5 ++- .../smartStepInto/KotlinLambdaInfo.kt | 3 ++ .../KotlinSmartStepTargetFilterer.kt | 4 +- .../smartStepInto/SmartStepTargetVisitor.kt | 3 +- ...2IdeK1CodeKotlinSteppingTestGenerated.java | 5 +++ ...2IdeK2CodeKotlinSteppingTestGenerated.java | 5 +++ ...IndyLambdaKotlinSteppingTestGenerated.java | 5 +++ .../test/DebuggerTestCompilerFacility.kt | 1 + ...dyLambdaIrKotlinSteppingTestGenerated.java | 5 +++ .../test/IrKotlinSteppingTestGenerated.java | 5 +++ ...1IdeK2CodeKotlinSteppingTestGenerated.java | 5 +++ .../custom/smartStepIntoFunWithContext.kt | 39 +++++++++++++++++++ .../custom/smartStepIntoFunWithContext.out | 14 +++++++ 14 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.kt create mode 100644 plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.out diff --git a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/InlineUtils.kt b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/InlineUtils.kt index ee49c6064243..e15ec82c2acc 100644 --- a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/InlineUtils.kt +++ b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/InlineUtils.kt @@ -1,9 +1,11 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.codeinsight.utils +import com.intellij.openapi.application.ApplicationManager import com.intellij.psi.util.parentOfType import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.resolution.KaFunctionCall import org.jetbrains.kotlin.analysis.api.resolution.successfulFunctionCallOrNull import org.jetbrains.kotlin.analysis.api.resolution.symbol import org.jetbrains.kotlin.analysis.api.symbols.* @@ -61,13 +63,23 @@ context(KaSession) fun getCallExpressionSymbol(argument: KtExpression): Pair? { if (argument !is KtFunction && argument !is KtCallableReferenceExpression) return null val parentCallExpression = KtPsiUtil.getParentCallIfPresent(argument) as? KtCallExpression ?: return null - val parentCall = parentCallExpression.resolveToCall()?.successfulFunctionCallOrNull() ?: return null + val parentCall = resolveFunctionCall(parentCallExpression) ?: return null val symbol = parentCall.partiallyAppliedSymbol.symbol val valueArgument = parentCallExpression.getContainingValueArgument(argument) ?: return null val argumentSymbol = parentCall.argumentMapping[valueArgument.getArgumentExpression()]?.symbol ?: return null return symbol to argumentSymbol } +context(KaSession) +@ApiStatus.Internal +fun resolveFunctionCall(expression: KtExpression): KaFunctionCall<*>? { + val successfulCall = expression.resolveToCall()?.successfulFunctionCallOrNull() + if (successfulCall != null) return successfulCall + if (!ApplicationManager.getApplication().isUnitTestMode) return null + // Functions with context receivers are not resolved in K2 tests for some reason + return expression.resolveToCallCandidates().firstOrNull()?.candidate as? KaFunctionCall<*> +} + context(KaSession) private fun isArrayGeneratorConstructorCall(symbol: KaFunctionSymbol): Boolean { fun checkParameters(symbol: KaFunctionSymbol): Boolean { diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/CallableMemberInfo.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/CallableMemberInfo.kt index 043aba99fe74..92eba5275703 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/CallableMemberInfo.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/CallableMemberInfo.kt @@ -1,6 +1,7 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol @@ -53,9 +54,11 @@ internal fun CallableMemberInfo( internal fun KaFunctionSymbol.isSuspend(): Boolean = this is KaNamedFunctionSymbol && this.isSuspend context(KaSession) +@OptIn(KaExperimentalApi::class) internal fun KaFunctionSymbol.containsInlineClassInParameters(): Boolean = valueParameters.any { it.returnType.expandedSymbol?.isInlineClass() == true } - || receiverParameter?.type?.expandedSymbol?.isInlineClass() == true + || receiverParameter?.returnType?.expandedSymbol?.isInlineClass() == true + || contextReceivers.any { it.type.expandedSymbol?.isInlineClass() == true } context(KaSession) private fun KaFunctionSymbol.methodName() = when (this) { diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinLambdaInfo.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinLambdaInfo.kt index e88b2f91c3c3..b8dc5674c547 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinLambdaInfo.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinLambdaInfo.kt @@ -1,6 +1,7 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol @@ -45,6 +46,7 @@ internal fun KotlinLambdaInfo( ) context(KaSession) +@OptIn(KaExperimentalApi::class) private fun countParameterIndex(methodSymbol: KaFunctionSymbol, argumentSymbol: KaValueParameterSymbol): Int { var resultIndex = methodSymbol.valueParameters.indexOf(argumentSymbol) @@ -52,6 +54,7 @@ private fun countParameterIndex(methodSymbol: KaFunctionSymbol, argumentSymbol: resultIndex++ if (methodSymbol.isInsideInlineClass()) resultIndex++ + resultIndex += methodSymbol.contextReceivers.size return resultIndex } diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinSmartStepTargetFilterer.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinSmartStepTargetFilterer.kt index 243c4523b7db..4fb38b025a1a 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinSmartStepTargetFilterer.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/KotlinSmartStepTargetFilterer.kt @@ -238,8 +238,10 @@ private fun String.isSubClassOf(baseInternalName: String?): Boolean { } context(KaSession) +@OptIn(KaExperimentalApi::class) private fun KaCallableSymbol.getJvmSignature(): String? { val element = psi ?: return null + val contextReceivers = contextReceivers.mapNotNull { it.type.jvmName(element) }.joinToString("") val receiver = receiverType?.jvmName(element) ?: "" val isSuspend = this is KaFunctionSymbol && isSuspend() val parameterTypes = if (this is KaFunctionSymbol) { @@ -253,7 +255,7 @@ private fun KaCallableSymbol.getJvmSignature(): String? { else -> returnType.jvmName(element) ?: return null } val continuationParameter = if (isSuspend) "Lkotlin/coroutines/Continuation;" else "" - return "($receiver$parameterTypes$continuationParameter)$returnType" + return "($contextReceivers$receiver$parameterTypes$continuationParameter)$returnType" } context(KaSession) diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/SmartStepTargetVisitor.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/SmartStepTargetVisitor.kt index f0dec4403fcb..9d060f6925e1 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/SmartStepTargetVisitor.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/stepping/smartStepInto/SmartStepTargetVisitor.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.analysis.api.resolution.symbol import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.types.KaFunctionType import org.jetbrains.kotlin.idea.codeinsight.utils.getCallExpressionSymbol +import org.jetbrains.kotlin.idea.codeinsight.utils.resolveFunctionCall import org.jetbrains.kotlin.idea.debugger.core.breakpoints.isInlineOnly import org.jetbrains.kotlin.idea.debugger.core.isInlineClass import org.jetbrains.kotlin.idea.debugger.core.stepping.getLineRange @@ -330,7 +331,7 @@ class SmartStepTargetVisitor( private fun recordFunctionCall(expression: KtExpression, highlightExpression: KtExpression) { analyze(expression) { - val resolvedCall = expression.resolveToCall()?.successfulFunctionCallOrNull() ?: return + val resolvedCall = resolveFunctionCall(expression) ?: return val symbol = resolvedCall.partiallyAppliedSymbol.symbol if (symbol.annotations.any { it.classId?.internalName == "kotlin/internal/IntrinsicConstEvaluation" }) { return diff --git a/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK1CodeKotlinSteppingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK1CodeKotlinSteppingTestGenerated.java index 45d9ff5ab561..1c4bb82f8333 100644 --- a/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK1CodeKotlinSteppingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK1CodeKotlinSteppingTestGenerated.java @@ -1698,6 +1698,11 @@ public abstract class K2IdeK1CodeKotlinSteppingTestGenerated extends AbstractK2I runTest("../testData/stepping/custom/smartStepIntoDeferredSamLambdas.kt"); } + @TestMetadata("smartStepIntoFunWithContext.kt") + public void testSmartStepIntoFunWithContext() throws Exception { + runTest("../testData/stepping/custom/smartStepIntoFunWithContext.kt"); + } + @TestMetadata("smartStepIntoFunWithDefaultArgs.kt") public void testSmartStepIntoFunWithDefaultArgs() throws Exception { runTest("../testData/stepping/custom/smartStepIntoFunWithDefaultArgs.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK2CodeKotlinSteppingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK2CodeKotlinSteppingTestGenerated.java index 8df07472a7f9..161b1173b4fa 100644 --- a/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK2CodeKotlinSteppingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IdeK2CodeKotlinSteppingTestGenerated.java @@ -1698,6 +1698,11 @@ public abstract class K2IdeK2CodeKotlinSteppingTestGenerated extends AbstractK2I runTest("../testData/stepping/custom/smartStepIntoDeferredSamLambdas.kt"); } + @TestMetadata("smartStepIntoFunWithContext.kt") + public void testSmartStepIntoFunWithContext() throws Exception { + runTest("../testData/stepping/custom/smartStepIntoFunWithContext.kt"); + } + @TestMetadata("smartStepIntoFunWithDefaultArgs.kt") public void testSmartStepIntoFunWithDefaultArgs() throws Exception { runTest("../testData/stepping/custom/smartStepIntoFunWithDefaultArgs.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IndyLambdaKotlinSteppingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IndyLambdaKotlinSteppingTestGenerated.java index 3c4930cd1e34..0ff4e941d89f 100644 --- a/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IndyLambdaKotlinSteppingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/k2/test/org/jetbrains/kotlin/idea/k2/debugger/test/cases/K2IndyLambdaKotlinSteppingTestGenerated.java @@ -1698,6 +1698,11 @@ public abstract class K2IndyLambdaKotlinSteppingTestGenerated extends AbstractK2 runTest("../testData/stepping/custom/smartStepIntoDeferredSamLambdas.kt"); } + @TestMetadata("smartStepIntoFunWithContext.kt") + public void testSmartStepIntoFunWithContext() throws Exception { + runTest("../testData/stepping/custom/smartStepIntoFunWithContext.kt"); + } + @TestMetadata("smartStepIntoFunWithDefaultArgs.kt") public void testSmartStepIntoFunWithDefaultArgs() throws Exception { runTest("../testData/stepping/custom/smartStepIntoFunWithDefaultArgs.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/DebuggerTestCompilerFacility.kt b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/DebuggerTestCompilerFacility.kt index 6aa61da1e3c4..9eeb9a7ac4b2 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/DebuggerTestCompilerFacility.kt +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/DebuggerTestCompilerFacility.kt @@ -200,6 +200,7 @@ open class DebuggerTestCompilerFacility( private fun getCompilerOptionsCommonForLibAndSource(): List { val options = mutableListOf( "-Xlambdas=${compileConfig.lambdasGenerationScheme.description}", + "-Xcontext-receivers", ) if (compileConfig.languageVersion != null) { options.add("-language-version=${compileConfig.languageVersion}") diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaIrKotlinSteppingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaIrKotlinSteppingTestGenerated.java index 81ae54208ae7..24c24981b22b 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaIrKotlinSteppingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaIrKotlinSteppingTestGenerated.java @@ -1698,6 +1698,11 @@ public abstract class IndyLambdaIrKotlinSteppingTestGenerated extends AbstractIn runTest("testData/stepping/custom/smartStepIntoDeferredSamLambdas.kt"); } + @TestMetadata("smartStepIntoFunWithContext.kt") + public void testSmartStepIntoFunWithContext() throws Exception { + runTest("testData/stepping/custom/smartStepIntoFunWithContext.kt"); + } + @TestMetadata("smartStepIntoFunWithDefaultArgs.kt") public void testSmartStepIntoFunWithDefaultArgs() throws Exception { runTest("testData/stepping/custom/smartStepIntoFunWithDefaultArgs.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrKotlinSteppingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrKotlinSteppingTestGenerated.java index 64245047d8a1..32b801dae660 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrKotlinSteppingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrKotlinSteppingTestGenerated.java @@ -1698,6 +1698,11 @@ public abstract class IrKotlinSteppingTestGenerated extends AbstractIrKotlinStep runTest("testData/stepping/custom/smartStepIntoDeferredSamLambdas.kt"); } + @TestMetadata("smartStepIntoFunWithContext.kt") + public void testSmartStepIntoFunWithContext() throws Exception { + runTest("testData/stepping/custom/smartStepIntoFunWithContext.kt"); + } + @TestMetadata("smartStepIntoFunWithDefaultArgs.kt") public void testSmartStepIntoFunWithDefaultArgs() throws Exception { runTest("testData/stepping/custom/smartStepIntoFunWithDefaultArgs.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeKotlinSteppingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeKotlinSteppingTestGenerated.java index 663236bd01d2..29ae0fddbb93 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeKotlinSteppingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeKotlinSteppingTestGenerated.java @@ -1698,6 +1698,11 @@ public abstract class K1IdeK2CodeKotlinSteppingTestGenerated extends AbstractK1I runTest("testData/stepping/custom/smartStepIntoDeferredSamLambdas.kt"); } + @TestMetadata("smartStepIntoFunWithContext.kt") + public void testSmartStepIntoFunWithContext() throws Exception { + runTest("testData/stepping/custom/smartStepIntoFunWithContext.kt"); + } + @TestMetadata("smartStepIntoFunWithDefaultArgs.kt") public void testSmartStepIntoFunWithDefaultArgs() throws Exception { runTest("testData/stepping/custom/smartStepIntoFunWithDefaultArgs.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.kt b/plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.kt new file mode 100644 index 000000000000..80c7cdbda58b --- /dev/null +++ b/plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.kt @@ -0,0 +1,39 @@ +package smartStepIntoFunWithContext + +context(Double) +fun funWithContext(lambda: (Int) -> Unit) = lambda(42) + +fun testContext() { + with(42.0) { + // SMART_STEP_INTO_BY_INDEX: 1 + // RESUME: 1 + //Breakpoint!, lambdaOrdinal = -1 + funWithContext { println() } + + // SMART_STEP_INTO_BY_INDEX: 2 + // RESUME: 1 + //Breakpoint!, lambdaOrdinal = -1 + funWithContext { println() } + } +} + +@JvmInline +value class X(val x: Int) + +context(X) +private fun funWithContext2(x: Int) = Unit +private fun getInt(): Int = 5 + +fun testContextInline() { + with(X(1)) { + // SMART_STEP_INTO_BY_INDEX: 1 + // RESUME: 1 + //Breakpoint! + funWithContext2(getInt()) + } +} + +fun main() { + testContext() + testContextInline() +} diff --git a/plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.out b/plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.out new file mode 100644 index 000000000000..3e95cd2f8cba --- /dev/null +++ b/plugins/kotlin/jvm-debugger/test/testData/stepping/custom/smartStepIntoFunWithContext.out @@ -0,0 +1,14 @@ +LineBreakpoint created at smartStepIntoFunWithContext.kt:11 lambdaOrdinal = -1 +LineBreakpoint created at smartStepIntoFunWithContext.kt:16 lambdaOrdinal = -1 +LineBreakpoint created at smartStepIntoFunWithContext.kt:32 +Run Java +Connected to the target VM +smartStepIntoFunWithContext.kt:11 +smartStepIntoFunWithContext.kt:4 +smartStepIntoFunWithContext.kt:16 +smartStepIntoFunWithContext.kt:16 +smartStepIntoFunWithContext.kt:32 +smartStepIntoFunWithContext.kt:24 +Disconnected from the target VM + +Process finished with exit code 0