From e5b219451551958239f3e5e20366b6ba6649f65c Mon Sep 17 00:00:00 2001 From: Aleksey Dobrynin Date: Mon, 13 Nov 2023 10:34:36 +0100 Subject: [PATCH] [java, inspection] new inspection "while can be do-while" IDEA-250650 GitOrigin-RevId: bd2bd0053500b98a316330b08481018825e9211f --- .../InspectionGadgetsBundle.properties | 3 + .../IntentionPowerPackBundle.properties | 2 - .../src/META-INF/InspectionGadgets.xml | 4 + .../src/META-INF/IntentionPowerPack.xml | 6 - .../WhileCanBeDoWhileInspection.java | 274 ++++++++++++++ ...laceWhileLoopWithDoWhileLoopIntention.java | 79 ----- .../WhileCanBeDoWhile.html | 26 ++ .../InfiniteLoop.java | 10 - .../InfiniteLoop_after.java | 10 - .../NoBraces.java | 11 - .../NoBraces_after.java | 14 - .../Regular.java | 14 - .../Regular_after.java | 16 - .../WhileCanBeDoWhileInspectionTest.java | 335 ++++++++++++++++++ ...WhileLoopWithDoWhileLoopIntentionTest.java | 39 -- 15 files changed, 642 insertions(+), 201 deletions(-) create mode 100644 java/java-impl/src/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspection.java delete mode 100644 java/java-impl/src/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntention.java create mode 100644 java/java-impl/src/inspectionDescriptions/WhileCanBeDoWhile.html delete mode 100644 java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop.java delete mode 100644 java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop_after.java delete mode 100644 java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces.java delete mode 100644 java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces_after.java delete mode 100644 java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular.java delete mode 100644 java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular_after.java create mode 100644 java/java-tests/testSrc/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspectionTest.java delete mode 100644 java/java-tests/testSrc/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntentionTest.java diff --git a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties index a3dac1adb2be..23e67c2b93c1 100644 --- a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties +++ b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties @@ -2522,3 +2522,6 @@ inspection.redundant.embedded.expression.message.literal=Redundant embedded lite inspection.redundant.embedded.expression.message.empty=Redundant empty embedded expression inspection.redundant.embedded.expression.fix.family.name=Inline embedded expression auto.closeable.resource.quickfix.preview=Add method {0} to the list of ignored methods +inspection.while.can.be.replaced.with.do.while.display.name='while' can be replaced with 'do while' +inspection.while.can.be.replaced.with.do.while.message=Replace 'while' with 'do while' +inspection.while.can.be.replaced.with.do.while.family.name=Replace 'while' with 'do while' \ No newline at end of file diff --git a/java/java-impl/resources/messages/IntentionPowerPackBundle.properties b/java/java-impl/resources/messages/IntentionPowerPackBundle.properties index 9527cd7edebf..a255eb3cf9ba 100644 --- a/java/java-impl/resources/messages/IntentionPowerPackBundle.properties +++ b/java/java-impl/resources/messages/IntentionPowerPackBundle.properties @@ -11,8 +11,6 @@ replace.for.each.loop.with.indexed.for.loop.intention.family.name=Replace for-ea replace.for.each.loop.with.optimized.indexed.for.loop.intention.name=Replace for-each loop with optimized indexed 'for' loop replace.for.each.loop.with.optimized.indexed.for.loop.intention.family.name=Replace for-each loop with optimized indexed for loop replace.for.each.loop.with.iterator.for.loop.intention.family.name=Replace for-each loop with iterator for loop -replace.while.loop.with.do.while.loop.intention.name=Replace 'while' loop with 'do while' loop -replace.while.loop.with.do.while.loop.intention.family.name=Replace while loop with do while loop replace.do.while.loop.with.while.loop.intention.name=Replace 'do while' loop with 'while' loop replace.do.while.loop.with.while.loop.intention.family.name=Replace do while loop with while loop #expand.boolean.intention.name=Convert to 'if else' diff --git a/java/java-impl/src/META-INF/InspectionGadgets.xml b/java/java-impl/src/META-INF/InspectionGadgets.xml index 5df611e8cd13..a4afae4fc0e2 100644 --- a/java/java-impl/src/META-INF/InspectionGadgets.xml +++ b/java/java-impl/src/META-INF/InspectionGadgets.xml @@ -776,6 +776,10 @@ key="if.can.be.assertion.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.control.flow.issues" enabledByDefault="true" level="INFORMATION" implementationClass="com.siyeh.ig.asserttoif.IfCanBeAssertionInspection"/> + messages.JavaBundle intention.category.control.flow - - JAVA - com.siyeh.ipp.whileloop.ReplaceWhileLoopWithDoWhileLoopIntention - messages.JavaBundle - intention.category.control.flow - JAVA com.siyeh.ipp.whileloop.ReplaceDoWhileLoopWithWhileLoopIntention diff --git a/java/java-impl/src/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspection.java b/java/java-impl/src/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspection.java new file mode 100644 index 000000000000..984d9cbcc8aa --- /dev/null +++ b/java/java-impl/src/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspection.java @@ -0,0 +1,274 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.siyeh.ig.whileloop; + +import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.modcommand.ModPsiUpdater; +import com.intellij.modcommand.PsiUpdateModCommandQuickFix; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.PsiReplacementUtil; +import com.siyeh.ig.psiutils.BoolUtils; +import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.EquivalenceChecker; +import com.siyeh.ig.psiutils.TrackingEquivalenceChecker; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static com.intellij.psi.JavaTokenType.WHILE_KEYWORD; + +public class WhileCanBeDoWhileInspection extends AbstractBaseJavaLocalInspectionTool { + @Override + public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + @Override + public void visitWhileStatement(@NotNull PsiWhileStatement statement) { + final PsiStatement body = statement.getBody(); + final PsiExpression condition = statement.getCondition(); + if (condition == null) return; + + PsiElement highlightElement = statement.getFirstChild(); + while (highlightElement != null && highlightElement.getNode().getElementType() != WHILE_KEYWORD) { + highlightElement = highlightElement.getNextSibling(); + } + highlightElement = highlightElement != null ? highlightElement : condition; + + final DiffRange duplicateElements; + if (body != null) { + final Block bodyBlock = Block.init(body); + duplicateElements = bodyBlock.getDiffRange(Block.find(statement, bodyBlock.statements.size())); + } + else { + duplicateElements = null; + } + if (duplicateElements != null) { + holder.registerProblem(highlightElement, + InspectionGadgetsBundle.message("inspection.while.can.be.replaced.with.do.while.message"), + duplicateElements.type(), new ReplaceWhileWithDoWhileFix()); + } + else { + holder.registerProblem(highlightElement, + InspectionGadgetsBundle.message("inspection.while.can.be.replaced.with.do.while.message"), + ProblemHighlightType.INFORMATION, new ReplaceWhileWithDoWhileFix()); + } + } + }; + } + + private static class ReplaceWhileWithDoWhileFix extends PsiUpdateModCommandQuickFix { + + @Override + public @NotNull String getFamilyName() { + return InspectionGadgetsBundle.message( + "inspection.while.can.be.replaced.with.do.while.family.name"); + } + + @Override + protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) { + final PsiWhileStatement statement = (PsiWhileStatement)element.getParent(); + if (statement == null) return; + + final PsiStatement body = statement.getBody(); + final PsiExpression condition = statement.getCondition(); + final boolean infiniteLoop = BoolUtils.isTrue(condition); + + final DiffRange duplicateElements; + if (body != null) { + final Block bodyBlock = Block.init(body); + duplicateElements = bodyBlock.getDiffRange(Block.find(statement, bodyBlock.statements.size())); + } + else { + duplicateElements = null; + } + final StringBuilder result = new StringBuilder(); + final CommentTracker tracker = new CommentTracker(); + if (!infiniteLoop && duplicateElements == null) { + result.append("if("); + if (condition != null) { + result.append(tracker.text(condition)); + } + result.append(") {\n"); + } + if (body instanceof PsiBlockStatement blockStatement) { + result.append("do {"); + final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); + final PsiElement[] children = codeBlock.getChildren(); + if (children.length > 2) { + for (int i = 1; i < children.length - 1; i++) { + final PsiElement child = children[i]; + result.append(tracker.text(child)); + } + } + result.append('}'); + } + else if (body != null) { + result.append("do ").append(tracker.text(body)).append('\n'); + } + result.append("while("); + if (condition != null) { + result.append(tracker.text(condition)); + } + result.append(");"); + if (!infiniteLoop && duplicateElements == null) { + result.append("\n}"); + } + + if (duplicateElements != null) { + statement.getParent().deleteChildRange(duplicateElements.first(), duplicateElements.last()); + } + PsiReplacementUtil.replaceStatement(statement, result.toString(), tracker); + } + } + + private record DiffRange(@NotNull PsiElement first, @NotNull PsiElement last, @NotNull ProblemHighlightType type) { + } + + private static class Block { + private @NotNull final List statements; + private @NotNull final List comments; + private final @Nullable PsiStatement block; + + Block(@NotNull PsiBlockStatement block) { + this.block = block; + comments = new ArrayList<>(); + statements = new ArrayList<>(); + for (PsiElement element : block.getCodeBlock().getChildren()) { + if (element instanceof PsiComment comment) { + comments.add(comment); + } + else if (element instanceof PsiStatement statement) { + statements.add(statement); + } + } + } + + Block(@NotNull List statements, @NotNull List comments) { + this.statements = statements; + this.comments = comments; + this.block = null; + } + + static Block init(@NotNull PsiStatement body) { + if (body instanceof PsiBlockStatement block) { + return new Block(block); + } + else { + return new Block(Collections.singletonList(body), Collections.emptyList()); + } + } + + static Block find(@NotNull PsiElement start, int maxSize) { + final List statements = new ArrayList<>(maxSize); + final List comments = new ArrayList<>(); + while (statements.size() < maxSize && (start = start.getPrevSibling()) != null) { + if (start instanceof PsiStatement statement) { + if (statements.isEmpty() && statement instanceof PsiBlockStatement block) { + return new Block(block); + } + else { + statements.add(statement); + } + } + else if (!statements.isEmpty() && start instanceof PsiComment comment) { // ignore comments between while and duplicate code + comments.add(comment); + } + } + Collections.reverse(statements); + Collections.reverse(comments); + return new Block(statements, comments); + } + + @Nullable + DiffRange getDiffRange(@NotNull Block block) { + final EquivalenceChecker checker = new BreakTrackingEquivalenceChecker(); + if (block.block != null && this.block != null) { + if (checker.statementsAreEquivalent(block.block, this.block)) { + return new DiffRange(block.block, block.block, equalsComments(comments, block.comments) + ? ProblemHighlightType.WEAK_WARNING + : ProblemHighlightType.INFORMATION); + } + else { + return null; + } + } + else { + if (statements.size() != block.statements.size()) return null; + for (int i = 0; i < statements.size(); i++) { + if (!checker.statementsAreEquivalent(statements.get(i), block.statements.get(i))) { + return null; + } + } + return new DiffRange(block.statements.get(0), block.statements.get(block.statements.size() - 1), + equalsComments(comments, block.comments) + ? ProblemHighlightType.WEAK_WARNING + : ProblemHighlightType.INFORMATION); + } + } + + private static boolean equalsComments(@NotNull List comments1, @NotNull List comments2) { + if (comments1.size() != comments2.size()) return false; + for (int i = 0; i < comments1.size(); i++) { + if (!Objects.equals(comments1.get(i).getText().trim(), + comments2.get(i).getText().trim())) { + return false; + } + } + return true; + } + + /** + * Expanding the possibilities of comparing break statements to avoid the situation: + *

