[java] Report even more unnecessary substring() calls (IDEA-345335)

GitOrigin-RevId: 37b86167b5d2b1cac74365eaadbf3a5ddf792f69
This commit is contained in:
Bas Leijdekkers
2024-03-18 00:08:54 +00:00
committed by intellij-monorepo-bot
parent 43bfdf8cf8
commit e3ccfd0e4a
4 changed files with 55 additions and 14 deletions
@@ -61,7 +61,8 @@ public final class RedundantStringOperationInspection extends AbstractBaseJavaLo
private static final CallMatcher STRING_SUBSTRING_TWO_ARG = exactInstanceCall(JAVA_LANG_STRING, "substring").parameterTypes("int", "int");
private static final CallMatcher STRING_BUILDER_SUBSTRING_TWO_ARG = exactInstanceCall(JAVA_LANG_ABSTRACT_STRING_BUILDER, "substring").parameterTypes("int", "int");
private static final CallMatcher STRING_SUBSTRING = anyOf(STRING_SUBSTRING_ONE_ARG, STRING_SUBSTRING_TWO_ARG);
private static final CallMatcher STRING_BUILDER_SUBSTRING = anyOf(STRING_BUILDER_SUBSTRING_ONE_ARG, STRING_BUILDER_SUBSTRING_TWO_ARG);
private static final CallMatcher SUBSTRING = anyOf(STRING_BUILDER_SUBSTRING_ONE_ARG, STRING_BUILDER_SUBSTRING_TWO_ARG,
STRING_SUBSTRING_ONE_ARG, STRING_SUBSTRING_TWO_ARG);
private static final CallMatcher STRING_BUILDER_APPEND =
instanceCall(JAVA_LANG_ABSTRACT_STRING_BUILDER, "append").parameterTypes(JAVA_LANG_STRING);
private static final CallMatcher SINGLE_ARG_STRING_BUILDER_APPEND =
@@ -107,8 +108,8 @@ public final class RedundantStringOperationInspection extends AbstractBaseJavaLo
private static class RedundantStringOperationVisitor extends JavaElementVisitor {
private final CallMapper<ProblemDescriptor> myProcessors = new CallMapper<ProblemDescriptor>()
.register(STRING_TO_STRING, call -> getProblem(call, "inspection.redundant.string.call.message"))
.register(STRING_SUBSTRING, this::getSubstringProblem)
.register(STRING_BUILDER_SUBSTRING, this::getSubstringProblem)
.register(SUBSTRING, this::getSubstringProblem)
.register(SUBSTRING, this::getAppendSubstringProblem)
.register(STRING_BUILDER_APPEND, this::getAppendProblem)
.register(STRING_BUILDER_TO_STRING, this::getStringBuilderToStringProblem)
.register(STRING_INTERN, this::getInternProblem)
@@ -566,6 +567,34 @@ public final class RedundantStringOperationInspection extends AbstractBaseJavaLo
return ExpressionUtils.isLiteral(PsiUtil.skipParenthesizedExprDown(args[0]), "") ? args[0] : null;
}
@Nullable
private ProblemDescriptor getAppendSubstringProblem(PsiMethodCallExpression call) {
PsiExpression[] args = call.getArgumentList().getExpressions();
PsiExpression stringExpression = call.getMethodExpression().getQualifierExpression();
if (args.length == 1) {
if (ExpressionUtils.isZero(args[0]) ||
isLengthOf(args[0], stringExpression) ||
!(PsiUtil.deparenthesizeExpression(stringExpression) instanceof PsiReferenceExpression)) {
return null;
}
}
else if (args.length == 2) {
if (ExpressionUtils.isZero(args[0]) && isLengthOf(args[1], stringExpression) || isBetterWithCharAt(call)) {
return null;
}
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(call.getParent());
if (parent instanceof PsiExpressionList list && list.getExpressionCount() == 1 &&
parent.getParent() instanceof PsiMethodCallExpression parentCall && SINGLE_ARG_STRING_BUILDER_APPEND.test(parentCall)) {
PsiElement nameElement = Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement());
return myManager.createProblemDescriptor(nameElement,
InspectionGadgetsBundle.message("inspection.redundant.string.call.message"),
new RemoveRedundantStringCallFix(nameElement.getText(), FixType.REPLACE_WITH_ARGUMENTS),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly);
}
return null;
}
@Nullable
private ProblemDescriptor getSubstringProblem(PsiMethodCallExpression call) {
PsiExpression[] args = call.getArgumentList().getExpressions();
@@ -602,16 +631,6 @@ public final class RedundantStringOperationInspection extends AbstractBaseJavaLo
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly,
new SubstringToCharAtQuickFix(call.getText(), converted, false));
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(call.getParent());
if (parent instanceof PsiExpressionList list && list.getExpressionCount() == 1 &&
parent.getParent() instanceof PsiMethodCallExpression parentCall && SINGLE_ARG_STRING_BUILDER_APPEND.test(parentCall)) {
PsiElement nameElement = Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement());
return myManager.createProblemDescriptor(nameElement,
InspectionGadgetsBundle.message("inspection.redundant.string.call.message"),
new RemoveRedundantStringCallFix(
nameElement.getText(), FixType.REPLACE_WITH_ARGUMENTS),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly);
}
return null;
}
@@ -952,9 +971,15 @@ public final class RedundantStringOperationInspection extends AbstractBaseJavaLo
case REPLACE_WITH_ARGUMENTS -> {
PsiExpressionList list = tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiExpressionList.class);
if (list == null) return;
for (PsiExpression arg : call.getArgumentList().getExpressions()) {
PsiExpression[] expressions = call.getArgumentList().getExpressions();
for (PsiExpression arg : expressions) {
list.add(ct.markUnchanged(arg));
}
if (expressions.length == 1) {
PsiExpression expression =
JavaPsiFacade.getElementFactory(project).createExpressionFromText(qualifier.getText() + ".length()", call);
list.add(expression);
}
ct.replaceAndRestoreComments(call, qualifier);
}
}
@@ -0,0 +1,7 @@
// "Remove redundant 'substring()' call" "true-preview"
class Foo {
public void x(String arg) {
StringBuilder sb = new StringBuilder();
sb.append(arg, 2, arg.length());
}
}
@@ -0,0 +1,7 @@
// "Remove redundant 'substring()' call" "true-preview"
class Foo {
public void x(String arg) {
StringBuilder sb = new StringBuilder();
sb.append(arg.subs<caret>tring(2));
}
}
@@ -12,6 +12,8 @@ class Substring {
String.valueOf(<warning descr="'new char[]' is redundant">new char[] </warning>{ 'c' });
new <warning descr="Can be replaced with 'String.valueOf()'">String</warning>(new char[] { 'c' });
String s2 = new <warning descr="'new String()' is redundant">String</warning>();
sb.append(message.<warning descr="Call to 'substring()' is redundant">substring</warning>(1, <warning descr="Unnecessary string length argument">message.length()</warning>));
sb.append(message.<warning descr="Call to 'substring()' is redundant">substring</warning>(1));
// incorrect type
if (s.substring(0, 1).equals(1)) {