mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-inspections] IDEA-285742 New inspection: replacement method has no effect
GitOrigin-RevId: 708af15324387475e434e2e0c87fe2d26ceac1ef
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8fab3337b0
commit
b27935c747
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports string methods like <code>replace</code>, <code>replaceAll</code>, <code>replaceFirst</code>
|
||||
applied to the string literal where they have no effect because the search string is not found in the
|
||||
literal. This is redundant code and may indicate a programming error.
|
||||
<p>Example:</p>
|
||||
<pre>
|
||||
"hello".replace("$value$", value); // replacement does nothing
|
||||
</pre>
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2022.1</small></p>
|
||||
</body>
|
||||
</html>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Remove redundant call" "true"
|
||||
class X {
|
||||
String s = "a$b"+ "c";
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Remove redundant call" "true"
|
||||
class X {
|
||||
String s = "a$b"+"c".<caret>replace("$", "/");
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Fix all 'Replacement operation has no effect' problems in file" "false"
|
||||
class X {
|
||||
void test() {
|
||||
"c".<caret>replace("$", "/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
public class ReplaceOnLiteral {
|
||||
private static final String PATTERN = "$a$";
|
||||
|
||||
void test(String s) {
|
||||
"a".<warning descr="Replacement operation has no effect">replace("b", "c")</warning>;
|
||||
"a".<warning descr="Replacement operation has no effect">replace('b', 'c')</warning>;
|
||||
"abc".replace('a', 'b');
|
||||
"abc".<warning descr="Replacement operation has no effect">replaceFirst("^b", "c")</warning>;
|
||||
"abc".replaceFirst("^a", "c");
|
||||
"abc".<warning descr="Replacement operation has no effect">replaceAll("^b", "c")</warning>;
|
||||
"abc".replaceAll("^a", "c");
|
||||
"abc".<warning descr="Replacement operation has no effect">replaceAll(PATTERN, s)</warning>;
|
||||
"abc".<warning descr="Replacement operation has no effect">replace(PATTERN, s)</warning>;
|
||||
|
||||
"a".<warning descr="Replacement operation has no effect">replace("b" + s, "")</warning>;
|
||||
"a".replace("a" + s, "");
|
||||
"a".replace('a' + s, "");
|
||||
"a".<warning descr="Replacement operation has no effect">replace('b' + s, "")</warning>;
|
||||
"a".replace(123 + s, "");
|
||||
"a".<warning descr="Replacement operation has no effect">replace((s + ('x') + s), "c")</warning>;
|
||||
"a".<warning descr="Replacement operation has no effect">replace(PATTERN+s, "")</warning>;
|
||||
|
||||
"a".replaceFirst("x" + s, "");
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import com.siyeh.ig.redundancy.RedundantStringOperationInspection;
|
||||
import com.siyeh.ig.redundancy.ReplaceOnLiteralHasNoEffectInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class ReplaceOnLiteralHasNoEffectInspectionFixTest extends LightQuickFixParameterizedTestCase {
|
||||
@Override
|
||||
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new ReplaceOnLiteralHasNoEffectInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceOnLiteral";
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import com.siyeh.ig.LightJavaInspectionTestCase;
|
||||
import com.siyeh.ig.redundancy.RedundantStringOperationInspection;
|
||||
import com.siyeh.ig.redundancy.ReplaceOnLiteralHasNoEffectInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReplaceOnLiteralHasNoEffectInspectionTest extends LightJavaInspectionTestCase {
|
||||
@Nullable
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new ReplaceOnLiteralHasNoEffectInspection();
|
||||
}
|
||||
|
||||
public void testReplaceOnLiteral() {doTest();}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/replaceOnLiteral/";
|
||||
}
|
||||
}
|
||||
+1
@@ -2192,6 +2192,7 @@ assignment.of.field.with.mutable.type.problem.descriptor=Assignment to {0} field
|
||||
return.of.field.with.mutable.type.problem.descriptor=Return of {0} field <code>{1}</code> #loc
|
||||
ignore.private.methods.option=Ignore assignments in and returns from private methods
|
||||
|
||||
inspection.replace.on.literal.display.name=Replacement operation has no effect
|
||||
inspection.redundant.string.operation.display.name=Redundant 'String' operation
|
||||
inspection.redundant.string.remove.fix.name=Remove redundant ''{0}()'' call
|
||||
inspection.redundant.string.fix.family.name=Remove redundant call
|
||||
|
||||
@@ -2689,6 +2689,9 @@
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="StringOperationCanBeSimplified" bundle="messages.InspectionGadgetsBundle"
|
||||
key="inspection.redundant.string.operation.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" cleanupTool="true" implementationClass="com.siyeh.ig.redundancy.RedundantStringOperationInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="ReplaceOnLiteralHasNoEffect" bundle="messages.InspectionGadgetsBundle"
|
||||
key="inspection.replace.on.literal.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" implementationClass="com.siyeh.ig.redundancy.ReplaceOnLiteralHasNoEffectInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="StringBufferReplaceableByString" bundle="messages.InspectionGadgetsBundle"
|
||||
key="string.buffer.replaceable.by.string.display.name" groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.verbose.or.redundant.code.constructs" enabledByDefault="true" level="WARNING"
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class ReplaceOnLiteralHasNoEffectInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
private static final CallMatcher STRING_REPLACE = CallMatcher.exactInstanceCall(CommonClassNames.JAVA_LANG_STRING,
|
||||
"replace", "replaceFirst", "replaceAll");
|
||||
private static final int MAX_QUALIFIER_LENGTH = 1000;
|
||||
private static final int MAX_PATTERN_LENGTH = 200;
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
|
||||
boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
if (!STRING_REPLACE.test(call)) return;
|
||||
PsiLiteralExpression qualifier = tryCast(
|
||||
PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression()), PsiLiteralExpression.class);
|
||||
if (qualifier == null || qualifier.getTextLength() > MAX_QUALIFIER_LENGTH) return;
|
||||
String str = tryCast(qualifier.getValue(), String.class);
|
||||
PsiExpression pattern = PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]);
|
||||
String name = call.getMethodExpression().getReferenceName();
|
||||
if (!isRedundant(str, pattern, "replace".equals(name))) return;
|
||||
PsiElement refName = Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement());
|
||||
holder.registerProblem(call, InspectionGadgetsBundle.message("inspection.replace.on.literal.display.name"),
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
TextRange.create(refName.getTextRangeInParent().getStartOffset(), call.getTextLength()),
|
||||
ExpressionUtils.isVoidContext(call) ? null : new ReplaceWithQualifierFix());
|
||||
}
|
||||
|
||||
private boolean isRedundant(String str, PsiExpression pattern, boolean literalMatch) {
|
||||
Object constValue = ExpressionUtils.computeConstantExpression(pattern);
|
||||
if (constValue != null) {
|
||||
return isRedundantLiteralMatch(str, constValue, literalMatch);
|
||||
}
|
||||
if (literalMatch && pattern instanceof PsiPolyadicExpression &&
|
||||
((PsiPolyadicExpression)pattern).getOperationTokenType().equals(JavaTokenType.PLUS)) {
|
||||
PsiExpression[] operands = ((PsiPolyadicExpression)pattern).getOperands();
|
||||
boolean stringType = false;
|
||||
for (int i = 0; i < operands.length; i++) {
|
||||
PsiExpression operand = operands[i];
|
||||
stringType = stringType || TypeUtils.isJavaLangString(operand.getType());
|
||||
if (!stringType && (i > 0 || !TypeUtils.isJavaLangString(operands[1].getType()))) continue;
|
||||
constValue = ExpressionUtils.computeConstantExpression(operand);
|
||||
if (constValue == null) continue;
|
||||
if (isRedundantLiteralMatch(str, constValue, true)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isRedundantLiteralMatch(String str, Object value, boolean literalMatch) {
|
||||
String patternValue;
|
||||
if (value instanceof String) {
|
||||
patternValue = (String)value;
|
||||
} else if (value instanceof Character) {
|
||||
patternValue = value.toString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (literalMatch) return !str.contains(patternValue);
|
||||
if (patternValue.length() > MAX_PATTERN_LENGTH) return false;
|
||||
Pattern regex;
|
||||
try {
|
||||
regex = Pattern.compile(patternValue);
|
||||
}
|
||||
catch (PatternSyntaxException e) {
|
||||
return false;
|
||||
}
|
||||
return !regex.matcher(str).find();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class ReplaceWithQualifierFix implements LocalQuickFix {
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("inspection.redundant.string.fix.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project,
|
||||
@NotNull ProblemDescriptor descriptor) {
|
||||
PsiMethodCallExpression call = tryCast(descriptor.getStartElement(), PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return;
|
||||
new CommentTracker().replace(call, qualifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user