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(); }