+     *   while(a) {
+     *     if(foo()) break;
+     *     while(b) {
+     *       if(foo()) break;
+     *     }
+     *   }
+     * 
+ * it isn't equivalent to: + *

+     *   do(a) {
+     *     if(foo()) break;
+     *   }
+     * 
+ */ + private static class BreakTrackingEquivalenceChecker extends TrackingEquivalenceChecker { + @Override + protected Match breakStatementsMatch(@NotNull PsiBreakStatement statement1, @NotNull PsiBreakStatement statement2) { + return !isParentFor(getFirstLoop(statement1), getFirstLoop(statement2)) + ? super.breakStatementsMatch(statement1, statement2) + : EXACT_MISMATCH; + } + + @Nullable + private static PsiElement getFirstLoop(PsiElement element) { + while ((element = element.getParent()) != null) { + if (element instanceof PsiLoopStatement) return element; + } + return element; + } + + private static boolean isParentFor(@Nullable PsiElement element1, @Nullable PsiElement element2) { + if (element1 == null || element2 == null) return false; + if (element1.equals(element2)) return false; + + PsiElement pinedElement1 = element1; + while ((element1 = element1.getParent()) != null) { + if (element1.equals(element2)) return true; + } + while ((element2 = element2.getParent()) != null) { + if (pinedElement1.equals(element2)) return true; + } + return false; + } + } + } +} diff --git a/java/java-impl/src/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntention.java b/java/java-impl/src/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntention.java deleted file mode 100644 index 56932807a83c..000000000000 --- a/java/java-impl/src/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntention.java +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.siyeh.ipp.whileloop; - -import com.intellij.codeInspection.util.IntentionName; -import com.intellij.psi.*; -import com.siyeh.IntentionPowerPackBundle; -import com.siyeh.ig.PsiReplacementUtil; -import com.siyeh.ig.psiutils.BoolUtils; -import com.siyeh.ig.psiutils.CommentTracker; -import com.siyeh.ipp.base.MCIntention; -import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -/** - * @author Bas Leijdekkers - */ -public class ReplaceWhileLoopWithDoWhileLoopIntention extends MCIntention { - - @Override - public @NotNull String getFamilyName() { - return IntentionPowerPackBundle.message("replace.while.loop.with.do.while.loop.intention.family.name"); - } - - @Override - public @IntentionName @NotNull String getTextForElement(@NotNull PsiElement element) { - return IntentionPowerPackBundle.message("replace.while.loop.with.do.while.loop.intention.name"); - } - - @Override - @NotNull - protected PsiElementPredicate getElementPredicate() { - return new WhileLoopPredicate(); - } - - @Override - protected void processIntention(@NotNull PsiElement element) { - final PsiWhileStatement whileStatement = (PsiWhileStatement)element.getParent(); - if (whileStatement == null) { - return; - } - final PsiStatement body = whileStatement.getBody(); - final PsiExpression condition = whileStatement.getCondition(); - final boolean infiniteLoop = BoolUtils.isTrue(condition); - @NonNls final StringBuilder doWhileStatementText = new StringBuilder(); - CommentTracker tracker = new CommentTracker(); - if (!infiniteLoop) { - doWhileStatementText.append("if("); - if (condition != null) { - doWhileStatementText.append(tracker.text(condition)); - } - doWhileStatementText.append(") {\n"); - } - if (body instanceof PsiBlockStatement blockStatement) { - doWhileStatementText.append("do {"); - final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); - final PsiElement[] children = codeBlock.getChildren(); - if (children.length > 2) { - for (int i = 1; i < children.length - 1; i++) { - final PsiElement child = children[i]; - doWhileStatementText.append(tracker.text(child)); - } - } - doWhileStatementText.append('}'); - } - else if (body != null) { - doWhileStatementText.append("do ").append(tracker.text(body)).append('\n'); - } - doWhileStatementText.append("while("); - if (condition != null) { - doWhileStatementText.append(tracker.text(condition)); - } - doWhileStatementText.append(");"); - if (!infiniteLoop) { - doWhileStatementText.append("\n}"); - } - PsiReplacementUtil.replaceStatement(whileStatement, doWhileStatementText.toString(), tracker); - } -} diff --git a/java/java-impl/src/inspectionDescriptions/WhileCanBeDoWhile.html b/java/java-impl/src/inspectionDescriptions/WhileCanBeDoWhile.html new file mode 100644 index 000000000000..d1e145c18990 --- /dev/null +++ b/java/java-impl/src/inspectionDescriptions/WhileCanBeDoWhile.html @@ -0,0 +1,26 @@ + + +

