[debugger] IDEA-338583 Fix smart-step-into lambdas with parameters destructing

Accept lambda parameters as suitable source position element if no other element available on the line


Merge-request: IJ-MR-121987
Merged-by: Maksim Zuev <Maksim.Zuev@jetbrains.com>

GitOrigin-RevId: 7159546a07067100ad35808934d5c1fcc1795f19
This commit is contained in:
Maksim Zuev
2023-12-12 10:11:09 +00:00
committed by intellij-monorepo-bot
parent 2c53e2141a
commit bba4e0edd2
4 changed files with 81 additions and 20 deletions

View File

@@ -12,6 +12,7 @@ import com.sun.jdi.Location
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
import org.jetbrains.kotlin.idea.base.psi.isMultiLine
import org.jetbrains.kotlin.idea.debugger.base.util.safeMethod
import org.jetbrains.kotlin.idea.debugger.breakpoints.inTheMethod
import org.jetbrains.kotlin.idea.debugger.core.DebuggerUtils.isGeneratedIrBackendLambdaMethodName
import org.jetbrains.kotlin.idea.debugger.core.DebuggerUtils.trimIfMangledInBytecode
import org.jetbrains.kotlin.idea.debugger.core.stepping.StopOnReachedMethodFilter
@@ -56,16 +57,7 @@ class KotlinLambdaMethodFilter(
private fun Location.matchesLambda(process: DebugProcessImpl, lambda: KtFunction): Boolean {
val sourcePosition = process.positionManager.getSourcePosition(this) ?: return true
return runReadAction {
val bodyExpression = lambda.bodyExpression
val elementAt = sourcePosition.elementAt
if (elementAt == bodyExpression) {
return@runReadAction true
}
val blockAt = elementAt?.parentOfType<KtBlockExpression>(withSelf = true)
blockAt == bodyExpression
}
return runReadAction { inTheMethod(sourcePosition, lambda) }
}
override fun getCallingExpressionLines() = callingExpressionLines

View File

@@ -75,6 +75,8 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -212,14 +214,18 @@ class KotlinPositionManager(private val debugProcess: DebugProcess) : MultiReque
}
private fun getFirstElementInsideLambdaOnLine(file: PsiFile, lambda: KtFunction, line: Int): PsiElement? {
val lineRange = file.getRangeOfLine(line) ?: return null
val elementsOnLine = file.findElementsOfTypeInRange<PsiElement>(lineRange)
.filter { it.startOffset in lineRange && it.parents.contains(lambda) }
// Prefer elements that are inside body range
val bodyRange = lambda.bodyExpression!!.textRange
val searchRange = file.getRangeOfLine(line)?.intersection(bodyRange) ?: return null
val elementsAtLine = file.findElementsOfTypeInRange<PsiElement>(searchRange)
elementsOnLine.firstOrNull {it.startOffset in bodyRange } ?.let { return it }
// Prefer elements that start on the line
elementsAtLine.firstOrNull { it.textRange.startOffset in searchRange }?.let { return it }
// Prefer KtElements
elementsOnLine.firstOrNull { it is KtElement } ?.let { return it }
return elementsAtLine.firstOrNull()
return elementsOnLine.firstOrNull()
}
private fun Location.shouldBeTreatedAsReentrantSourcePosition(psiFile: PsiFile, sourceFileName: String): Boolean {

View File

@@ -1,6 +1,13 @@
package smartStepIntoLambdaWithparametersDestructuring
fun main() {
testWithIndex()
testNoDestruction()
testDestruction()
testInlineDestruction()
}
fun testWithIndex() {
// STEP_OVER: 1
//Breakpoint!
val list = listOf(1, 2, 3)
@@ -10,6 +17,47 @@ fun main() {
list.withIndex().find { (i, value) ->
i + 1 > value
}
// RESUME: 1
}
fun testNoDestruction() {
// STEP_OVER: 1
//Breakpoint!
val a = Ax(0, 0)
// SMART_STEP_INTO_BY_INDEX: 2
// STEP_OVER: 1
a.foo { x ->
println("$x")
}
// RESUME: 1
}
fun testDestruction() {
// STEP_OVER: 1
//Breakpoint!
val a = Ax(0, 0)
// SMART_STEP_INTO_BY_INDEX: 2
// STEP_OVER: 1
a.foo { (x, y) ->
println("$x$y")
}
// RESUME: 1
}
fun testInlineDestruction() {
// STEP_OVER: 1
//Breakpoint!
val a = Ax(0, 0)
// SMART_STEP_INTO_BY_INDEX: 2
// STEP_OVER: 1
a.inlineFoo { (x, y) ->
println("$x$y")
}
// RESUME: 1
}
fun Ax.foo(f: (Ax) -> Unit) = f(this)
fun Ax.inlineFoo(f: (Ax) -> Unit) = f(this)
data class Ax(val x: Int, val y: Int)
// IGNORE_K2

View File

@@ -1,10 +1,25 @@
LineBreakpoint created at smartStepIntoLambdaWithparametersDestructuring.kt:6
LineBreakpoint created at smartStepIntoLambdaWithparametersDestructuring.kt:13
LineBreakpoint created at smartStepIntoLambdaWithparametersDestructuring.kt:26
LineBreakpoint created at smartStepIntoLambdaWithparametersDestructuring.kt:38
LineBreakpoint created at smartStepIntoLambdaWithparametersDestructuring.kt:50
Run Java
Connected to the target VM
smartStepIntoLambdaWithparametersDestructuring.kt:6
smartStepIntoLambdaWithparametersDestructuring.kt:10
smartStepIntoLambdaWithparametersDestructuring.kt:11
smartStepIntoLambdaWithparametersDestructuring.kt:10
smartStepIntoLambdaWithparametersDestructuring.kt:13
smartStepIntoLambdaWithparametersDestructuring.kt:17
smartStepIntoLambdaWithparametersDestructuring.kt:17
smartStepIntoLambdaWithparametersDestructuring.kt:18
smartStepIntoLambdaWithparametersDestructuring.kt:26
smartStepIntoLambdaWithparametersDestructuring.kt:29
smartStepIntoLambdaWithparametersDestructuring.kt:30
smartStepIntoLambdaWithparametersDestructuring.kt:31
smartStepIntoLambdaWithparametersDestructuring.kt:38
smartStepIntoLambdaWithparametersDestructuring.kt:41
smartStepIntoLambdaWithparametersDestructuring.kt:42
smartStepIntoLambdaWithparametersDestructuring.kt:43
smartStepIntoLambdaWithparametersDestructuring.kt:50
smartStepIntoLambdaWithparametersDestructuring.kt:53
smartStepIntoLambdaWithparametersDestructuring.kt:54
smartStepIntoLambdaWithparametersDestructuring.kt:55
Disconnected from the target VM
Process finished with exit code 0