mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[java, inspection, memory] IDEA-270638 add handling for immutable collections in static collection inspection
GitOrigin-RevId: 97168a2d412b9f3227f5f76086e110f9c0f66311
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9cb03803e1
commit
a367eaef63
+19
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
@@ -230,6 +230,15 @@ public final class CustomMethodHandlers {
|
||||
"newConcurrentHashMap", "newTreeMap").parameterCount(0)),
|
||||
toValue((arguments, state, factory, method) -> COLLECTION_SIZE.asDfType(intValue(0)).meet(LOCAL_OBJECT)));
|
||||
|
||||
private static final CallMatcher IMMUTABLE_CALLS = anyOf(
|
||||
staticCall(JAVA_UTIL_COLLECTIONS, "emptyList", "emptySet", "emptyMap").parameterCount(0),
|
||||
staticCall(JAVA_UTIL_COLLECTIONS, "singleton", "singletonList", "singletonMap"),
|
||||
staticCall(JAVA_UTIL_LIST, "of", "copyOf"),
|
||||
staticCall(JAVA_UTIL_SET, "of", "copyOf"),
|
||||
staticCall(JAVA_UTIL_MAP, "of", "ofEntries", "copyOf"),
|
||||
staticCall(JAVA_UTIL_ARRAYS, "asList")
|
||||
);
|
||||
|
||||
public static CustomMethodHandler find(PsiMethod method) {
|
||||
CustomMethodHandler handler = null;
|
||||
if (isConstantCall(method)) {
|
||||
@@ -248,6 +257,15 @@ public final class CustomMethodHandlers {
|
||||
return CONSTANT_CALLS.methodMatches(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param method method to check
|
||||
* @return true if the method returns an immutable result, false otherwise
|
||||
*/
|
||||
@Contract("null -> false")
|
||||
public static boolean isImmutableCall(PsiMethod method) {
|
||||
return IMMUTABLE_CALLS.methodMatches(method);
|
||||
}
|
||||
|
||||
private static @NotNull DfType handleConstantCall(DfaCallArguments arguments, DfaMemoryState state, PsiMethod method) {
|
||||
PsiType returnType = method.getReturnType();
|
||||
if (returnType == null) return DfType.TOP;
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
package com.siyeh.ig.memory;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.CustomMethodHandlers;
|
||||
import com.intellij.codeInspection.options.OptPane;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
@@ -67,6 +70,13 @@ public final class StaticCollectionInspection extends BaseInspection {
|
||||
if (m_ignoreWeakCollections && CollectionUtils.isWeakCollectionClass(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore final fields initialized with immutable, fixed-size collections
|
||||
if (field.hasModifierProperty(PsiModifier.FINAL) &&
|
||||
PsiUtil.skipParenthesizedExprDown(field.getInitializer()) instanceof PsiMethodCallExpression call &&
|
||||
CustomMethodHandlers.isImmutableCall(call.resolveMethod())){
|
||||
return;
|
||||
}
|
||||
registerFieldError(field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@ therefore prohibited by some coding standards.
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>
|
||||
The inspection ignores <code>final</code> fields initialized with immutable, fixed-size collections
|
||||
(e.g., <code>Map.of()</code>, <code>List.of()</code>, <code>Collections.emptyList()</code>, <code>Collections.singletonMap()</code>).
|
||||
These collections cannot grow and therefore cannot cause memory leaks.
|
||||
</p>
|
||||
<!-- tooltip end -->
|
||||
<p>
|
||||
Configure the inspection:
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import java.util.*;
|
||||
|
||||
public class StaticCollectionInspection {
|
||||
// with warning
|
||||
private static final Map <warning descr="Static collection 's_map1'">s_map1</warning> = new HashMap(10);
|
||||
private static final List <warning descr="Static collection 's_list2'">s_list2</warning> = new ArrayList(10);
|
||||
private static final Set <warning descr="Static collection 's_set3'">s_set3</warning> = new HashSet<>();
|
||||
private static /* final */ Map <warning descr="Static collection 's_map4'">s_map4</warning> = Map.of("key", "value");
|
||||
private static /* final */ List <warning descr="Static collection 's_list5'">s_list5</warning> = List.of("a", "b");
|
||||
|
||||
private static final Map <warning descr="Static collection 's_map6'">s_map6</warning>;
|
||||
static {
|
||||
s_map6 = new HashMap<>();
|
||||
}
|
||||
|
||||
// without warning
|
||||
private static final Map<String, String> s_map7 = Map.of("a", "A", "b", "B", "c", "C");
|
||||
private static final List<String> s_list8 = List.of("a", "b", "c");
|
||||
private static final Set<Integer> s_set9 = Set.of(1, 2, 3);
|
||||
private static final Map<String, String> s_map10 = Map.copyOf(Collections.emptyMap());
|
||||
private static final List<String> s_list11 = List.copyOf(Collections.emptyList());
|
||||
private static final Set<String> s_set12 = Set.copyOf(Collections.emptySet());
|
||||
private static final List<String> s_list13 = Collections.emptyList();
|
||||
private static final Set<String> s_set14 = Collections.emptySet();
|
||||
private static final Map s_map15 = Collections.emptyMap();
|
||||
private static final List<String> s_list16 = Collections.singletonList("only");
|
||||
private static final Set<String> s_set17 = Collections.singleton("only");
|
||||
private static final Map<String, String> s_map18 = Collections.singletonMap("key", "value");
|
||||
private static final List<String> s_list19 = Arrays.asList("a", "b", "c");
|
||||
private static final Map<String, String> s_map20 = Map.ofEntries(
|
||||
Map.entry("key1", "value1"),
|
||||
Map.entry("key2", "value2")
|
||||
);
|
||||
|
||||
private StaticCollectionInspection() {
|
||||
}
|
||||
}
|
||||
+33
-11
@@ -1,17 +1,39 @@
|
||||
package com.siyeh.igtest.performance;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class StaticCollectionInspection
|
||||
{
|
||||
private static final Map s_map1 = new HashMap(10);
|
||||
private static final List s_map2 = new ArrayList(10);
|
||||
public class StaticCollectionInspection {
|
||||
// with warning
|
||||
private static final Map s_map1 = new HashMap(10);
|
||||
private static final List s_list2 = new ArrayList(10);
|
||||
private static final Set s_set3 = new HashSet<>();
|
||||
private static /* final */ Map s_map4 = Map.of("key", "value");
|
||||
private static /* final */ List s_list5 = List.of("a", "b");
|
||||
|
||||
private StaticCollectionInspection()
|
||||
{
|
||||
}
|
||||
private static final Map s_map6;
|
||||
static {
|
||||
s_map6 = new HashMap<>();
|
||||
}
|
||||
|
||||
// without warning
|
||||
private static final Map<String, String> s_map7 = Map.of("a", "A", "b", "B", "c", "C");
|
||||
private static final List<String> s_list8 = List.of("a", "b", "c");
|
||||
private static final Set<Integer> s_set9 = Set.of(1, 2, 3);
|
||||
private static final Map<String, String> s_map10 = Map.copyOf(Collections.emptyMap());
|
||||
private static final List<String> s_list11 = List.copyOf(Collections.emptyList());
|
||||
private static final Set<String> s_set12 = Set.copyOf(Collections.emptySet());
|
||||
private static final List<String> s_list13 = Collections.emptyList();
|
||||
private static final Set<String> s_set14 = Collections.emptySet();
|
||||
private static final Map s_map15 = Collections.emptyMap();
|
||||
private static final List<String> s_list16 = Collections.singletonList("only");
|
||||
private static final Set<String> s_set17 = Collections.singleton("only");
|
||||
private static final Map<String, String> s_map18 = Collections.singletonMap("key", "value");
|
||||
private static final List<String> s_list19 = Arrays.asList("a", "b", "c");
|
||||
private static final Map<String, String> s_map20 = Map.ofEntries(
|
||||
Map.entry("key1", "value1"),
|
||||
Map.entry("key2", "value2")
|
||||
);
|
||||
|
||||
private StaticCollectionInspection() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.memory;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.siyeh.ig.LightJavaInspectionTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class StaticCollectionInspectionTest extends LightJavaInspectionTestCase {
|
||||
public void testStaticCollectionInspection() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_11;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new StaticCollectionInspection();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user