From 73943d0839fdc7364be735ec4953f172db31130b Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 16 Jun 2025 10:27:38 +0200 Subject: [PATCH] [java-dfa] Simplify Mutability computation using external annotations GitOrigin-RevId: 6570f36a8fa94ca14f4341875b5b980c7124641a --- .../codeInspection/dataFlow/Mutability.java | 22 ++-------- .../dataFlow/fixture/MutabilityJdk16.java | 41 ------------------- .../dataFlow/fixture/MutabilityJdk21.java | 17 ++++++++ .../DataFlowInspection16Test.java | 2 - .../DataFlowInspection21Test.java | 3 ++ .../java/util/stream/annotations.xml | 4 ++ 6 files changed, 28 insertions(+), 61 deletions(-) delete mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk21.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/Mutability.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/Mutability.java index 7f8e09a82826..79f78220ef5a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/Mutability.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/Mutability.java @@ -10,7 +10,6 @@ import com.intellij.java.analysis.JavaAnalysisBundle; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ModificationTracker; -import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.light.LightElement; import com.intellij.psi.impl.source.PsiMethodImpl; @@ -20,7 +19,6 @@ import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.containers.ContainerUtil; -import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.ExpressionUtils; import one.util.streamex.StreamEx; @@ -72,12 +70,6 @@ public enum Mutability { public static final @NotNull String UNMODIFIABLE_ANNOTATION = UNMODIFIABLE.myAnnotation; public static final @NotNull String UNMODIFIABLE_VIEW_ANNOTATION = UNMODIFIABLE_VIEW.myAnnotation; - private static final @NotNull CallMatcher STREAM_COLLECT = CallMatcher.instanceCall( - CommonClassNames.JAVA_UTIL_STREAM_STREAM, "collect").parameterTypes("java.util.stream.Collector"); - private static final @NotNull CallMatcher STREAM_TO_LIST = CallMatcher.instanceCall( - CommonClassNames.JAVA_UTIL_STREAM_STREAM, "toList").withLanguageLevelAtLeast(LanguageLevel.JDK_16); - private static final @NotNull CallMatcher UNMODIFIABLE_COLLECTORS = CallMatcher.staticCall( - CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toUnmodifiableList", "toUnmodifiableSet", "toUnmodifiableMap"); private final @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String myResourceKey; private final String myAnnotation; private final Key> myKey; @@ -199,18 +191,12 @@ public enum Mutability { Mutability mutability = UNMODIFIABLE; for (PsiExpression initializer : expressions) { Mutability newMutability = UNKNOWN; - if (ClassUtils.isImmutable(initializer.getType())) { + PsiType type = initializer.getType(); + if (ClassUtils.isImmutable(type) || (type != null && type.hasAnnotation(UNMODIFIABLE_ANNOTATION))) { newMutability = UNMODIFIABLE; } else if (initializer instanceof PsiMethodCallExpression call) { - if (STREAM_COLLECT.test(call)) { - PsiExpression collector = call.getArgumentList().getExpressions()[0]; - newMutability = UNMODIFIABLE_COLLECTORS.matches(collector) ? UNMODIFIABLE : UNKNOWN; - } else if (STREAM_TO_LIST.test(call)) { - newMutability = UNMODIFIABLE; - } else { - PsiMethod method = call.resolveMethod(); - newMutability = method == null ? UNKNOWN : getMutability(method); - } + PsiMethod method = call.resolveMethod(); + newMutability = method == null ? UNKNOWN : getMutability(method); } mutability = mutability.join(newMutability); if (!mutability.isUnmodifiable()) break; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java b/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java deleted file mode 100644 index 65605490ccfe..000000000000 --- a/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java +++ /dev/null @@ -1,41 +0,0 @@ -package java.util.stream; - -import java.util.List; -import java.util.Iterator; -import java.util.Spliterator; - -//mock -class Stream implements BaseStream>{ - public native List toList(); - - public native Iterator iterator(); - - public native Spliterator spliterator(); - - public native boolean isParallel(); - - public native Stream sequential(); - - public native Stream parallel(); - - public native Stream unordered(); - - public native Stream onClose(Runnable var1); - - public native void close(); -} - -public class MutabilityJdk16 { - - private final List list = new Stream().toList(); - - void testFieldList(){ - list.add(4); - } - - void testToList() { - List l = new Stream() - .toList(); - l.add(4); - } -} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk21.java b/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk21.java new file mode 100644 index 000000000000..e03c86f4c7fa --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk21.java @@ -0,0 +1,17 @@ +import java.util.List; +import java.util.stream.*; + +public class MutabilityJdk21 { + + private final List list = Stream.of(1, 2, 3).toList(); + + void testFieldList(){ + list.add(4); + } + + void testToList() { + List l = Stream.of(1, 2, 3) + .toList(); + l.add(4); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java index 38247e85ac1f..ba4e29439e94 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java @@ -40,8 +40,6 @@ public class DataFlowInspection16Test extends DataFlowInspectionTestCase { } public void testStaticFieldInAnonymous() { doTest(); } - public void testMutabilityJdk16() { doTest(); } - public void testAccessorNullityUnderDefaultQualifier() { addCheckerAnnotations(myFixture); doTest(); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java index 9ff0b9a59f3b..0116e6ede2a1 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java @@ -202,4 +202,7 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase { public void testPassthroughGenericParameter() { doTestWith((dfi, cvi) -> dfi.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true); } + + public void testMutabilityJdk21() { doTest(); } + } \ No newline at end of file diff --git a/java/jdkAnnotations/java/util/stream/annotations.xml b/java/jdkAnnotations/java/util/stream/annotations.xml index ab3986c48bb5..fe9761219cb7 100644 --- a/java/jdkAnnotations/java/util/stream/annotations.xml +++ b/java/jdkAnnotations/java/util/stream/annotations.xml @@ -1034,6 +1034,10 @@ + + + +