Debugger: refactor smart step into to use descriptors instead of psi elements (KT-13485)
#KT-13485 Fixed
This commit is contained in:
committed by
Nikolay Krasko
parent
f7203da2d6
commit
0fb5a18a26
@@ -50,9 +50,9 @@ import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.util.*
|
||||
@@ -116,9 +116,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
if (lambdaOrFunIfInside != null) {
|
||||
return SourcePosition.createFromElement(lambdaOrFunIfInside.bodyExpression!!)
|
||||
}
|
||||
val property = getParameterIfInConstructor(location, psiFile, lineNumber)
|
||||
if (property != null) {
|
||||
return SourcePosition.createFromElement(property)
|
||||
val elementInDeclaration = getElementForDeclarationLine(location, psiFile, lineNumber)
|
||||
if (elementInDeclaration != null) {
|
||||
return SourcePosition.createFromElement(elementInDeclaration)
|
||||
}
|
||||
|
||||
if (lineNumber > psiFile.getLineCount() && myDebugProcess.isDexDebug()) {
|
||||
@@ -134,18 +134,25 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
||||
return SourcePosition.createFromLine(psiFile, lineNumber)
|
||||
}
|
||||
|
||||
private fun getParameterIfInConstructor(location: Location, file: KtFile, lineNumber: Int): KtParameter? {
|
||||
// Returns a property or a constructor if debugger stops at class declaration
|
||||
private fun getElementForDeclarationLine(location: Location, file: KtFile, lineNumber: Int): KtElement? {
|
||||
val lineStartOffset = file.getLineStartOffset(lineNumber) ?: return null
|
||||
val elementAt = file.findElementAt(lineStartOffset)
|
||||
val contextElement = KotlinCodeFragmentFactory.getContextElement(elementAt)
|
||||
|
||||
if (contextElement !is KtClass) return null
|
||||
|
||||
val methodName = location.method().name()
|
||||
if (contextElement is KtClass && JvmAbi.isGetterName(methodName)) {
|
||||
val parameterForGetter = contextElement.getPrimaryConstructor()?.valueParameters?.firstOrNull() {
|
||||
it.hasValOrVar() && it.name != null && JvmAbi.getterName(it.name!!) == methodName
|
||||
} ?: return null
|
||||
return parameterForGetter
|
||||
return when {
|
||||
JvmAbi.isGetterName(methodName) -> {
|
||||
val parameterForGetter = contextElement.getPrimaryConstructor()?.valueParameters?.firstOrNull() {
|
||||
it.hasValOrVar() && it.name != null && JvmAbi.getterName(it.name!!) == methodName
|
||||
} ?: return null
|
||||
parameterForGetter
|
||||
}
|
||||
methodName == "<init>" -> contextElement.getPrimaryConstructor()
|
||||
else -> null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getLambdaOrFunIfInside(location: Location, file: KtFile, lineNumber: Int): KtFunction? {
|
||||
|
||||
+38
-16
@@ -16,31 +16,31 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.stepping
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.NamedMethodFilter
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||
import com.intellij.util.Range
|
||||
import com.sun.jdi.Location
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
|
||||
import org.jetbrains.kotlin.psi.KtConstructor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtPropertyAccessor
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
|
||||
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
|
||||
|
||||
class KotlinBasicStepMethodFilter(
|
||||
val resolvedElement: KtElement,
|
||||
val descriptor: CallableDescriptor,
|
||||
val myCallingExpressionLines: Range<Int>
|
||||
) : NamedMethodFilter {
|
||||
private val myTargetMethodName: String
|
||||
|
||||
init {
|
||||
myTargetMethodName = when (resolvedElement) {
|
||||
is KtAnonymousInitializer -> "<init>"
|
||||
is KtConstructor<*> -> "<init>"
|
||||
is KtPropertyAccessor -> JvmAbi.getterName((resolvedElement.property).name!!)
|
||||
else -> resolvedElement.name!!
|
||||
myTargetMethodName = when (descriptor) {
|
||||
is ClassDescriptor, is ConstructorDescriptor -> "<init>"
|
||||
is PropertyAccessorDescriptor -> JvmAbi.getterName(descriptor.correspondingProperty.name.asString())
|
||||
else -> descriptor.name.asString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,35 @@ class KotlinBasicStepMethodFilter(
|
||||
val method = location.method()
|
||||
if (myTargetMethodName != method.name()) return false
|
||||
|
||||
val sourcePosition = runReadAction { SourcePosition.createFromElement(resolvedElement) } ?: return false
|
||||
val positionManager = process.positionManager ?: return false
|
||||
|
||||
val classes = positionManager.getAllClasses(sourcePosition)
|
||||
val descriptor = runReadAction {
|
||||
val elementAt = positionManager.getSourcePosition(location)?.elementAt
|
||||
|
||||
return classes.any {
|
||||
it == location.declaringType() || DebuggerUtilsEx.isAssignableFrom(it.name(), location.declaringType())
|
||||
val declaration = elementAt?.getParentOfTypesAndPredicate(false, KtDeclaration::class.java) {
|
||||
it !is KtProperty || !it.isLocal
|
||||
}
|
||||
|
||||
if (declaration is KtClass && method.name() == "<init>") {
|
||||
(declaration.resolveToDescriptor() as? ClassDescriptor)?.unsubstitutedPrimaryConstructor
|
||||
} else {
|
||||
declaration?.resolveToDescriptor()
|
||||
}
|
||||
} ?: return false
|
||||
|
||||
fun compareDescriptors(d1: DeclarationDescriptor, d2: DeclarationDescriptor): Boolean {
|
||||
return d1 == d2 || d1.original == d2.original
|
||||
}
|
||||
|
||||
if (compareDescriptors(descriptor, this.descriptor)) return true
|
||||
|
||||
if (descriptor is CallableDescriptor) {
|
||||
return descriptor.findTopMostOverriddenDescriptors().any { compareDescriptors(it, this.descriptor) } ||
|
||||
this.descriptor.findTopMostOverriddenDescriptors().any { compareDescriptors(it, descriptor)}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+6
-6
@@ -3,23 +3,23 @@ package org.jetbrains.kotlin.idea.debugger.stepping
|
||||
import com.intellij.debugger.actions.SmartStepTarget
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.Range
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import javax.swing.Icon
|
||||
|
||||
class KotlinMethodSmartStepTarget(
|
||||
val resolvedElement: KtElement,
|
||||
val descriptor: CallableDescriptor,
|
||||
label: String,
|
||||
highlightElement: PsiElement,
|
||||
lines: Range<Int>
|
||||
): SmartStepTarget(label, highlightElement, false, lines) {
|
||||
override fun getIcon(): Icon? {
|
||||
return when {
|
||||
resolvedElement is KtNamedFunction && resolvedElement.receiverTypeReference != null -> KotlinIcons.EXTENSION_FUNCTION
|
||||
descriptor.isExtension -> KotlinIcons.EXTENSION_FUNCTION
|
||||
else -> KotlinIcons.FUNCTION
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ class KotlinMethodSmartStepTarget(
|
||||
|
||||
if (other == null || other !is KotlinMethodSmartStepTarget) return false
|
||||
|
||||
return resolvedElement == other.resolvedElement
|
||||
return descriptor == other.descriptor
|
||||
}
|
||||
|
||||
override fun hashCode() = resolvedElement.hashCode()
|
||||
override fun hashCode() = descriptor.hashCode()
|
||||
}
|
||||
+22
-22
@@ -27,12 +27,14 @@ import com.intellij.util.Range
|
||||
import com.intellij.util.containers.OrderedSet
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.load.java.isFromJava
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||
@@ -146,20 +148,15 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
val delegatedResolvedCall = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor]
|
||||
if (delegatedResolvedCall == null) {
|
||||
val getter = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, getterDescriptor)
|
||||
if (getter is KtPropertyAccessor && (getter.bodyExpression != null || getter.equalsToken != null)) {
|
||||
if (getter is KtPropertyAccessor && getter.hasBody()) {
|
||||
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(getter, label, expression, lines))
|
||||
result.add(KotlinMethodSmartStepTarget(getterDescriptor, label, expression, lines))
|
||||
}
|
||||
}
|
||||
else {
|
||||
val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor
|
||||
if (delegatedPropertyGetterDescriptor is CallableMemberDescriptor) {
|
||||
val function = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, delegatedPropertyGetterDescriptor)
|
||||
if (function is KtNamedFunction || function is KtSecondaryConstructor) {
|
||||
val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(function as KtFunction, label, expression, lines))
|
||||
}
|
||||
}
|
||||
val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, label, expression, lines))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,19 +169,22 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
if (descriptor is FunctionDescriptor && !isIntrinsic(descriptor)) {
|
||||
val resolvedElement = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
|
||||
if (resolvedElement is KtNamedFunction || resolvedElement is KtConstructor<*>) {
|
||||
if (descriptor.isFromJava) {
|
||||
(DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) as? PsiMethod)?.let {
|
||||
result.add(MethodSmartStepTarget(it, null, expression, false, lines))
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) {
|
||||
val psiElement = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
|
||||
if (psiElement is KtClass && psiElement.getAnonymousInitializers().isEmpty()) {
|
||||
// There is no constructor or init block, so do not show it in smart step into
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val label = KotlinMethodSmartStepTarget.calcLabel(descriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(resolvedElement as KtFunction, label, expression, lines))
|
||||
}
|
||||
else if (resolvedElement is PsiMethod) {
|
||||
result.add(MethodSmartStepTarget(resolvedElement, null, expression, false, lines))
|
||||
}
|
||||
else if (resolvedElement is KtClass) {
|
||||
resolvedElement.getAnonymousInitializers().firstOrNull()?.let {
|
||||
val label = KotlinMethodSmartStepTarget.calcLabel(descriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(it, label, expression, lines))
|
||||
}
|
||||
result.add(KotlinMethodSmartStepTarget(descriptor, label, expression, lines))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
|
||||
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
|
||||
return when (stepTarget) {
|
||||
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.resolvedElement, stepTarget.callingExpressionLines!!)
|
||||
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.callingExpressionLines!!)
|
||||
is KotlinLambdaSmartStepTarget -> KotlinLambdaMethodFilter(stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline)
|
||||
else -> super.createMethodFilter(stepTarget)
|
||||
}
|
||||
|
||||
@@ -9,30 +9,36 @@ LineBreakpoint created at smartStepIntoConstructor.kt:35
|
||||
LineBreakpoint created at smartStepIntoConstructor.kt:39
|
||||
LineBreakpoint created at smartStepIntoConstructor.kt:43
|
||||
LineBreakpoint created at smartStepIntoConstructor.kt:47
|
||||
LineBreakpoint created at smartStepIntoConstructor.kt:51
|
||||
LineBreakpoint created at smartStepIntoConstructor.kt:55
|
||||
!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! smartStepIntoConstructor.SmartStepIntoConstructorKt
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
smartStepIntoConstructor.kt:7
|
||||
smartStepIntoConstructor.kt:51
|
||||
smartStepIntoConstructor.kt:59
|
||||
smartStepIntoConstructor.kt:11
|
||||
smartStepIntoConstructor.kt:52
|
||||
smartStepIntoConstructor.kt:60
|
||||
smartStepIntoConstructor.kt:15
|
||||
smartStepIntoConstructor.kt:54
|
||||
smartStepIntoConstructor.kt:62
|
||||
smartStepIntoConstructor.kt:19
|
||||
smartStepIntoConstructor.kt:57
|
||||
smartStepIntoConstructor.kt:65
|
||||
smartStepIntoConstructor.kt:23
|
||||
smartStepIntoConstructor.kt:61
|
||||
smartStepIntoConstructor.kt:27
|
||||
smartStepIntoConstructor.kt:66
|
||||
smartStepIntoConstructor.kt:31
|
||||
smartStepIntoConstructor.kt:69
|
||||
smartStepIntoConstructor.kt:35
|
||||
smartStepIntoConstructor.kt:27
|
||||
smartStepIntoConstructor.kt:74
|
||||
smartStepIntoConstructor.kt:31
|
||||
smartStepIntoConstructor.kt:77
|
||||
smartStepIntoConstructor.kt:35
|
||||
smartStepIntoConstructor.kt:82
|
||||
smartStepIntoConstructor.kt:39
|
||||
smartStepIntoConstructor.kt:81
|
||||
smartStepIntoConstructor.kt:43
|
||||
smartStepIntoConstructor.kt:89
|
||||
smartStepIntoConstructor.kt:47
|
||||
smartStepIntoConstructor.kt:43
|
||||
smartStepIntoConstructor.kt:97
|
||||
smartStepIntoConstructor.kt:47
|
||||
smartStepIntoConstructor.kt:105
|
||||
smartStepIntoConstructor.kt:51
|
||||
smartStepIntoConstructor.kt:112
|
||||
smartStepIntoConstructor.kt:55
|
||||
smartStepIntoConstructor.kt:113
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
LineBreakpoint created at smartStepIntoInterfaceFun.kt:23
|
||||
LineBreakpoint created at smartStepIntoInterfaceFun.kt:27
|
||||
!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! smartStepIntoInterfaceFun.SmartStepIntoInterfaceFunKt
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
smartStepIntoInterfaceFun.kt:23
|
||||
smartStepIntoInterfaceFun.kt:11
|
||||
smartStepIntoInterfaceFun.kt:27
|
||||
smartStepIntoInterfaceFun.kt:14
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
|
||||
+12
@@ -45,6 +45,14 @@ fun main(args: Array<String>) {
|
||||
// RESUME: 1
|
||||
//Breakpoint!
|
||||
N(1)
|
||||
// SMART_STEP_INTO_BY_INDEX: 1
|
||||
// RESUME: 1
|
||||
//Breakpoint!
|
||||
O(1)
|
||||
// SMART_STEP_INTO_BY_INDEX: 1
|
||||
// RESUME: 1
|
||||
//Breakpoint!
|
||||
O(1, "1")
|
||||
|
||||
}
|
||||
|
||||
@@ -100,4 +108,8 @@ class N {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
class O<T>(i: T) {
|
||||
constructor(i: Int, j: T): this(j) {
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// KT-13485
|
||||
package smartStepIntoInterfaceFun
|
||||
|
||||
interface ObjectFace<T> {
|
||||
fun act()
|
||||
fun act2(t: T)
|
||||
}
|
||||
|
||||
class ObjectClass : ObjectFace<Int> {
|
||||
override fun act() {
|
||||
println()
|
||||
}
|
||||
override fun act2(t: Int) {
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val simple: ObjectFace<Int> = ObjectClass()
|
||||
// SMART_STEP_INTO_BY_INDEX: 1
|
||||
// RESUME: 1
|
||||
//Breakpoint!
|
||||
simple.act()
|
||||
// SMART_STEP_INTO_BY_INDEX: 1
|
||||
// RESUME: 1
|
||||
//Breakpoint!
|
||||
simple.act2(1)
|
||||
}
|
||||
@@ -55,6 +55,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
||||
import java.io.File
|
||||
import java.lang.AssertionError
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
@@ -213,7 +214,7 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
stepTarget ->
|
||||
when (stepTarget) {
|
||||
is KotlinLambdaSmartStepTarget -> KotlinLambdaMethodFilter(stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!!, stepTarget.isInline)
|
||||
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.resolvedElement, stepTarget.getCallingExpressionLines()!!)
|
||||
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.getCallingExpressionLines()!!)
|
||||
is MethodSmartStepTarget -> BasicStepMethodFilter(stepTarget.method, stepTarget.getCallingExpressionLines())
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -890,6 +890,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
||||
doCustomTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartStepIntoInterfaceFun.kt")
|
||||
public void testSmartStepIntoInterfaceFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoInterfaceFun.kt");
|
||||
doCustomTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartStepIntoInterfaceImpl.kt")
|
||||
public void testSmartStepIntoInterfaceImpl() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoInterfaceImpl.kt");
|
||||
|
||||
Reference in New Issue
Block a user