From 0ded7bc2f5df6e26790f002b27764a0fbd6309df Mon Sep 17 00:00:00 2001 From: pyltsin-m Date: Mon, 6 Dec 2021 15:49:15 +0700 Subject: [PATCH] [java-inspections] IDEA-284073 Enhance dfa: Support that Stream.toList() is unmodifiable PR#1861 GitOrigin-RevId: ddddc2451708df8e11d00e059eafdb8674fa562b --- .../codeInspection/dataFlow/Mutability.java | 5 +++ .../java/inliner/StreamChainInliner.java | 4 ++ .../dataFlow/fixture/MutabilityJdk16.java | 41 +++++++++++++++++++ .../DataFlowInspection16Test.java | 2 + 4 files changed, 52 insertions(+) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.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 4046ccc824d1..b9041c4a3238 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 @@ -12,6 +12,7 @@ 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; @@ -64,6 +65,8 @@ public enum Mutability { 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; @@ -187,6 +190,8 @@ public enum Mutability { 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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/StreamChainInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/StreamChainInliner.java index 4a72a0afd3ba..37c5acff245d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/StreamChainInliner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/StreamChainInliner.java @@ -28,6 +28,7 @@ import com.intellij.codeInspection.dataFlow.types.DfType; import com.intellij.codeInspection.dataFlow.types.DfTypes; import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; import com.intellij.codeInspection.dataFlow.value.RelationType; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiUtil; @@ -69,6 +70,8 @@ public class StreamChainInliner implements CallInliner { instanceCall(JAVA_UTIL_STREAM_STREAM, "reduce").parameterTypes("T", "java.util.function.BinaryOperator"); private static final CallMatcher COLLECT_TERMINAL = instanceCall(JAVA_UTIL_STREAM_STREAM, "collect").parameterTypes("java.util.stream.Collector"); + private static final CallMatcher TO_LIST_TERMINAL = instanceCall(JAVA_UTIL_STREAM_STREAM, "toList") + .withLanguageLevelAtLeast(LanguageLevel.JDK_16); private static final CallMatcher COLLECT3_TERMINAL = instanceCall(JAVA_UTIL_STREAM_STREAM, "collect").parameterTypes("java.util.function.Supplier", "java.util.function.BiConsumer", "java.util.function.BiConsumer"); @@ -142,6 +145,7 @@ public class StreamChainInliner implements CallInliner { .register(TO_ARRAY_TERMINAL, call -> new ToArrayStep(call)) .register(COLLECT3_TERMINAL, call -> new Collect3TerminalStep(call)) .register(COLLECT_TERMINAL, call -> createTerminalFromCollector(call)) + .register(TO_LIST_TERMINAL, call -> new ToCollectionStep(call, null, true)) .register(TWO_ARG_REDUCE, call -> new TwoArgReduceStep(call)); private static final Step NULL_TERMINAL_STEP = new Step(null, null, null) { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java b/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java new file mode 100644 index 000000000000..b32be0cd1c94 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/MutabilityJdk16.java @@ -0,0 +1,41 @@ +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/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java index 9ef2c01a8fc8..ae0c9a35dc36 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection16Test.java @@ -39,4 +39,6 @@ public class DataFlowInspection16Test extends DataFlowInspectionTestCase { doTest(); } public void testStaticFieldInAnonymous() { doTest(); } + + public void testMutabilityJdk16() { doTest(); } } \ No newline at end of file