diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt index 9ba6674669b..6b1fc6cf15b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -58,8 +59,10 @@ public class KotlinSteppingCommandProvider: JvmSteppingCommandProvider() { val file = sourcePosition.file as? JetFile ?: return null - val inlinedArguments = getInlinedArgumentsIfAny(sourcePosition) - if (inlinedArguments.isEmpty()) return null + val inlineFunctionCalls = getInlineFunctionCallsIfAny(sourcePosition) + if (inlineFunctionCalls.isEmpty()) return null + + val inlinedArguments = getInlinedArgumentsIfAny(inlineFunctionCalls) if (inlinedArguments.any { it.shouldNotUseStepOver(sourcePosition.elementAt) }) { return null @@ -305,7 +308,16 @@ public class KotlinSteppingCommandProvider: JvmSteppingCommandProvider() { return result } - private fun getInlinedArgumentsIfAny(sourcePosition: SourcePosition): List { + private fun getInlinedArgumentsIfAny(inlineFunctionCalls: List): List { + return inlineFunctionCalls.flatMap { + it.valueArguments + .map { it.getArgumentExpression() } + .filterIsInstance() + .map { it.functionLiteral } + } + } + + private fun getInlineFunctionCallsIfAny(sourcePosition: SourcePosition): List { val file = sourcePosition.file as? JetFile ?: return emptyList() val lineNumber = sourcePosition.line var elementAt = sourcePosition.elementAt @@ -327,20 +339,23 @@ public class KotlinSteppingCommandProvider: JvmSteppingCommandProvider() { val start = topMostElement.startOffset val end = topMostElement.endOffset - val allInlinedArguments = CodeInsightUtils. - findElementsOfClassInRange(file, start, end, JetFunctionLiteral::class.java, JetNamedFunction::class.java) - .filter { JetPsiUtil.getParentCallIfPresent(it as JetExpression) != null } - .filterIsInstance() - .filter { - val context = it.analyze(BodyResolveMode.PARTIAL) - InlineUtil.isInlinedArgument(it, context, false) - } + fun isInlineCall(expr: JetExpression): Boolean { + val context = expr.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = expr.getResolvedCall(context) ?: return false + return InlineUtil.isInline(resolvedCall.resultingDescriptor) + } + + val allInlineFunctionCalls = CodeInsightUtils. + findElementsOfClassInRange(file, start, end, JetExpression::class.java) + .map { JetPsiUtil.getParentCallIfPresent(it as JetExpression) } + .filterIsInstance() + .filter { isInlineCall(it) } + .toSet() // It is necessary to check range because of multiline assign var linesRange = lineNumber..lineNumber - return allInlinedArguments.filter { - val parentCall = JetPsiUtil.getParentCallIfPresent(it)!! - val shouldInclude = parentCall.getLineNumber() in linesRange + return allInlineFunctionCalls.filter { + val shouldInclude = it.getLineNumber() in linesRange if (shouldInclude) { linesRange = Math.min(linesRange.start, it.getLineNumber())..Math.max(linesRange.end, it.getLineNumber(false)) } diff --git a/idea/testData/debugger/tinyApp/outs/stepOverReifiedParam.out b/idea/testData/debugger/tinyApp/outs/stepOverReifiedParam.out new file mode 100644 index 00000000000..12d42a2967a --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stepOverReifiedParam.out @@ -0,0 +1,10 @@ +LineBreakpoint created at stepOverReifiedParam.kt:5 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stepOverReifiedParam.StepOverReifiedParamPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stepOverReifiedParam.kt:5 +stepOverReifiedParam.kt:6 +stepOverReifiedParam.kt:7 +stepOverReifiedParam.kt:8 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverReifiedParam.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverReifiedParam.kt new file mode 100644 index 00000000000..a7a95a2d789 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverReifiedParam.kt @@ -0,0 +1,15 @@ +package stepOverReifiedParam + +fun main(args: Array) { + //Breakpoint! + val a = 1 + foo(a) + val b = 1 +} + +inline fun foo(t: T): T { + val a = t + return a +} + +// STEP_OVER: 3 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index 043cf887104..9cbdde218c7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -367,6 +367,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("stepOverReifiedParam.kt") + public void testStepOverReifiedParam() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverReifiedParam.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stepOverSimpleFun.kt") public void testStepOverSimpleFun() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverSimpleFun.kt");