From 0ecc455ce9162b8900d7907096cf2bc3ab868709 Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Wed, 12 Nov 2025 12:00:10 +0100 Subject: [PATCH] [java-inspections] part of IDEA-380832 - support taking into account nullability of type parameter of class GitOrigin-RevId: fb7296c039189816591014e435cc30bdb5118e14 --- .../intellij/psi/impl/PsiSubstitutorImpl.java | 5 +- .../fixture/DelegateClassTypeParameter.java | 63 +++++++++++++++++++ .../DataFlowInspection8Test.java | 6 ++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/DelegateClassTypeParameter.java diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java index 62073456fc82..c4c5ec6c53bd 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java @@ -221,7 +221,10 @@ public final class PsiSubstitutorImpl implements PsiSubstitutor { while (true) { final PsiTypeParameter[] params = resolve.getTypeParameters(); for (final PsiTypeParameter param : params) { - final PsiType original = originalSubstitutor.substitute(param); + PsiType original = originalSubstitutor.substitute(param); + if (original != null && original.getNullability().equals(TypeNullability.UNKNOWN)) { + original = original.withNullability(TypeNullability.ofTypeParameter(param)); + } PsiType mapping = original == null ? null : original.accept(this); substMap = substMap.with(param, mapping); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/DelegateClassTypeParameter.java b/java/java-tests/testData/inspection/dataFlow/fixture/DelegateClassTypeParameter.java new file mode 100644 index 000000000000..e907877552b3 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/DelegateClassTypeParameter.java @@ -0,0 +1,63 @@ +package org.example; + +import org.jspecify.annotations.*; + +@NullMarked +class CustomOptionalTest2 { + @Nullable + public String a; + public String @Nullable [] a3; + + CustomOptional2 returnNotNullableDelegate() { + return Delegate2.ofNullable(a); + } + + CustomOptional2 returnNotNullableDelegate2() { + return Delegate2.ofNullable2(a); + } + + CustomOptional2 returnNotNullableDelegate3() { + return Delegate2.ofNullable3(a3); + } + + CustomOptional2 returnNotNullableDelegate4() { + return Delegate2.ofNullable4(a3); + } + + CustomOptional2 returnNotNullableDelegate5() { + return Delegate2.ofNullable4(a3); + } +} + +class Delegate2 { + static CustomOptional2 ofNullable(@Nullable T t) { + return CustomOptional2.ofNullable(t); + } + + static CustomOptional2 ofNullable2(@Nullable String t) { + return CustomOptional2.ofNullable(t); + } + + static CustomOptional2 ofNullable3(@Nullable String @Nullable [] t) { + return CustomOptional2.ofNullable(t); + } + + static CustomOptional2 ofNullable4(T t) { + return CustomOptional2.ofNullable(t); + } + + static CustomOptional2 ofNullable5(Object t) { + return CustomOptional2.ofNullable(t); + } +} + +class CustomOptional2 { + public CustomOptional2(T s) { + + } + + public static CustomOptional2<@NotNull T> ofNullable(@Nullable T value) { + return value == null ? (CustomOptional2) new CustomOptional2<>("") + : new CustomOptional2<>(value); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java index 036fab92629b..f5ec0d64024f 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java @@ -256,6 +256,12 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase { public void testFlatMapSideEffect() { doTest(); } public void testOptionalValueTracking() { doTest(); } public void testOptionalAsQualifier() { doTest(); } + public void testDelegateClassTypeParameter() { + addJSpecifyNullMarked(myFixture); + setupTypeUseAnnotations("org.jspecify.annotations", myFixture); + doTest(); + } + public void testClearZeroesSize() { doTest(); } public void testLambdaInlineReassignReturnWithDeeperEquality() { doTest(); }