diff --git a/java/java-impl/src/com/intellij/codeInspection/StringRepeatCanBeUsedInspection.java b/java/java-impl/src/com/intellij/codeInspection/StringRepeatCanBeUsedInspection.java index 219b37f33f6b..3c2de2ee5801 100644 --- a/java/java-impl/src/com/intellij/codeInspection/StringRepeatCanBeUsedInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/StringRepeatCanBeUsedInspection.java @@ -4,7 +4,6 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.ExpressionUtil; import com.intellij.codeInsight.Nullability; import com.intellij.codeInspection.dataFlow.CommonDataflow; -import com.intellij.codeInspection.dataFlow.DfaFactType; import com.intellij.codeInspection.dataFlow.NullabilityUtil; import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet; import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; @@ -105,12 +104,22 @@ public class StringRepeatCanBeUsedInspection extends AbstractBaseJavaLocalInspec CommentTracker ct = new CommentTracker(); String repeatQualifier = getRepeatQualifier(arg, ct); String countText = getCountText(from, to, loop.isIncluding(), ct); - if (myAddMathMax && canBeNegative(from, to)) { + if (myAddMathMax) { countText = CommonClassNames.JAVA_LANG_MATH + ".max(0," + countText + ")"; } String replacement = repeatQualifier + ".repeat(" + countText + ")"; ct.replace(arg, replacement); - ct.replaceAndRestoreComments(statement, call.getParent()); + PsiExpressionStatement result = (PsiExpressionStatement)ct.replaceAndRestoreComments(statement, call.getParent()); + if (myAddMathMax) { + PsiMethodCallExpression appendCall = (PsiMethodCallExpression)result.getExpression(); + PsiMethodCallExpression repeatCall = (PsiMethodCallExpression)appendCall.getArgumentList().getExpressions()[0]; + PsiMethodCallExpression maxCall = (PsiMethodCallExpression)repeatCall.getArgumentList().getExpressions()[0]; + PsiExpression count = maxCall.getArgumentList().getExpressions()[1]; + LongRangeSet range = CommonDataflow.getExpressionRange(count); + if (range != null && !range.isEmpty() && range.min() >= 0) { + maxCall.replace(count); + } + } } @NotNull @@ -137,19 +146,6 @@ public class StringRepeatCanBeUsedInspection extends AbstractBaseJavaLocalInspec return countText; } - private static boolean canBeNegative(PsiExpression from, PsiExpression to) { - boolean canBeNegative = true; - CommonDataflow.DataflowResult dataflow = CommonDataflow.getDataflowResult(from); - if (dataflow != null) { - LongRangeSet fromRange = dataflow.getExpressionFact(from, DfaFactType.RANGE); - LongRangeSet toRange = dataflow.getExpressionFact(to, DfaFactType.RANGE); - if (fromRange != null && !fromRange.isEmpty() && toRange != null && !toRange.isEmpty() && fromRange.max() <= toRange.min()) { - canBeNegative = false; - } - } - return canBeNegative; - } - @NotNull private static String getRepeatQualifier(PsiExpression arg, CommentTracker ct) { if (arg instanceof PsiLiteralExpression && !TypeUtils.isJavaLangString(arg.getType())) { diff --git a/java/java-tests/testData/inspection/stringRepeat/afterRepeatDiffKnown.java b/java/java-tests/testData/inspection/stringRepeat/afterRepeatDiffKnown.java new file mode 100644 index 000000000000..95f209ebf099 --- /dev/null +++ b/java/java-tests/testData/inspection/stringRepeat/afterRepeatDiffKnown.java @@ -0,0 +1,8 @@ +// "Replace with 'String.repeat()'" "true" +class Test { + String testRepeat(String s, StringBuilder sb, int digits) { + if ((s.length() < digits) && (sb.length() > 0)) { + sb.append("0".repeat(digits - s.length())); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringRepeat/afterRepeatField.java b/java/java-tests/testData/inspection/stringRepeat/afterRepeatField.java new file mode 100644 index 000000000000..222faa7be805 --- /dev/null +++ b/java/java-tests/testData/inspection/stringRepeat/afterRepeatField.java @@ -0,0 +1,11 @@ +// "Replace with 'String.repeat()'" "true" +class Test { + public int pendingSpaces; + + String testRepeat(StringBuilder buffer) { + if (pendingSpaces > 0) { + buffer.append(" ".repeat(pendingSpaces)); + pendingSpaces = 0; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringRepeat/beforeRepeatDiffKnown.java b/java/java-tests/testData/inspection/stringRepeat/beforeRepeatDiffKnown.java new file mode 100644 index 000000000000..526b52e0b8bf --- /dev/null +++ b/java/java-tests/testData/inspection/stringRepeat/beforeRepeatDiffKnown.java @@ -0,0 +1,10 @@ +// "Replace with 'String.repeat()'" "true" +class Test { + String testRepeat(String s, StringBuilder sb, int digits) { + if ((s.length() < digits) && (sb.length() > 0)) { + for (int i=s.length(); i < digits; i++) { + sb.append('0'); + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringRepeat/beforeRepeatField.java b/java/java-tests/testData/inspection/stringRepeat/beforeRepeatField.java new file mode 100644 index 000000000000..9da4b5b83f22 --- /dev/null +++ b/java/java-tests/testData/inspection/stringRepeat/beforeRepeatField.java @@ -0,0 +1,12 @@ +// "Replace with 'String.repeat()'" "true" +class Test { + public int pendingSpaces; + + String testRepeat(StringBuilder buffer) { + if (pendingSpaces > 0) { + for (int sp = 0; sp < pendingSpaces; sp++) + buffer.append(' '); + pendingSpaces = 0; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/StringRepeatCanBeUsedInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/StringRepeatCanBeUsedInspectionTest.java index 24167ba5dede..d533f4759024 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/StringRepeatCanBeUsedInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/StringRepeatCanBeUsedInspectionTest.java @@ -4,26 +4,22 @@ package com.intellij.java.codeInspection; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.StringRepeatCanBeUsedInspection; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; import org.jetbrains.annotations.NotNull; -public class StringRepeatCanBeUsedInspectionTest extends LightQuickFixParameterizedTestCase { - @Override - protected LanguageLevel getDefaultLanguageLevel() { - return LanguageLevel.JDK_11; - } +import static com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase.JAVA_11; +public class StringRepeatCanBeUsedInspectionTest extends LightQuickFixParameterizedTestCase { @NotNull @Override protected LocalInspectionTool[] configureLocalInspectionTools() { return new LocalInspectionTool[]{new StringRepeatCanBeUsedInspection()}; } + @NotNull @Override - protected Sdk getProjectJDK() { - return IdeaTestUtil.getMockJdk9(); + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_11; } @Override