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");
- }
-}