diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaSourcePositionHighlighter.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaSourcePositionHighlighter.java index f2b2a1991839..5f438e430ad5 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaSourcePositionHighlighter.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaSourcePositionHighlighter.java @@ -21,7 +21,6 @@ import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiKeyword; import com.intellij.psi.PsiLambdaExpression; /** @@ -31,10 +30,10 @@ public class JavaSourcePositionHighlighter extends SourcePositionHighlighter imp @Override public TextRange getHighlightRange(SourcePosition sourcePosition) { // Highlight only return keyword in case of conditional return breakpoint. - if (sourcePosition.getElementAt() instanceof PsiKeyword retKeyword && - retKeyword.getText().equals(PsiKeyword.RETURN) && - retKeyword == JavaLineBreakpointType.findSingleConditionalReturn(sourcePosition)) { - return retKeyword.getTextRange(); + PsiElement element = sourcePosition.getElementAt(); + if (JavaLineBreakpointType.isReturnKeyword(element) && + element == JavaLineBreakpointType.findSingleConditionalReturn(sourcePosition)) { + return element.getTextRange(); } // Highlight only lambda body in case of lambda breakpoint. diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java index 4a2170dd1e9e..7f86670ff84d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -181,24 +181,32 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio } } - // Adjust position if we've stopped on conditional return. - if (myDebugProcess.getVirtualMachineProxy().canGetBytecodes()) { - PsiElement ret = JavaLineBreakpointType.findSingleConditionalReturn(sourcePosition); + SourcePosition condRetPos = adjustPositionForConditionalReturn(myDebugProcess, location, psiFile, lineNumber); + if (condRetPos != null) { + sourcePosition = condRetPos; + } + + return new JavaSourcePosition(sourcePosition, location.declaringType(), method, lambdaOrdinal); + } + + @Nullable + public static SourcePosition adjustPositionForConditionalReturn(DebugProcess debugProcess, Location location, PsiFile file, int lineNumber) { + if (debugProcess.getVirtualMachineProxy().canGetBytecodes()) { + PsiElement ret = JavaLineBreakpointType.findSingleConditionalReturn(file, lineNumber); if (ret != null) { - byte[] bytecodes = method.bytecodes(); + byte[] bytecodes = DebuggerUtilsEx.getMethod(location).bytecodes(); int bytecodeOffs = Math.toIntExact(location.codeIndex()); // Implicit return instruction at the end of bytecode should not be treated as conditional return. // (Note that we also relay on the fact that all return instructions have no operands.) if (0 <= bytecodeOffs && bytecodeOffs < bytecodes.length - 1) { int opcode = bytecodes[bytecodeOffs] & 0xFF; if (Opcodes.IRETURN <= opcode && opcode <= Opcodes.RETURN) { - sourcePosition = SourcePosition.createFromOffset(sourcePosition.getFile(), ret.getTextOffset()); + return SourcePosition.createFromOffset(file, ret.getTextOffset()); } } } } - - return new JavaSourcePosition(sourcePosition, location.declaringType(), method, lambdaOrdinal); + return null; } public static class JavaSourcePosition extends RemappedSourcePosition { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java index b1346606cae0..e6da54346e47 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java @@ -17,6 +17,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.LeafElement; import com.intellij.util.DocumentUtil; import com.intellij.util.Processor; import com.intellij.util.SmartList; @@ -46,8 +47,6 @@ import java.util.Collections; import java.util.List; import java.util.stream.Stream; -import static org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties.COND_RET_CODE; - /** * Base class for java line-connected breakpoints (line, method, field) * @@ -115,7 +114,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase lambdas = DebuggerUtilsEx.collectLambdas(pos, true); - PsiKeyword condRet = findSingleConditionalReturn(project, document, position.getLine()); + PsiElement condRet = findSingleConditionalReturn(project, document, position.getLine()); if ((lambdas.isEmpty() || (lambdas.contains(startMethod) && lambdas.size() == 1)) && condRet == null) { return Collections.emptyList(); @@ -155,9 +154,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase l != method).count()) - : -1; + int ordinal = lambdas.indexOf(method); res.add(new ConditionalReturnJavaBreakpointVariant(position, condRet, ordinal)); //conditional return } @@ -166,47 +163,65 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase { - @Nullable PsiKeyword conditionalReturn = null; + + class RetFinder implements Processor { + // Our ultimate goal is to find the single return statement which is executed conditionally to break on it. + // However, in most cases it's enough to just check that return is not the first on the line + // (code like `workHard(); return result();` is quite rare, + // also note that putting breakpoint on such return would not lead to catastrophic circumstances). + // We ignore multiple returns because it's not easy to map them in source code to multiple return instructions in bytecode. + + boolean somethingBeforeReturn = false; + @Nullable PsiElement singleReturn = null; @Override public boolean process(PsiElement element) { - if (element instanceof PsiKeyword retKeyword && element.getText().equals(PsiKeyword.RETURN)) { - if (conditionalReturn != null) { - // it's not easy to map multiple returns in source code to multiple returns in bytecode - conditionalReturn = null; + if (isReturnKeyword(element)) { + if (singleReturn != null) { + singleReturn = null; return false; } - PsiElement cur = element; - while (cur != null && cur.getTextOffset() >= curLineRange.getStartOffset()) { - PsiElement parent = cur.getParent(); - if (parent instanceof PsiIfStatement ifStmt && - (cur == ifStmt.getThenBranch() || cur == ifStmt.getElseBranch())) { - conditionalReturn = retKeyword; - } - - cur = parent; + if (!somethingBeforeReturn) { + assert singleReturn == null; + return false; } + + singleReturn = element; + } + + if (!(element instanceof PsiWhiteSpace || element instanceof PsiComment)) { + somethingBeforeReturn = true; } return true; } } - CondRetFinder finder = new CondRetFinder(); + RetFinder finder = new RetFinder(); new XDebuggerUtilImpl().iterateLine(project, document, line, finder); - return finder.conditionalReturn; + return finder.singleReturn; + } + + public static boolean isReturnKeyword(@NotNull PsiElement element) { + // Don't check for PsiKeyword to cover many languages at once. + return element instanceof LeafElement && element.getText().equals("return"); } public boolean matchesPosition(@NotNull LineBreakpoint breakpoint, @NotNull SourcePosition position) { @@ -238,7 +253,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase collectInlineConditionalReturnLocations(@NotNull Method method, int lineNumber) { + protected static Stream collectInlineConditionalReturnLocations(@NotNull Method method, int lineNumber) { assert lineNumber > 0; class Visitor extends MethodVisitor implements MethodBytecodeUtil.InstructionOffsetReader { final SmartList returnOffsets = new SmartList<>(); @@ -288,9 +303,15 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase= 0 + ? JavaDebuggerBundle.message("breakpoint.variant.text.line.and.lambda", lambdaCount) + : JavaDebuggerBundle.message("breakpoint.variant.text.line.and.lambda.uknown.count"); } } @@ -299,7 +320,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase { SourcePosition linePosition = createLineSourcePosition((XLineBreakpointImpl)breakpoint); if (linePosition != null) { - if (condRet) { - // Ignore lambda ordinal, return element should be unique on the line. - return XSourcePositionImpl.createByElement(findSingleConditionalReturn(linePosition)); + PsiElement theReturn = condRet ? findSingleConditionalReturn(linePosition) : null; + if (theReturn != null) { + return XSourcePositionImpl.createByElement(theReturn); } else { return DebuggerUtilsEx.toXSourcePosition(new PositionManagerImpl.JavaSourcePosition(linePosition, lambdaOrdinal)); } diff --git a/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java b/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java index 667e61a1b525..6501245c1ec2 100644 --- a/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java +++ b/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java @@ -4,20 +4,27 @@ package org.jetbrains.java.debugger.breakpoints.properties; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.util.xmlb.annotations.Transient; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class JavaLineBreakpointProperties extends JavaBreakpointProperties { // TODO: rework encoding of inline position, introduce enum/class-based external API - // null - stop at all positions on the line + // null - stop at line and all lambdas // -1 - stop only at the base position (first on the line) // 0 or more - index of the lambda on the line to stop at // -10 or less - stop only at single conditional return statement (-10 at the base method, (-10-i) at the i-th lambda) - private Integer encodedInlinePosition = null; + private @Nullable Integer encodedInlinePosition = null; - public static final int COND_RET_CODE = -10; + private static final int COND_RET_CODE = -10; + + public static int encodeInlinePosition(int lambdaOrdinal, boolean conditionalReturn) { + return !conditionalReturn + ? lambdaOrdinal + : COND_RET_CODE - lambdaOrdinal - 1; + } @Transient - public Integer getLambdaOrdinal() { + public @Nullable Integer getLambdaOrdinal() { if (encodedInlinePosition == null) { return null; } @@ -30,11 +37,11 @@ public class JavaLineBreakpointProperties extends JavaBreakpointProperties { breakpoint = breakpointManager.addLineBreakpoint(document, commentLine + 1, p -> { - // Note that we don't support `return` inside of lambda in unit tests. - p.setEncodedInlinePosition(JavaLineBreakpointProperties.COND_RET_CODE); + int lambdaOrdinal = -1; // Note that we don't support `return` inside of lambda in unit tests. + p.setEncodedInlinePosition(JavaLineBreakpointProperties.encodeInlinePosition(lambdaOrdinal, true)); }); if (breakpoint != null) { systemPrintln("ConditionalReturnBreakpoint created at " + breakpointLocation); diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinPositionManager.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinPositionManager.kt index 2773eb8d8f02..90c1c53ff086 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinPositionManager.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinPositionManager.kt @@ -9,6 +9,7 @@ import com.intellij.debugger.SourcePosition import com.intellij.debugger.engine.DebugProcess import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebuggerUtils.isSynthetic +import com.intellij.debugger.engine.PositionManagerImpl import com.intellij.debugger.engine.PositionManagerWithMultipleStackFrames import com.intellij.debugger.engine.evaluation.EvaluationContext import com.intellij.debugger.impl.DebuggerUtilsAsync @@ -149,6 +150,10 @@ class KotlinPositionManager(private val debugProcess: DebugProcess) : MultiReque throw NoDataException.INSTANCE } + PositionManagerImpl.adjustPositionForConditionalReturn(debugProcess, location, psiFile, sourceLineNumber)?.let { + return it + } + val sourcePosition = createSourcePosition(location, psiFile, sourceLineNumber) ?: SourcePosition.createFromLine(psiFile, sourceLineNumber) @@ -177,7 +182,8 @@ class KotlinPositionManager(private val debugProcess: DebugProcess) : MultiReque if (!location.hasVisibleInlineLambdasOnLines(lines)) { return KotlinSourcePositionWithEntireLineHighlighted(sourcePosition) } - return sourcePosition + + return sourcePosition } private fun createSourcePosition(location: Location, file: KtFile, sourceLineNumber: Int): SourcePosition? { diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinSourcePositionHighlighter.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinSourcePositionHighlighter.kt index e0db2d1eec19..dff247654051 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinSourcePositionHighlighter.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinSourcePositionHighlighter.kt @@ -4,6 +4,7 @@ package org.jetbrains.kotlin.idea.debugger.core import com.intellij.debugger.SourcePosition import com.intellij.debugger.engine.SourcePositionHighlighter +import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType import com.intellij.openapi.util.TextRange import com.intellij.psi.util.parentOfType import org.jetbrains.kotlin.idea.base.psi.isOneLiner @@ -13,15 +14,26 @@ import org.jetbrains.kotlin.psi.KtFunctionLiteral class KotlinSourcePositionHighlighter : SourcePositionHighlighter() { override fun getHighlightRange(sourcePosition: SourcePosition?): TextRange? { - if (sourcePosition is KotlinSourcePositionWithEntireLineHighlighted || + if (sourcePosition == null || + sourcePosition is KotlinSourcePositionWithEntireLineHighlighted || sourcePosition is KotlinReentrantSourcePosition) { return null } - val lambda = sourcePosition?.elementAt?.parentOfType() + val element = sourcePosition.elementAt ?: return null + + // Highlight only return keyword in case of conditional return breakpoint. + if (JavaLineBreakpointType.isReturnKeyword(element) && + element === JavaLineBreakpointType.findSingleConditionalReturn(sourcePosition)) { + return element.textRange + } + + // Highlight only lambda body in case of lambda breakpoint. + val lambda = element.parentOfType() if (lambda != null && lambda.isOneLiner()) { return lambda.textRange } + return null } } diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/breakpoints/KotlinLineBreakpointType.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/breakpoints/KotlinLineBreakpointType.kt index 67fcdd381c6b..80137450ad93 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/breakpoints/KotlinLineBreakpointType.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/breakpoints/KotlinLineBreakpointType.kt @@ -107,7 +107,9 @@ class KotlinLineBreakpointType : val pos = SourcePosition.createFromLine(file, position.line) val lambdas = getLambdasAtLineIfAny(pos) - if (lambdas.isEmpty()) return emptyList() + val condRet = findSingleConditionalReturn(file, position.line) + + if (lambdas.isEmpty() && condRet == null) return emptyList() val result = LinkedList() val elementAt = pos.elementAt.parentsWithSelf.firstIsInstance() @@ -135,20 +137,29 @@ class KotlinLineBreakpointType : result.add(KotlinBreakpointVariant(position, lambdas.size)) } + if (condRet != null) { + val method = condRet.getContainingMethod() + val ordinal = lambdas.indexOf(method) + result.add(ConditionalReturnJavaBreakpointVariant(position, condRet, ordinal)) + } + return result } override fun getHighlightRange(breakpoint: XLineBreakpoint): TextRange? { val properties = breakpoint.properties ?: return null - val ordinal = properties.lambdaOrdinal ?: return null - val javaBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint) as? LineBreakpoint<*> ?: return null - val position = javaBreakpoint.sourcePosition ?: return null + val position = (BreakpointManager.getJavaBreakpoint(breakpoint) as? LineBreakpoint<*>)?.sourcePosition ?: return null + if (properties.isConditionalReturn) { + return findSingleConditionalReturn(position)?.textRange + } + + val lambdaOrdinal = properties.lambdaOrdinal ?: return null // Since lambda breakpoints are placed on the first lambda statement, // we should find the function parent to highlight lambda breakpoints properly val function = position.elementAt.parentOfType() ?: return null val updatedPosition = SourcePosition.createFromElement(function) ?: return null - return getLambdaByOrdinal(updatedPosition, ordinal)?.textRange + return getLambdaByOrdinal(updatedPosition, lambdaOrdinal)?.textRange } override fun getSourcePosition(breakpoint: XBreakpoint): XSourcePosition? = @@ -157,13 +168,22 @@ class KotlinLineBreakpointType : private fun calculateSourcePosition(breakpoint: XBreakpoint): XSourcePosition? { val javaBreakpointProperties = breakpoint.properties ?: return null val sourcePosition = createLineSourcePosition(breakpoint as XLineBreakpointImpl<*>) ?: return null - val ordinal = javaBreakpointProperties.lambdaOrdinal - val function = getLambdaByOrdinal(sourcePosition, ordinal) ?: return null + + if (javaBreakpointProperties.isConditionalReturn) { + runReadAction { + findSingleConditionalReturn(sourcePosition)?.let { + XSourcePositionImpl.createByElement(it) + } + }?.let { return it } + } + + val lambdaOrdinal = javaBreakpointProperties.lambdaOrdinal ?: return null + val function = getLambdaByOrdinal(sourcePosition, lambdaOrdinal) ?: return null val firstStatement = function.bodyBlockExpression?.statements?.firstOrNull() ?: return null return runReadAction { val linePosition = SourcePosition.createFromElement(firstStatement) ?: return@runReadAction null DebuggerUtilsEx.toXSourcePosition( - PositionManagerImpl.JavaSourcePosition(linePosition, ordinal) + PositionManagerImpl.JavaSourcePosition(linePosition, lambdaOrdinal) ) } } diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/BreakpointHighlightingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/BreakpointHighlightingTestGenerated.java index 669dee557b54..65fa5ac60b12 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/BreakpointHighlightingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/BreakpointHighlightingTestGenerated.java @@ -23,6 +23,16 @@ public class BreakpointHighlightingTestGenerated extends AbstractBreakpointHighl KotlinTestUtils.runTest(this::doCustomTest, this, testDataFilePath); } + @TestMetadata("conditionalReturn.kt") + public void testConditionalReturn() throws Exception { + runTest("testData/highlighting/conditionalReturn.kt"); + } + + @TestMetadata("conditionalReturnInLambda.kt") + public void testConditionalReturnInLambda() throws Exception { + runTest("testData/highlighting/conditionalReturnInLambda.kt"); + } + @TestMetadata("lambdasOnSameLine.kt") public void testLambdasOnSameLine() throws Exception { runTest("testData/highlighting/lambdasOnSameLine.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaBreakpointHighlightingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaBreakpointHighlightingTestGenerated.java index a107ed1cba13..369a5f804ecb 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaBreakpointHighlightingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IndyLambdaBreakpointHighlightingTestGenerated.java @@ -23,6 +23,16 @@ public class IndyLambdaBreakpointHighlightingTestGenerated extends AbstractIndyL KotlinTestUtils.runTest(this::doCustomTest, this, testDataFilePath); } + @TestMetadata("conditionalReturn.kt") + public void testConditionalReturn() throws Exception { + runTest("testData/highlighting/conditionalReturn.kt"); + } + + @TestMetadata("conditionalReturnInLambda.kt") + public void testConditionalReturnInLambda() throws Exception { + runTest("testData/highlighting/conditionalReturnInLambda.kt"); + } + @TestMetadata("lambdasOnSameLine.kt") public void testLambdasOnSameLine() throws Exception { runTest("testData/highlighting/lambdasOnSameLine.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrBreakpointHighlightingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrBreakpointHighlightingTestGenerated.java index e7e53e3a47ac..b32732656c2c 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrBreakpointHighlightingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/IrBreakpointHighlightingTestGenerated.java @@ -23,6 +23,16 @@ public class IrBreakpointHighlightingTestGenerated extends AbstractIrBreakpointH KotlinTestUtils.runTest(this::doCustomTest, this, testDataFilePath); } + @TestMetadata("conditionalReturn.kt") + public void testConditionalReturn() throws Exception { + runTest("testData/highlighting/conditionalReturn.kt"); + } + + @TestMetadata("conditionalReturnInLambda.kt") + public void testConditionalReturnInLambda() throws Exception { + runTest("testData/highlighting/conditionalReturnInLambda.kt"); + } + @TestMetadata("lambdasOnSameLine.kt") public void testLambdasOnSameLine() throws Exception { runTest("testData/highlighting/lambdasOnSameLine.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeBreakpointHighlightingTestGenerated.java b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeBreakpointHighlightingTestGenerated.java index 18754247cbec..f2a68749f132 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeBreakpointHighlightingTestGenerated.java +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/K1IdeK2CodeBreakpointHighlightingTestGenerated.java @@ -23,6 +23,16 @@ public class K1IdeK2CodeBreakpointHighlightingTestGenerated extends AbstractK1Id KotlinTestUtils.runTest(this::doCustomTest, this, testDataFilePath); } + @TestMetadata("conditionalReturn.kt") + public void testConditionalReturn() throws Exception { + runTest("testData/highlighting/conditionalReturn.kt"); + } + + @TestMetadata("conditionalReturnInLambda.kt") + public void testConditionalReturnInLambda() throws Exception { + runTest("testData/highlighting/conditionalReturnInLambda.kt"); + } + @TestMetadata("lambdasOnSameLine.kt") public void testLambdasOnSameLine() throws Exception { runTest("testData/highlighting/lambdasOnSameLine.kt"); diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/util/BreakpointCreator.kt b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/util/BreakpointCreator.kt index 31ce08fe4262..7fcef72e6cfa 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/util/BreakpointCreator.kt +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/util/BreakpointCreator.kt @@ -85,9 +85,10 @@ internal class BreakpointCreator( } } comment.startsWith("//Breakpoint!") -> { - val ordinal = getPropertyFromComment(comment, "lambdaOrdinal")?.toInt() + val lambdaOrdinal = getPropertyFromComment(comment, "lambdaOrdinal")?.toInt() + val conditionalReturn = getPropertyFromComment(comment, "conditionalReturn").toBoolean() val condition = getPropertyFromComment(comment, "condition") - createLineBreakpoint(breakpointManager, file, lineIndex, ordinal, condition) + createLineBreakpoint(breakpointManager, file, lineIndex, lambdaOrdinal, conditionalReturn, condition) } comment.startsWith("//FunctionBreakpoint!") -> { createFunctionBreakpoint(breakpointManager, file, lineIndex, false) @@ -117,7 +118,7 @@ internal class BreakpointCreator( when (kind) { "line" -> createBreakpoint(fileName, lineMarker) { psiFile, lineNumber -> - createLineBreakpoint(breakpointManager, psiFile, lineNumber + 1, ordinal, null) + createLineBreakpoint(breakpointManager, psiFile, lineNumber + 1, ordinal, false, null) } "fun" -> createBreakpoint(fileName, lineMarker) { psiFile, lineNumber -> createFunctionBreakpoint(breakpointManager, psiFile, lineNumber, true) @@ -182,6 +183,7 @@ internal class BreakpointCreator( file: PsiFile, lineIndex: Int, lambdaOrdinal: Int?, + conditionalReturn: Boolean, condition: String? ) { val kotlinLineBreakpointType = findBreakpointType(KotlinLineBreakpointType::class.java) @@ -192,7 +194,8 @@ internal class BreakpointCreator( kotlinLineBreakpointType, lineIndex, file.virtualFile, - updatedLambdaOrdinal + updatedLambdaOrdinal, + conditionalReturn, ) if (javaBreakpoint is LineBreakpoint<*>) { @@ -217,12 +220,15 @@ internal class BreakpointCreator( lineIndex: Int, virtualFile: VirtualFile, lambdaOrdinal: Int? = null, + conditionalReturn: Boolean = false, ): Breakpoint>? { if (!breakpointType.canPutAt(virtualFile, lineIndex, project)) return null val xBreakpoint = runWriteAction { val properties = breakpointType.createBreakpointProperties(virtualFile, lineIndex) if (properties is JavaLineBreakpointProperties) { - properties.encodedInlinePosition = lambdaOrdinal + properties.encodedInlinePosition = + if (lambdaOrdinal == null && !conditionalReturn) null + else JavaLineBreakpointProperties.encodeInlinePosition(lambdaOrdinal ?: -1, conditionalReturn) } breakpointManager.addLineBreakpoint( diff --git a/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturn.kt b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturn.kt new file mode 100644 index 000000000000..12aa2a62168c --- /dev/null +++ b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturn.kt @@ -0,0 +1,14 @@ +package conditionalReturn + +// RESUME: 20 +fun main() { + headAndTail(setOf(1, 2)) + headAndTail(setOf()) +} + +fun headAndTail(s: Set): Pair? { + //Breakpoint!, conditionalReturn = true + val head = s.firstOrNull() ?: return null + val tail = s.last() + return Pair(head, tail) +} diff --git a/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturn.out b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturn.out new file mode 100644 index 000000000000..d4cbeacce648 --- /dev/null +++ b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturn.out @@ -0,0 +1,8 @@ +LineBreakpoint created at conditionalReturn.kt:11 +Run Java +Connected to the target VM +conditionalReturn.kt:11 +Highlight lambda range: return +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturnInLambda.kt b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturnInLambda.kt new file mode 100644 index 000000000000..7d9c9a8caf74 --- /dev/null +++ b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturnInLambda.kt @@ -0,0 +1,15 @@ +package conditionalReturnInLambda + +// RESUME: 20 +fun main() { + for (x in listOf(37, 42)) { + //Breakpoint!, conditionalReturn = true, lambdaOrdinal = 2 + oneTwoOne(x, { println("1") }, { if (it == 37) return@oneTwoOne; println("2") }) + } +} + +fun oneTwoOne(v: Int, one: (Int) -> Unit, two: (Int) -> Unit) { + one(v) + two(v) + one(v) +} diff --git a/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturnInLambda.out b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturnInLambda.out new file mode 100644 index 000000000000..b2a7bae70898 --- /dev/null +++ b/plugins/kotlin/jvm-debugger/test/testData/highlighting/conditionalReturnInLambda.out @@ -0,0 +1,8 @@ +LineBreakpoint created at conditionalReturnInLambda.kt:7 lambdaOrdinal = 2 +Run Java +Connected to the target VM +conditionalReturnInLambda.kt:7 +Highlight lambda range: return +Disconnected from the target VM + +Process finished with exit code 0