Debugger: Fix "Step over" with inline function calls in non-functions (KT-20342)

This commit is contained in:
Yan Zhulanow
2019-08-08 21:39:07 +09:00
parent 7bd628f989
commit 8325216d38
12 changed files with 206 additions and 10 deletions
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
import org.jetbrains.kotlin.idea.debugger.*
@@ -95,7 +96,7 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
}
data class KotlinSourcePosition(
val file: KtFile, val function: KtNamedFunction,
val file: KtFile, val declaration: KtDeclaration,
val linesRange: IntRange, val sourcePosition: SourcePosition
) {
companion object {
@@ -104,17 +105,20 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
if (sourcePosition.line < 0) return null
val elementAt = sourcePosition.elementAt ?: return null
val containingFunction = elementAt.parents
.filterIsInstance<KtNamedFunction>()
.firstOrNull { !it.isLocal } ?: return null
val startLineNumber = containingFunction.getLineNumber(true) + 1
val endLineNumber = containingFunction.getLineNumber(false) + 1
val containingDeclaration = elementAt.parents
.filterIsInstance<KtDeclaration>()
.filter { it is KtFunction || it is KtProperty || it is KtClassInitializer }
.firstOrNull { !KtPsiUtil.isLocal(it) }
?: return null
val startLineNumber = containingDeclaration.getLineNumber(true) + 1
val endLineNumber = containingDeclaration.getLineNumber(false) + 1
if (startLineNumber > endLineNumber) return null
val linesRange = startLineNumber..endLineNumber
return KotlinSourcePosition(file, containingFunction, linesRange, sourcePosition)
return KotlinSourcePosition(file, containingDeclaration, linesRange, sourcePosition)
}
}
}
@@ -128,9 +132,9 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
}
// Step over calls to lambda arguments in inline function while execution is already in that function
val containingFunctionDescriptor = kotlinSourcePosition.function.unsafeResolveToDescriptor()
if (InlineUtil.isInline(containingFunctionDescriptor)) {
val inlineArgumentsCallsIfAny = getInlineArgumentsCallsIfAny(sourcePosition, containingFunctionDescriptor)
val containingDescriptor = kotlinSourcePosition.declaration.resolveToDescriptorIfAny()
if (containingDescriptor != null && InlineUtil.isInline(containingDescriptor)) {
val inlineArgumentsCallsIfAny = getInlineArgumentsCallsIfAny(sourcePosition, containingDescriptor)
if (inlineArgumentsCallsIfAny != null && inlineArgumentsCallsIfAny.isNotEmpty()) {
return true
}
@@ -422,6 +422,31 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineCallInForRangeExpression.kt");
}
@TestMetadata("inlineFunInClassInitializer.kt")
public void testInlineFunInClassInitializer() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineFunInClassInitializer.kt");
}
@TestMetadata("inlineFunInClassInitializer2.kt")
public void testInlineFunInClassInitializer2() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineFunInClassInitializer2.kt");
}
@TestMetadata("inlineFunInConstructor.kt")
public void testInlineFunInConstructor() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineFunInConstructor.kt");
}
@TestMetadata("inlineFunInLazyProperty.kt")
public void testInlineFunInLazyProperty() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineFunInLazyProperty.kt");
}
@TestMetadata("inlineFunInPropertyGetter.kt")
public void testInlineFunInPropertyGetter() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineFunInPropertyGetter.kt");
}
@TestMetadata("inlineFunctionSameLines.kt")
public void testInlineFunctionSameLines() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/inlineFunctionSameLines.kt");
@@ -0,0 +1,23 @@
package inlineFunInClassInitializer
fun main() {
Goo()
}
class Foo(val a: String?)
class DumbList<T>(private val element: T) : AbstractList<T>() {
override val size get() = 1
override fun get(index: Int) = if (index == 0) element else error("Invalid index $index")
}
class Goo {
init {
//Breakpoint!
val a = DumbList(Foo("a"))
val b = a.mapNotNull { it.a }
1
}
}
// STEP_OVER: 2
@@ -0,0 +1,9 @@
LineBreakpoint created at inlineFunInClassInitializer.kt:17
Run Java
Connected to the target VM
inlineFunInClassInitializer.kt:17
inlineFunInClassInitializer.kt:18
inlineFunInClassInitializer.kt:19
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,29 @@
package inlineFunInClassInitializer2
fun main() {
Goo()
}
class Foo(val a: String?)
class DumbList<T>(private val element: T) : AbstractList<T>() {
override val size get() = 1
override fun get(index: Int) = if (index == 0) element else error("Invalid index $index")
}
class Goo {
init {
block {
//Breakpoint!
val a = DumbList(Foo("a"))
val b = a.mapNotNull { it.a }
1
}
}
}
fun <T> block(block: () -> T): T {
return block()
}
// STEP_OVER: 2
@@ -0,0 +1,9 @@
LineBreakpoint created at inlineFunInClassInitializer2.kt:18
Run Java
Connected to the target VM
inlineFunInClassInitializer2.kt:18
inlineFunInClassInitializer2.kt:19
inlineFunInClassInitializer2.kt:20
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,23 @@
package inlineFunInConstructor
fun main() {
Goo()
}
class Foo(val a: String?)
class DumbList<T>(private val element: T) : AbstractList<T>() {
override val size get() = 1
override fun get(index: Int) = if (index == 0) element else error("Invalid index $index")
}
class Goo {
constructor() {
//Breakpoint!
val a = DumbList(Foo("a"))
val b = a.mapNotNull { it.a }
1
}
}
// STEP_OVER: 2
@@ -0,0 +1,9 @@
LineBreakpoint created at inlineFunInConstructor.kt:17
Run Java
Connected to the target VM
inlineFunInConstructor.kt:17
inlineFunInConstructor.kt:18
inlineFunInConstructor.kt:19
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,23 @@
package inlineFunInLazyProperty
fun main() {
Goo().f
}
class Foo(val a: String?)
class DumbList<T>(private val element: T) : AbstractList<T>() {
override val size get() = 1
override fun get(index: Int) = if (index == 0) element else error("Invalid index $index")
}
class Goo {
val f by lazy {
//Breakpoint!
val a = DumbList(Foo("a"))
val b = a.mapNotNull { it.a }
1
}
}
// STEP_OVER: 2
@@ -0,0 +1,9 @@
LineBreakpoint created at inlineFunInLazyProperty.kt:17
Run Java
Connected to the target VM
inlineFunInLazyProperty.kt:17
inlineFunInLazyProperty.kt:18
inlineFunInLazyProperty.kt:19
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,24 @@
package inlineFunInPropertyGetter
fun main() {
Goo().f
}
class Foo(val a: String?)
class DumbList<T>(private val element: T) : AbstractList<T>() {
override val size get() = 1
override fun get(index: Int) = if (index == 0) element else error("Invalid index $index")
}
class Goo {
val f: Int
get() {
//Breakpoint!
val a = DumbList(Foo("a"))
val b = a.mapNotNull { it.a }
return 1
}
}
// STEP_OVER: 2
@@ -0,0 +1,9 @@
LineBreakpoint created at inlineFunInPropertyGetter.kt:18
Run Java
Connected to the target VM
inlineFunInPropertyGetter.kt:18
inlineFunInPropertyGetter.kt:19
inlineFunInPropertyGetter.kt:20
Disconnected from the target VM
Process finished with exit code 0