+ Reports while loops that could be more effectively written as do-while loops. + The focus is on cases where the entire body of the while loop is executed before the condition is checked, + leading to potential redundancy in code execution. +

+

Example:

+

+  foo();
+  while (x) {
+      foo();
+  }
+
+

Can be replaced with:

+

+  do {
+    foo();
+  } while (x);
+
+ +

+ New in 2024.1 +

+ + \ No newline at end of file diff --git a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop.java b/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop.java deleted file mode 100644 index ccba421e67b8..000000000000 --- a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.siyeh.ipp.whileloop.replace_while_with_do_while_loop; - -class InfiniteLoop { - - void m() { - while((true)) { - System.out.println(1); - } - } -} \ No newline at end of file diff --git a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop_after.java b/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop_after.java deleted file mode 100644 index efe03a688115..000000000000 --- a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/InfiniteLoop_after.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.siyeh.ipp.whileloop.replace_while_with_do_while_loop; - -class InfiniteLoop { - - void m() { - do { - System.out.println(1); - } while ((true)); - } -} \ No newline at end of file diff --git a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces.java b/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces.java deleted file mode 100644 index 0ac04ecfeb92..000000000000 --- a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces.java +++ /dev/null @@ -1,11 +0,0 @@ -class NoBraces { - void m() { - while//after while - (b(/*inside call*/)) //before body - System.out.println(); - } - - boolean b() { - return true; - } -} \ No newline at end of file diff --git a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces_after.java b/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces_after.java deleted file mode 100644 index 8f18424e79cc..000000000000 --- a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/NoBraces_after.java +++ /dev/null @@ -1,14 +0,0 @@ -class NoBraces { - void m() { - //after while - //before body - if (b(/*inside call*/)) { - do System.out.println(); - while (b(/*inside call*/)); - } - } - - boolean b() { - return true; - } -} \ No newline at end of file diff --git a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular.java b/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular.java deleted file mode 100644 index 3b9ba309560c..000000000000 --- a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.siyeh.ipp.whileloop.replace_while_with_do_while_loop; - -class Regular { - - void m() { - while(b()) { - System.out.println(1); - } - } - - boolean b() { - return true; - } -} \ No newline at end of file diff --git a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular_after.java b/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular_after.java deleted file mode 100644 index ae5b0ffbfdfc..000000000000 --- a/java/java-tests/testData/ipp/com/siyeh/ipp/whileloop/replace_while_with_do_while_loop/Regular_after.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.siyeh.ipp.whileloop.replace_while_with_do_while_loop; - -class Regular { - - void m() { - if (b()) { - do { - System.out.println(1); - } while (b()); - } - } - - boolean b() { - return true; - } -} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspectionTest.java new file mode 100644 index 000000000000..5dd6bf8a7575 --- /dev/null +++ b/java/java-tests/testSrc/com/siyeh/ig/whileloop/WhileCanBeDoWhileInspectionTest.java @@ -0,0 +1,335 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.siyeh.ig.whileloop; + +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.NotNull; + +public class WhileCanBeDoWhileInspectionTest extends LightJavaCodeInsightFixtureTestCase { + public void testReplaceSingleStatement() { + doTest(""" + class DoWhileClass { + void test() { + System.out.println(1); + while(condition()) { + System.out.println(1); + } + } + boolean condition() { return true; } + }""", """ + class DoWhileClass { + void test() { + do { + System.out.println(1); + } while (condition()); + } + boolean condition() { return true; } + }"""); + } + + public void testReplaceSingleStatementWithoutCodeBlock() { + doTest(""" + class DoWhileClass { + void test() { + System.out.println(1); + while(condition()) + System.out.println(1); + } + boolean condition() { return true; } + }""", """ + class DoWhileClass { + void test() { + do System.out.println(1); + while (condition()); + } + boolean condition() { return true; } + }"""); + } + + public void testReplaceCodeBlock() { + doTest(""" + class DoWhileClass { + void test() { + int sum = 0; + { + sum += 1; + print(sum); + int a = 2; + sum = sum * a; + } + while(sum < 100) { + sum += 1; + print(sum); + int b = 2; + sum = sum * b; + } + } + void print(int a) { + System.out.println(a); + } + }""", """ + class DoWhileClass { + void test() { + int sum = 0; + do { + sum += 1; + print(sum); + int b = 2; + sum = sum * b; + } while (sum < 100); + } + void print(int a) { + System.out.println(a); + } + }"""); + } + + public void testReplaceCodeWithComments() { + doTest(""" + class DoWhileClass { + void test() { + int sum = 0; + // comment 0 + sum += 1; + // comment 1 + print(sum); + + int a = 2; + + sum = sum * a; + // comment 2 + while(sum < 100) { + // comment 3 + sum += 1; + print(sum); + int b = 2; + sum = sum * b; + } + } + void print(int a) { + System.out.println(a); + } + }""", """ + class DoWhileClass { + void test() { + int sum = 0; + // comment 0 + // comment 2 + do { + // comment 3 + sum += 1; + print(sum); + int b = 2; + sum = sum * b; + } while (sum < 100); + } + void print(int a) { + System.out.println(a); + } + }"""); + } + + public void testDifferentBreaks() { + doTest(""" + class DoWhileClass { + void test() { + for(int i = 0; i < 10; i++) { + int j = i; + if(condition()) break; + j--; + while(j > 4) { + if(condition()) break; + j--; + } + } + } + boolean condition() { return true; } + }""", """ + class DoWhileClass { + void test() { + for(int i = 0; i < 10; i++) { + int j = i; + if(condition()) break; + j--; + if (j > 4) { + do { + if (condition()) break; + j--; + } while (j > 4); + } + } + } + boolean condition() { return true; } + }"""); + } + + public void testEqBreaks() { + doTest(""" + class DoWhileClass { + void test() { + for(int i = 0; i < 10; i++) { + int j = i; + for(int k = 0; k < 2; k++) if(condition()) break; + j--; + while(j > 4) { + for(int k = 0; k < 2; k++) if(condition()) break; + j--; + } + } + } + boolean condition() { return true; } + }""", """ + class DoWhileClass { + void test() { + for(int i = 0; i < 10; i++) { + int j = i; + do { + for (int k = 0; k < 2; k++) if (condition()) break; + j--; + } while (j > 4); + } + } + boolean condition() { return true; } + }"""); + } + + public void testTrue() { + doTest(""" + class DoWhileClass { + void test() { + int sum = 0; + sum += 1; + System.out.println("sum"); + while(true) { + sum += 1; + System.out.println("sum"); + } + } + }""", """ + class DoWhileClass { + void test() { + int sum = 0; + do { + sum += 1; + System.out.println("sum"); + } while (true); + } + }"""); + } + + public void testInfiniteLoop() { + doTest(""" + class DoWhileClass { + void test() { + while((true)) { + System.out.println(1); + } + } + }""", """ + class DoWhileClass { + void test() { + do { + System.out.println(1); + } while ((true)); + } + }"""); + } + + public void testNoBraces() { + doTest(""" + class DoWhileClass { + void test() { + while//after while + (b(/*inside call*/)) //before body + System.out.println(); + } + boolean b() { return true; } + }""", """ + class DoWhileClass { + void test() { + //after while + //before body + if (b(/*inside call*/)) { + do System.out.println(); + while (b(/*inside call*/)); + } + } + boolean b() { return true; } + }"""); + } + + public void testRegular() { + doTest(""" + class DoWhileClass { + void test() { + while(b()) { + System.out.println(1); + }; + } + boolean b() { return true; } + }""", """ + class DoWhileClass { + void test() { + if (b()) { + do { + System.out.println(1); + } while (b()); + } + ; + } + boolean b() { return true; } + }"""); + } + + + public void testDifferentComments() { + testHighlighting(""" + class DoWhileClass { + void test() { + int sum = 0; + sum += 1; + // comment 1 + System.out.println("sum"); + while(true) { + sum += 1; + // comment 2 + System.out.println("sum"); + } + } + }"""); + } + + public void testEqComments() { + testHighlighting(""" + class DoWhileClass { + void test() { + int sum = 0; + sum += 1; + // comment + System.out.println("sum"); + while(true) { + sum += 1; + // comment + System.out.println("sum"); + } + } + }"""); + } + + + private void doTest(@NotNull @Language("Java") String before, @NotNull @Language("Java") String after) { + myFixture.configureByText("DoWhile.java", before); + + myFixture.enableInspections(new WhileCanBeDoWhileInspection()); + myFixture.launchAction(myFixture.findSingleIntention("Replace 'while' with 'do while'")); + + myFixture.checkResult(after); + } + + private void testHighlighting(@NotNull @Language("Java") String code) { + myFixture.configureByText("DoWhile.java", code); + + myFixture.enableInspections(new WhileCanBeDoWhileInspection()); + myFixture.testHighlighting(true, true, true); + } +} diff --git a/java/java-tests/testSrc/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntentionTest.java b/java/java-tests/testSrc/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntentionTest.java deleted file mode 100644 index 106b56ad93ac..000000000000 --- a/java/java-tests/testSrc/com/siyeh/ipp/whileloop/ReplaceWhileLoopWithDoWhileLoopIntentionTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ipp.whileloop; - -import com.siyeh.IntentionPowerPackBundle; -import com.siyeh.ipp.IPPTestCase; - -/** - * @author Bas Leijdekkers - */ -public class ReplaceWhileLoopWithDoWhileLoopIntentionTest extends IPPTestCase { - - public void testInfiniteLoop() { doTest(); } - public void testRegular() { doTest(); } - public void testNoBraces() { doTest(); } - - @Override - protected String getRelativePath() { - return "whileloop/replace_while_with_do_while_loop"; - } - - @Override - protected String getIntentionName() { - return IntentionPowerPackBundle.message("replace.while.loop.with.do.while.loop.intention.name"); - } -}