From eb1143abcfee79a62d834f17d67dc7f82c91f30f Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 30 Jan 2020 13:25:22 +0700 Subject: [PATCH] DfaAssist: support while and do-while loops GitOrigin-RevId: 2061e3ea64f9091060770c5bfa08c70f6921ad54 --- .../engine/dfaassist/DebuggerDfaRunner.java | 4 ++-- .../debugger/engine/dfaassist/DfaAssist.java | 19 +++++++++++++---- .../dataFlow/ControlFlowAnalyzer.java | 21 ++++++++++++++++--- .../src/messages/DebuggerBundle.properties | 2 ++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DebuggerDfaRunner.java b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DebuggerDfaRunner.java index 252b41fd2f77..6a95052c2df7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DebuggerDfaRunner.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DebuggerDfaRunner.java @@ -40,14 +40,14 @@ class DebuggerDfaRunner extends DataFlowRunner { CommonClassNames.JAVA_UTIL_LINKED_LIST, CommonClassNames.JAVA_UTIL_HASH_MAP, "java.util.TreeMap"); - private final @NotNull PsiCodeBlock myBody; + private final @NotNull PsiElement myBody; private final @NotNull PsiStatement myStatement; private final @NotNull Project myProject; private final @Nullable ControlFlow myFlow; private final @Nullable DfaInstructionState myStartingState; private final long myModificationStamp; - DebuggerDfaRunner(@NotNull PsiCodeBlock body, @NotNull PsiStatement statement, @NotNull StackFrame frame) { + DebuggerDfaRunner(@NotNull PsiElement body, @NotNull PsiStatement statement, @NotNull StackFrame frame) { super(body.getProject(), body.getParent() instanceof PsiClassInitializer ? ((PsiClassInitializer)body.getParent()).getContainingClass() : body); myBody = body; myStatement = statement; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssist.java b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssist.java index d35c89d1fb1a..9e03b0c1cc9e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssist.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssist.java @@ -5,6 +5,7 @@ import com.intellij.codeInsight.hints.presentation.MenuOnClickPresentation; import com.intellij.codeInsight.hints.presentation.PresentationFactory; import com.intellij.codeInsight.hints.presentation.PresentationRenderer; import com.intellij.codeInspection.dataFlow.RunnerResult; +import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.SourcePosition; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; @@ -185,7 +186,7 @@ public class DfaAssist implements DebuggerContextListener { if (!locationMatches(element, frame.location())) return null; PsiStatement statement = getAnchorStatement(element); if (statement == null) return null; - PsiCodeBlock body = getCodeBlock(statement); + PsiElement body = getCodeBlock(statement); if (body == null) return null; DebuggerDfaRunner runner = new DebuggerDfaRunner(body, statement, frame); return runner.isValid() ? runner : null; @@ -233,7 +234,10 @@ public class DfaAssist implements DebuggerContextListener { } @Nullable - private static PsiCodeBlock getCodeBlock(@NotNull PsiStatement statement) { + private static PsiElement getCodeBlock(@NotNull PsiStatement statement) { + if (statement instanceof PsiWhileStatement || statement instanceof PsiDoWhileStatement) { + return statement; + } PsiElement e = statement; while (e != null && !(e instanceof PsiClass) && !(e instanceof PsiFileSystemItem)) { e = e.getParent(); @@ -243,9 +247,15 @@ public class DfaAssist implements DebuggerContextListener { // We cannot properly restore context if we started from finally, so let's analyze just finally block parent instanceof PsiTryStatement && ((PsiTryStatement)parent).getFinallyBlock() == e || parent instanceof PsiBlockStatement && parent.getParent() instanceof PsiLoopStatement) { - return (PsiCodeBlock)e; + if (parent.getParent() instanceof PsiDoWhileStatement) { + return parent.getParent(); + } + return e; } } + if (e instanceof PsiDoWhileStatement) { + return e; + } } return null; } @@ -254,7 +264,8 @@ public class DfaAssist implements DebuggerContextListener { private final DebuggerContextImpl myContext; private TurnOffDfaProcessorAction(DebuggerContextImpl context) { - super("Turn Off Dataflow Assist", "Switch off dataflow aided debugging for this session", AllIcons.Actions.Cancel); + super(DebuggerBundle.message("action.TurnOffDfaAssist.text"), + DebuggerBundle.message("action.TurnOffDfaAssist.description"), AllIcons.Actions.Cancel); myContext = context; } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 915c5077d583..126d21e76194 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -61,6 +61,15 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private final Map myExceptionCache; private ExpressionBlockContext myExpressionBlockContext; + /** + * @param valueFactory factory to create values + * @param codeFragment code fragment to analyze: + * normally a PsiCodeBlock or PsiExpression. + * If PsiWhileStatement or PsiDoWhileStatement then only one loop iteration will be analyzed + * (similar to analyzing the loop body, but condition is analyzed as well). + * If PsiClass then class initializers + field initializers will be analyzed + * @param inlining if true inlining is performed for known method calls + */ ControlFlowAnalyzer(final DfaValueFactory valueFactory, @NotNull PsiElement codeFragment, boolean inlining) { myInlining = inlining; myFactory = valueFactory; @@ -429,7 +438,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { @Override public void visitContinueStatement(PsiContinueStatement statement) { startElement(statement); PsiStatement continuedStatement = statement.findContinuedStatement(); - if (continuedStatement instanceof PsiLoopStatement && PsiTreeUtil.isAncestor(myCodeFragment, continuedStatement, false)) { + if (continuedStatement instanceof PsiLoopStatement && PsiTreeUtil.isAncestor(myCodeFragment, continuedStatement, true)) { PsiStatement body = ((PsiLoopStatement)continuedStatement).getBody(); controlTransfer(new InstructionTransfer(getEndOffset(body), getVariablesInside(body)), getTrapsInsideElement(body)); } else { @@ -449,7 +458,11 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if (condition != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); - addInstruction(new ConditionalGotoInstruction(getStartOffset(statement), false, condition)); + if (statement == myCodeFragment) { + addInstruction(new PopInstruction()); + } else { + addInstruction(new ConditionalGotoInstruction(getStartOffset(statement), false, condition)); + } } } @@ -1172,7 +1185,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { body.accept(this); } - addInstruction(new GotoInstruction(getStartOffset(statement))); + if (statement != myCodeFragment) { + addInstruction(new GotoInstruction(getStartOffset(statement))); + } finishElement(statement); } diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index 9d725ea5ed4d..7b5959bc9794 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -536,3 +536,5 @@ action.AnActionButton.text.import=Import action.AnActionButton.description.import=Import action.AnActionButton.text.export=Export action.AnActionButton.description.export=Export +action.TurnOffDfaAssist.text=Turn Off Data Flow Assist +action.TurnOffDfaAssist.description=Switch off data flow aided debugging for this session