diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index 24f57f7d1b8c..1265c2983119 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -17,6 +17,7 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.extensions.ExtensionPointListener; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.fileTypes.FileTypeRegistry; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; @@ -142,16 +143,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement @Override public @NotNull Optional getAnnotationNullability(String name) { - if (myNotNulls.contains(name)) { - return Optional.of(Nullability.NOT_NULL); - } - if (myNullables.contains(name)) { - return Optional.of(Nullability.NULLABLE); - } - if (myDefaultUnknowns.containsKey(name)) { - return Optional.of(Nullability.UNKNOWN); - } - return Optional.empty(); + return Optional.ofNullable(getAllNullabilityAnnotationsWithNickNames().getNullability(name)); } @Override @@ -361,41 +353,40 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement @Override protected @NotNull NullabilityAnnotationDataHolder getAllNullabilityAnnotationsWithNickNames() { + if (DumbService.isDumb(myProject)) { + // Searching for nullability nicknames is not available in the dumb mode + return NullabilityAnnotationDataHolder.fromMap(getNullabilityMap()); + } return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> { - Map result = new HashMap<>(); - for (String qName : myDefaultAll) { - result.put(qName, null); - } - for (String qName : getNotNulls()) { - result.put(qName, Nullability.NOT_NULL); - } - for (String qName : getNullables()) { - result.put(qName, Nullability.NULLABLE); - } - for (String qName : myDefaultUnknowns.keySet()) { - result.put(qName, Nullability.UNKNOWN); - } + Map result = getNullabilityMap(); for (PsiClass aClass : getAllNullabilityNickNames()) { String qName = aClass.getQualifiedName(); if (qName != null) { result.putIfAbsent(qName, Jsr305Support.getNickNamedNullability(aClass)); } } - NullabilityAnnotationDataHolder holder = new NullabilityAnnotationDataHolder() { - @Override - public Set qualifiedNames() { - return result.keySet(); - } - - @Override - public @Nullable Nullability getNullability(String annotation) { - return result.get(annotation); - } - }; + NullabilityAnnotationDataHolder holder = NullabilityAnnotationDataHolder.fromMap(result); return Result.create(holder, PsiModificationTracker.MODIFICATION_COUNT); }); } + private @NotNull Map getNullabilityMap() { + Map result = new HashMap<>(); + for (String qName : myDefaultAll) { + result.put(qName, null); + } + for (String qName : getNotNulls()) { + result.put(qName, Nullability.NOT_NULL); + } + for (String qName : getNullables()) { + result.put(qName, Nullability.NULLABLE); + } + for (String qName : myDefaultUnknowns.keySet()) { + result.put(qName, Nullability.UNKNOWN); + } + return result; + } + @Override protected @NotNull Nullability correctNullability(@NotNull Nullability nullability, @NotNull PsiAnnotation annotation) { if (nullability == Nullability.NOT_NULL && annotation.hasQualifiedName(Jsr305Support.JAVAX_ANNOTATION_NONNULL)) { diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index 63a27350dc2b..2a0c811e42c1 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -10,10 +10,7 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.*; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import static com.intellij.codeInsight.AnnotationUtil.*; @@ -489,5 +486,23 @@ public abstract class NullableNotNullManager { * @return nullability */ @Nullable Nullability getNullability(String annotation); + + /** + * @param map from annotation qualified name to nullability + * @return a data holder implementation based on the provided map + */ + static @NotNull NullabilityAnnotationDataHolder fromMap(@NotNull Map map) { + return new NullabilityAnnotationDataHolder() { + @Override + public Set qualifiedNames() { + return map.keySet(); + } + + @Override + public @Nullable Nullability getNullability(String annotation) { + return map.get(annotation); + } + }; + } } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/Jsr305NicknameAsTypeAnnotation.java b/java/java-tests/testData/inspection/dataFlow/fixture/Jsr305NicknameAsTypeAnnotation.java new file mode 100644 index 000000000000..b28d7876033f --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/Jsr305NicknameAsTypeAnnotation.java @@ -0,0 +1,42 @@ +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierDefault; +import javax.annotation.meta.TypeQualifierNickname; +import javax.annotation.meta.When; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Nonnull(when = When.ALWAYS) +@TypeQualifierNickname +@Target({ElementType.TYPE}) +@TypeQualifierDefault({ + ElementType.METHOD, + ElementType.FIELD, + ElementType.PARAMETER, + ElementType.LOCAL_VARIABLE, + ElementType.TYPE_PARAMETER, + ElementType.TYPE_USE, +}) +@interface DefaultNonNull { +} + +@Nonnull(when = When.MAYBE) +@TypeQualifierNickname +@Target({ + ElementType.METHOD, + ElementType.FIELD, + ElementType.PARAMETER, + ElementType.LOCAL_VARIABLE, + ElementType.TYPE_USE, +}) +@interface Nullable { +} + +@DefaultNonNull +class Test { + void test(String s) { + if (s == null) {} + @Nullable String @Nullable [] arr = new String[] {"a", "b", "c", null}; + @Nullable String @Nullable [] arr2 = {"a", "b", "c", null}; + @Nullable String @Nullable [] arr3 = new @Nullable String [] {"a", "b", "c", null}; + } +} 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 6aa6ac03a45b..71a58f828335 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java @@ -179,4 +179,9 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase { setupTypeUseAnnotations("org.jspecify.annotations", myFixture); doTest(); } + + public void testJsr305NicknameAsTypeAnnotation() { + DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture); + doTest(); + } } \ No newline at end of file