diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinBasicStepMethodFilter.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinBasicStepMethodFilter.kt index dcd4ddb58e0..d22ee3c5386 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinBasicStepMethodFilter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinBasicStepMethodFilter.kt @@ -20,8 +20,10 @@ import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.NamedMethodFilter import com.intellij.util.Range import com.sun.jdi.Location +import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.* +import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations import org.jetbrains.kotlin.idea.util.application.runReadAction @@ -33,8 +35,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate import org.jetbrains.kotlin.resolve.DescriptorUtils class KotlinBasicStepMethodFilter( - val targetDescriptor: CallableMemberDescriptor, - val myCallingExpressionLines: Range + private val targetDescriptor: CallableMemberDescriptor, + private val myCallingExpressionLines: Range ) : NamedMethodFilter { private val myTargetMethodName = when (targetDescriptor) { is ClassDescriptor, is ConstructorDescriptor -> "" @@ -50,7 +52,7 @@ class KotlinBasicStepMethodFilter( val method = location.method() if (myTargetMethodName != method.name()) return false - val positionManager = process.positionManager ?: return false + val positionManager = process.positionManager val currentDescriptor = runReadAction { val elementAt = positionManager.getSourcePosition(location)?.elementAt @@ -72,6 +74,10 @@ class KotlinBasicStepMethodFilter( if (compareDescriptors(currentDescriptor, targetDescriptor)) return true + if (targetDescriptor is FunctionInvokeDescriptor && currentDescriptor is FunctionDescriptor) { + return isCompatibleSignatures(targetDescriptor, currentDescriptor) + } + // We should stop if current descriptor overrides the target one or some base descriptor of target // (if target descriptor is delegation or fake override) @@ -94,4 +100,24 @@ class KotlinBasicStepMethodFilter( private fun compareDescriptors(d1: DeclarationDescriptor, d2: DeclarationDescriptor): Boolean { return d1 == d2 || d1.original == d2.original +} + +private fun isCompatibleSignatures(target: FunctionDescriptor, current: FunctionDescriptor): Boolean { + // A very primitive approximation + if (target.valueParameters.size != current.valueParameters.size) { + return false + } + + if (target.returnType != current.returnType) { + return false + } + + for ((i, targetParam) in target.valueParameters.withIndex()) { + val currentParam = current.valueParameters[i] + if (targetParam.type != currentParam.type) { + return false + } + } + + return true } \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.kt b/idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.kt new file mode 100644 index 00000000000..c49c0b8658a --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.kt @@ -0,0 +1,50 @@ +package smartStepIntoToLambdaParameter + +fun nonDefaultParameter(paramFun: (Int) -> Int) { + // SMART_STEP_INTO_BY_INDEX: 0 + // RESUME: 1 + //Breakpoint! + paramFun(1) +} + +fun defaultParameter(f: (String) -> Int = { it.toInt() }) { + // SMART_STEP_INTO_BY_INDEX: 0 + // RESUME: 1 + //Breakpoint! + f("12") +} + +fun badSignatureIsSkipped(f: (String) -> Int = { it.toInt() }, paramFun: (Int) -> Int) { + // SMART_STEP_INTO_BY_INDEX: 0 + // RESUME: 1 + //Breakpoint! + paramFun(f("12")) +} + +fun nonDefaultWithAnonymousFun(paramFun: (Int) -> Int) { + // SMART_STEP_INTO_BY_INDEX: 0 + // RESUME: 1 + //Breakpoint! + paramFun(12) +} + +fun withExtensionParameters(paramFun: Int.() -> Int) { + // SMART_STEP_INTO_BY_INDEX: 0 + // RESUME: 1 + //Breakpoint! + 12.paramFun() +} + +fun main(args: Array) { + val localFun : (Int) -> Int = { it + 1 } + + nonDefaultParameter(localFun) + + defaultParameter() + + badSignatureIsSkipped { it + 1 } + + nonDefaultWithAnonymousFun(fun (i: Int): Int { return i }) + + withExtensionParameters { this } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.out b/idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.out new file mode 100644 index 00000000000..e8f001bec24 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.out @@ -0,0 +1,20 @@ +LineBreakpoint created at smartStepIntoToLambdaParameter.kt:7 +LineBreakpoint created at smartStepIntoToLambdaParameter.kt:14 +LineBreakpoint created at smartStepIntoToLambdaParameter.kt:21 +LineBreakpoint created at smartStepIntoToLambdaParameter.kt:28 +LineBreakpoint created at smartStepIntoToLambdaParameter.kt:35 +Run Java +Connected to the target VM +smartStepIntoToLambdaParameter.kt:7 +smartStepIntoToLambdaParameter.kt:39 +smartStepIntoToLambdaParameter.kt:14 +smartStepIntoToLambdaParameter.kt:10 +smartStepIntoToLambdaParameter.kt:21 +smartStepIntoToLambdaParameter.kt:45 +smartStepIntoToLambdaParameter.kt:28 +smartStepIntoToLambdaParameter.kt:47 +smartStepIntoToLambdaParameter.kt:35 +smartStepIntoToLambdaParameter.kt:49 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index df412d19d8b..a9b09d5645b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -1259,6 +1259,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doCustomTest(fileName); } + @TestMetadata("smartStepIntoToLambdaParameter.kt") + public void testSmartStepIntoToLambdaParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoToLambdaParameter.kt"); + doCustomTest(fileName); + } + @TestMetadata("smartStepIntoWithDelegates.kt") public void testSmartStepIntoWithDelegates() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoWithDelegates.kt");