StringRepeatCanBeUsedInspection: use dataflow after Math.max replacement is performed to cover more cases

This commit is contained in:
Tagir Valeev
2019-03-15 11:40:11 +07:00
parent 62bbfeaa3c
commit 2c67b41839
6 changed files with 59 additions and 26 deletions
@@ -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())) {
@@ -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()));
}
}
}
@@ -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;
}
}
}
@@ -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)) {
f<caret>or (int i=s.length(); i < digits; i++) {
sb.append('0');
}
}
}
}
@@ -0,0 +1,12 @@
// "Replace with 'String.repeat()'" "true"
class Test {
public int pendingSpaces;
String testRepeat(StringBuilder buffer) {
if (pendingSpaces > 0) {
fo<caret>r (int sp = 0; sp < pendingSpaces; sp++)
buffer.append(' ');
pendingSpaces = 0;
}
}
}
@@ -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