[debugger, kotlin] IDEA-346083 Fix smart-step-into methods with context receivers

GitOrigin-RevId: c7c1ee9ad93b21be2508669aeb4ca33791d40b44
This commit is contained in:
Maksim Zuev
2024-10-10 00:41:04 +00:00
committed by intellij-monorepo-bot
parent cf003cbd8d
commit 55c932f04f
14 changed files with 109 additions and 4 deletions
@@ -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<KaFunctionSymbol, KaValueParameterSymbol>? {
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 {
@@ -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) {
@@ -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
}
@@ -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)
@@ -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
@@ -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");
@@ -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");
@@ -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");
@@ -200,6 +200,7 @@ open class DebuggerTestCompilerFacility(
private fun getCompilerOptionsCommonForLibAndSource(): List<String> {
val options = mutableListOf(
"-Xlambdas=${compileConfig.lambdasGenerationScheme.description}",
"-Xcontext-receivers",
)
if (compileConfig.languageVersion != null) {
options.add("-language-version=${compileConfig.languageVersion}")
@@ -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");
@@ -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");
@@ -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");
@@ -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()
}
@@ -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