mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 06:39:38 +07:00
[java-inspection] SlowListContainsAllInspection (IDEA-274813)
GitOrigin-RevId: 8c8381b699eb83cec89889ad596fc35e81afd6f1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
5215d2a116
commit
020ffc7630
@@ -17,7 +17,6 @@ import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.ParenthesesUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -71,10 +70,17 @@ public class SlowAbstractSetRemoveAllInspection extends AbstractBaseJavaLocalIns
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the set of possible values for the collection size
|
||||
*
|
||||
* @param collection a collection to get the range of possible values for its size
|
||||
* @return the set of possible values for the collection size
|
||||
*/
|
||||
@NotNull
|
||||
private static LongRangeSet getSizeRangeOfCollection(PsiExpression expression) {
|
||||
public static LongRangeSet getSizeRangeOfCollection(PsiExpression collection) {
|
||||
final SpecialField lengthField = SpecialField.COLLECTION_SIZE;
|
||||
final DfType origType = CommonDataflow.getDfType(expression);
|
||||
final DfType origType = CommonDataflow.getDfType(collection);
|
||||
final DfType length = lengthField.getFromQualifier(origType);
|
||||
final DfIntegralType dfType = ObjectUtils.tryCast(length, DfIntegralType.class);
|
||||
if (dfType == null) return LongRangeSet.all();
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2000-2020 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.codeInspection;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.siyeh.ig.callMatcher.CallMatcher.instanceCall;
|
||||
|
||||
public class SlowListContainsAllInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
private static final CallMatcher LIST_CONTAINS_ALL =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_LIST, "containsAll").parameterTypes(CommonClassNames.JAVA_UTIL_COLLECTION);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
super.visitMethodCallExpression(call);
|
||||
if (!LIST_CONTAINS_ALL.test(call)) return;
|
||||
PsiReferenceExpression expression = call.getMethodExpression();
|
||||
final PsiExpression qualifier = ExpressionUtils.getEffectiveQualifier(expression);
|
||||
if (qualifier == null) return;
|
||||
final LongRangeSet listSizeRange = SlowAbstractSetRemoveAllInspection.getSizeRangeOfCollection(qualifier);
|
||||
if (listSizeRange.isEmpty() || listSizeRange.max() <= 1) return;
|
||||
holder.registerProblem(call,
|
||||
JavaBundle.message("inspection.slow.list.contains.all.description"),
|
||||
ProblemHighlightType.WARNING,
|
||||
expression.getTextRangeInParent(),
|
||||
new ReplaceWithHashSetContainsAllFix(qualifier.getText()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class ReplaceWithHashSetContainsAllFix implements LocalQuickFix {
|
||||
private final String myCollectionText;
|
||||
|
||||
ReplaceWithHashSetContainsAllFix(String collectionText) {
|
||||
myCollectionText = collectionText;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return JavaBundle.message("inspection.slow.list.contains.all.fix.name", myCollectionText);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return JavaBundle.message("inspection.slow.list.contains.all.fix.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiMethodCallExpression call = ObjectUtils.tryCast(descriptor.getPsiElement(), PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
final PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return;
|
||||
final CommentTracker ct = new CommentTracker();
|
||||
PsiElement result = ct.replace(qualifier, "new java.util.HashSet<>(" + ct.text(qualifier) + ")");
|
||||
JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1433,6 +1433,12 @@
|
||||
bundle="messages.JavaBundle"
|
||||
key="inspection.slow.abstract.set.remove.all.description"
|
||||
implementationClass="com.intellij.codeInspection.SlowAbstractSetRemoveAllInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="SlowListContainsAll"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.performance.issues" enabledByDefault="true" level="WARNING"
|
||||
bundle="messages.JavaBundle"
|
||||
key="inspection.slow.list.contains.all.description"
|
||||
implementationClass="com.intellij.codeInspection.SlowListContainsAllInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="RedundantUnmodifiable"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.verbose.or.redundant.code.constructs" enabledByDefault="true" level="WARNING"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports calls to <code>containsAll()</code> on <code>java.util.List</code>.
|
||||
<p>
|
||||
The time complexity of this method call is O(n·m). When the list is large, this can be an expensive operation.
|
||||
</p>
|
||||
<p>
|
||||
The quick-fix wraps the list in <code>new java.util.HashSet<>()</code> since the time required to create
|
||||
<code>java.util.HashSet</code> from <code>java.util.List</code> and execute <code>containsAll()</code> on
|
||||
<code>java.util.HashSet</code> is O(n+m).
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code> public boolean check(List<String> list, Collection<String> collection) {
|
||||
// O(n·m) complexity
|
||||
return list.containsAll(collection);
|
||||
}
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code> public boolean check(List<String> list, Collection<String> collection) {
|
||||
// O(n+m) complexity
|
||||
return new HashSet<>(list).containsAll(collection);
|
||||
}
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2022.1</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Wrap 'list' in 'HashSet' constructor" "true"
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
boolean check(List<String> list, Collection<String> collection) {
|
||||
return new HashSet<>(list).containsAll(collection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'list' in 'HashSet' constructor" "true"
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
boolean check(List<String> list, Collection<String> collection) {
|
||||
return list.containsAll<caret>(collection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Wrap 'list' in 'HashSet' constructor" "false"
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
boolean check(List<String> list, Collection<String> collection) {
|
||||
if (!list.isEmpty()) return true;
|
||||
return list.containsAll<caret>(collection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Wrap 'list' in 'HashSet' constructor" "false"
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
boolean check(List<String> list, Collection<String> collection) {
|
||||
if (list.size() > 1) return true;
|
||||
return list.containsAll<caret>(collection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2020 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.codeInspection.SlowListContainsAllInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SlowListContainsAllInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
|
||||
@Override
|
||||
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{
|
||||
new SlowListContainsAllInspection()
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/slowListContainsAll";
|
||||
}
|
||||
}
|
||||
@@ -1328,6 +1328,9 @@ inspection.move.field.assignment.to.initializer.display.name=Field assignment ca
|
||||
inspection.frequently.used.inheritor.inspection.display.name=Class may extend a commonly used base class
|
||||
inspection.slow.abstract.set.remove.all.description=Call to 'set.removeAll(list)' may work slowly
|
||||
inspection.slow.abstract.set.remove.all.fix.family.name=Use 'Set.remove' instead of 'Set.removeAll'
|
||||
inspection.slow.list.contains.all.description=Call to 'list.containsAll(collection)' may have poor performance
|
||||
inspection.slow.list.contains.all.fix.family.name=Wrap in 'HashSet' constructor
|
||||
inspection.slow.list.contains.all.fix.name=Wrap ''{0}'' in ''HashSet'' constructor
|
||||
slice.filter.parse.error.null.filter.not.applicable.for.primitive.type=''null'' filter is not applicable for primitive type {0}
|
||||
slice.filter.parse.error.not.null.filter.not.applicable.for.primitive.type=''!null'' filter is not applicable for primitive type {0}
|
||||
slice.filter.parse.error.enum.constant.not.found=Enum constant not found: {0}
|
||||
|
||||
Reference in New Issue
Block a user