diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java
index f0f379513e70..69b4d1365164 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java
@@ -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;
diff --git a/java/java-analysis-impl/src/com/siyeh/ig/memory/StaticCollectionInspection.java b/java/java-analysis-impl/src/com/siyeh/ig/memory/StaticCollectionInspection.java
index 044250a85068..1cdba1585b4c 100644
--- a/java/java-analysis-impl/src/com/siyeh/ig/memory/StaticCollectionInspection.java
+++ b/java/java-analysis-impl/src/com/siyeh/ig/memory/StaticCollectionInspection.java
@@ -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);
}
}
diff --git a/java/java-impl/resources/inspectionDescriptions/StaticCollection.html b/java/java-impl/resources/inspectionDescriptions/StaticCollection.html
index 498b1d1b354c..4015d897a692 100644
--- a/java/java-impl/resources/inspectionDescriptions/StaticCollection.html
+++ b/java/java-impl/resources/inspectionDescriptions/StaticCollection.html
@@ -10,6 +10,11 @@ therefore prohibited by some coding standards.
}
+
+The inspection ignores final fields initialized with immutable, fixed-size collections
+(e.g., Map.of(), List.of(), Collections.emptyList(), Collections.singletonMap()).
+These collections cannot grow and therefore cannot cause memory leaks.
+
Configure the inspection:
diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/memory/static_collection/StaticCollectionInspection.java b/java/java-tests/testData/ig/com/siyeh/igtest/memory/static_collection/StaticCollectionInspection.java
new file mode 100644
index 000000000000..f6b0e252b8d4
--- /dev/null
+++ b/java/java-tests/testData/ig/com/siyeh/igtest/memory/static_collection/StaticCollectionInspection.java
@@ -0,0 +1,37 @@
+import java.util.*;
+
+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 static final Map s_map6;
+ static {
+ s_map6 = new HashMap<>();
+ }
+
+ // without warning
+ private static final Map s_map7 = Map.of("a", "A", "b", "B", "c", "C");
+ private static final List s_list8 = List.of("a", "b", "c");
+ private static final Set s_set9 = Set.of(1, 2, 3);
+ private static final Map s_map10 = Map.copyOf(Collections.emptyMap());
+ private static final List s_list11 = List.copyOf(Collections.emptyList());
+ private static final Set s_set12 = Set.copyOf(Collections.emptySet());
+ private static final List s_list13 = Collections.emptyList();
+ private static final Set s_set14 = Collections.emptySet();
+ private static final Map s_map15 = Collections.emptyMap();
+ private static final List s_list16 = Collections.singletonList("only");
+ private static final Set s_set17 = Collections.singleton("only");
+ private static final Map s_map18 = Collections.singletonMap("key", "value");
+ private static final List s_list19 = Arrays.asList("a", "b", "c");
+ private static final Map s_map20 = Map.ofEntries(
+ Map.entry("key1", "value1"),
+ Map.entry("key2", "value2")
+ );
+
+ private StaticCollectionInspection() {
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/performance/StaticCollectionInspection.java b/java/java-tests/testData/ig/com/siyeh/igtest/performance/StaticCollectionInspection.java
index 939a27711ceb..af90b6f6bd52 100644
--- a/java/java-tests/testData/ig/com/siyeh/igtest/performance/StaticCollectionInspection.java
+++ b/java/java-tests/testData/ig/com/siyeh/igtest/performance/StaticCollectionInspection.java
@@ -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 s_map7 = Map.of("a", "A", "b", "B", "c", "C");
+ private static final List s_list8 = List.of("a", "b", "c");
+ private static final Set s_set9 = Set.of(1, 2, 3);
+ private static final Map s_map10 = Map.copyOf(Collections.emptyMap());
+ private static final List s_list11 = List.copyOf(Collections.emptyList());
+ private static final Set s_set12 = Set.copyOf(Collections.emptySet());
+ private static final List s_list13 = Collections.emptyList();
+ private static final Set s_set14 = Collections.emptySet();
+ private static final Map s_map15 = Collections.emptyMap();
+ private static final List s_list16 = Collections.singletonList("only");
+ private static final Set s_set17 = Collections.singleton("only");
+ private static final Map s_map18 = Collections.singletonMap("key", "value");
+ private static final List s_list19 = Arrays.asList("a", "b", "c");
+ private static final Map s_map20 = Map.ofEntries(
+ Map.entry("key1", "value1"),
+ Map.entry("key2", "value2")
+ );
+
+ private StaticCollectionInspection() {
+ }
}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/siyeh/ig/memory/StaticCollectionInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/memory/StaticCollectionInspectionTest.java
new file mode 100644
index 000000000000..fae981c9b2a3
--- /dev/null
+++ b/java/java-tests/testSrc/com/siyeh/ig/memory/StaticCollectionInspectionTest.java
@@ -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();
+ }
+}