diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java index ea062c7f31df..94e3d892d66e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java @@ -404,6 +404,12 @@ public class MethodCallInstruction extends ExpressionPushingInstruction { } else { mutable = Mutability.getMutability(myTargetMethod); } + if (type.hasAnnotation(Mutability.UNMODIFIABLE_ANNOTATION)) { + mutable = Mutability.UNMODIFIABLE; + } + else if (type.hasAnnotation(Mutability.UNMODIFIABLE_VIEW_ANNOTATION)) { + mutable = Mutability.UNMODIFIABLE_VIEW; + } PsiType qualifierType = DfaPsiUtil.dfTypeToPsiType(factory.getProject(), state.getDfType(qualifierValue)); type = narrowReturnType(type, qualifierType, realMethod); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/types/DfTypes.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/types/DfTypes.java index 7e128d424a08..65e08ef54cec 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/types/DfTypes.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/types/DfTypes.java @@ -434,8 +434,15 @@ public final class DfTypes { if (constraint.isSingleton() && nullability == Nullability.NOT_NULL) { return new DfReferenceConstantType(constraint, constraint, false); } + Mutability mutability = Mutability.UNKNOWN; + if (type.hasAnnotation(Mutability.UNMODIFIABLE_ANNOTATION)) { + mutability = Mutability.UNMODIFIABLE; + } + else if (type.hasAnnotation(Mutability.UNMODIFIABLE_VIEW_ANNOTATION)) { + mutability = Mutability.UNMODIFIABLE_VIEW; + } return new DfGenericObjectType(Set.of(), constraint, - DfaNullability.fromNullability(nullability), Mutability.UNKNOWN, null, DfType.BOTTOM, false); + DfaNullability.fromNullability(nullability), mutability, null, DfType.BOTTOM, false); } /** diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/MethodReturnTypeUnmodifiableAnnotation.java b/java/java-tests/testData/inspection/dataFlow/fixture/MethodReturnTypeUnmodifiableAnnotation.java new file mode 100644 index 000000000000..81daa536bde6 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/MethodReturnTypeUnmodifiableAnnotation.java @@ -0,0 +1,16 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.Map; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MethodReturnTypeUnmodifiableAnnotation { + void test() { + Collector> unmodifiableMap = Collectors.toUnmodifiableMap(Integer::parseInt, xx -> xx); + Stream.of("1", "2", "3").collect(unmodifiableMap).put(1, "2"); + var map = Stream.of("1", "2", "3").collect(unmodifiableMap); + map.put(1, "3"); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection11Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection11Test.java index 46debc5c6608..f66c2d75af52 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection11Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection11Test.java @@ -24,4 +24,8 @@ public class DataFlowInspection11Test extends DataFlowInspectionTestCase { public void testAvoidWarningAtNotInferredType() { doTest();} public void testSameArguments() { doTest(); } + + public void testMethodReturnTypeUnmodifiableAnnotation() { + doTest(); + } } \ No newline at end of file