[java-inspections] SequencedCollectionMethodCanBeUsedInspection should ignore named constants

#IDEA-367961 fixed


Merge-request: IJ-MR-155974
Merged-by: Bartek Pacia <bartek.pacia@jetbrains.com>

GitOrigin-RevId: aa16bf0758ef523f21c0eeaf73e27e418fc0cd89
This commit is contained in:
Bartek Pacia
2025-02-25 12:59:44 +00:00
committed by intellij-monorepo-bot
parent 15d703aa85
commit bb7efcadba
4 changed files with 44 additions and 3 deletions
@@ -1244,6 +1244,7 @@ negated.if.else.ignore.negated.zero.option=Ignore '!= 0' comparisons
negated.if.else.invert.quickfix=Invert 'if' condition
overly.complex.boolean.expression.max.terms.option=Maximum number of terms:
pointless.boolean.expression.ignore.option=Ignore named constants in determining pointless expressions
sequenced.collection.method.can.be.used.ignore.option=Ignore named constants used as arguments
simplifiable.conditional.expression.problem.descriptor=<code>{1}</code> can be simplified to ''{0}'' #loc
switch.statement.density.min.option=Minimum density of branches: %
switch.statement.density.problem.descriptor=<code>#ref</code> branch density is too low ({0}%) #loc
@@ -2538,4 +2539,4 @@ inspection.redundant.embedded.expression.fix.family.name=Inline embedded express
auto.closeable.resource.quickfix.preview=Add method <code>{0}</code> to the list of ignored methods
inspection.while.can.be.replaced.with.do.while.display.name='while' can be replaced with 'do while'
inspection.while.can.be.replaced.with.do.while.message=Replace 'while' with 'do while'
inspection.while.can.be.replaced.with.do.while.family.name=Replace 'while' with 'do while'
inspection.while.can.be.replaced.with.do.while.family.name=Replace 'while' with 'do while'
@@ -1,6 +1,7 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
@@ -11,6 +12,7 @@ import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.*;
import one.util.streamex.StreamEx;
@@ -19,6 +21,9 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Set;
import static com.intellij.codeInspection.options.OptPane.checkbox;
import static com.intellij.codeInspection.options.OptPane.pane;
public final class SequencedCollectionMethodCanBeUsedInspection extends AbstractBaseJavaLocalInspectionTool {
private static final CallMatcher LIST_GET_REMOVE = CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_LIST, "get", "remove")
.parameterTypes("int");
@@ -29,6 +34,14 @@ public final class SequencedCollectionMethodCanBeUsedInspection extends Abstract
private static final CallMatcher COLLECTION_ITERATOR = CallMatcher.instanceCall("java.util.Collection", "iterator")
.parameterCount(0);
public boolean m_ignoreExpressionsContainingConstants = true;
@Override
public @NotNull OptPane getOptionsPane() {
return pane(
checkbox("m_ignoreExpressionsContainingConstants", InspectionGadgetsBundle.message("sequenced.collection.method.can.be.used.ignore.option")));
}
@Override
public @NotNull Set<@NotNull JavaFeature> requiredFeatures() {
return Set.of(JavaFeature.SEQUENCED_COLLECTIONS);
@@ -72,11 +85,12 @@ public final class SequencedCollectionMethodCanBeUsedInspection extends Abstract
PsiExpression list = PsiUtil.skipParenthesizedExprDown(methodExpr.getQualifierExpression());
if (list == null || list instanceof PsiThisExpression) return;
PsiExpression arg = PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]);
if (m_ignoreExpressionsContainingConstants && isArgConstant(arg)) return;
if (ExpressionUtils.isZero(arg) && !hasDifferentIndexNearby(call)) {
report(call, "addFirst");
}
}
// Do not warn if we have series of calls with different constant indices like {@code list.get(0); list.get(1); ...}
private static boolean hasDifferentIndexNearby(@NotNull PsiMethodCallExpression call) {
PsiMethod method = call.resolveMethod();
@@ -86,7 +100,7 @@ public final class SequencedCollectionMethodCanBeUsedInspection extends Abstract
PsiCodeBlock block = PsiTreeUtil.getParentOfType(call, PsiCodeBlock.class, true, PsiLambdaExpression.class, PsiMember.class);
if (block == null) return false;
PsiStatement[] statements = block.getStatements();
int index = (int) StreamEx.of(statements).indexOf(s -> PsiTreeUtil.isAncestor(s, call, true)).orElse(-1);
int index = (int)StreamEx.of(statements).indexOf(s -> PsiTreeUtil.isAncestor(s, call, true)).orElse(-1);
if (index == -1) return false;
for (int i = Math.max(0, index - 2); i <= Math.min(statements.length - 1, index + 2); i++) {
Integer otherIndex = SyntaxTraverser.psiTraverser(statements[i]).filter(PsiMethodCallExpression.class)
@@ -106,12 +120,24 @@ public final class SequencedCollectionMethodCanBeUsedInspection extends Abstract
return false;
}
private static boolean isArgConstant(PsiExpression arg) {
if (arg instanceof PsiReferenceExpression referenceExpr) {
PsiElement resolvedArg = referenceExpr.resolve();
if (resolvedArg instanceof PsiField field && ExpressionUtils.isConstant(field)) {
return true;
}
}
return false;
}
private void processListGetRemove(@NotNull PsiMethodCallExpression call) {
PsiReferenceExpression methodExpr = call.getMethodExpression();
String name = methodExpr.getReferenceName();
PsiExpression list = PsiUtil.skipParenthesizedExprDown(methodExpr.getQualifierExpression());
if (list == null || list instanceof PsiThisExpression) return;
PsiExpression arg = PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]);
if (m_ignoreExpressionsContainingConstants && isArgConstant(arg)) return;
if (ExpressionUtils.isZero(arg) && !hasDifferentIndexNearby(call)) {
report(call, name + "First");
}
@@ -4,6 +4,8 @@ import java.util.*;
interface Foo extends SequencedCollection<String> {}
public class Test {
private static final int SOME_CONSTANT = 0;
public static void main(Foo foo, String[] args) {
List<String> list = List.of(args);
@@ -14,6 +16,11 @@ public class Test {
var e5 = list.removeLast();
list.remove("e");
list.get(1);
var e6 = list.get(SOME_CONSTANT);
var e7 = list.remove(SOME_CONSTANT);
list.addFirst("hello");
list.add(SOME_CONSTANT, "world");
}
void testAdd(List<String> list) {
@@ -4,6 +4,8 @@ import java.util.*;
interface Foo extends SequencedCollection<String> {}
public class Test {
private static final int SOME_CONSTANT = 0;
public static void main(Foo foo, String[] args) {
List<String> list = List.of(args);
@@ -14,6 +16,11 @@ public class Test {
var e5 = list.remove(list.size() - 1);
list.remove("e");
list.get(1);
var e6 = list.get(SOME_CONSTANT);
var e7 = list.remove(SOME_CONSTANT);
list.add(0, "hello");
list.add(SOME_CONSTANT, "world");
}
void testAdd(List<String> list) {