IDEA-386341 [java-analysis] PE: IdempotenceChecker.reportFailure via NullableNotNullManagerImpl.getAllNullabilityNickNames

Cache fqn->nullability instead of PsiClass. PsiClass is unnecessary and not always equals/hashCode-comparable, so we may store duplicates


(cherry picked from commit 760ec4bcca697de0055ea7d0262341aecb97a71d)

IJ-CR-193998

GitOrigin-RevId: 097ed6208b4c6b2eb223e0bb48e6627281fbae6b
This commit is contained in:
Tagir Valeev
2026-02-27 15:36:06 +01:00
committed by intellij-monorepo-bot
parent 6354ec3ec4
commit 5d3d1708b9

View File

@@ -64,13 +64,11 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.SequencedMap;
import java.util.Set;
import java.util.function.Function;
import static com.intellij.codeInsight.AnnotationUtil.NOT_NULL;
@@ -279,7 +277,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
}
}
if (myInstrumentedNotNulls.size() != 1 || !NOT_NULL.equals(myInstrumentedNotNulls.get(0))) {
if (myInstrumentedNotNulls.size() != 1 || !NOT_NULL.equals(myInstrumentedNotNulls.getFirst())) {
// poor man's @XCollection(style = XCollection.Style.v2)
Element instrumentedNotNulls = new Element(INSTRUMENTED_NOT_NULLS_TAG);
for (String value : myInstrumentedNotNulls) {
@@ -351,12 +349,11 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
myTracker.incModificationCount();
}
private @NotNull List<PsiClass> getAllNullabilityNickNames() {
private @NotNull Map<String, Nullability> getAllNullabilityNickNames() {
if (!getNotNulls().contains(Jsr305Support.JAVAX_ANNOTATION_NONNULL)) {
return Collections.emptyList();
return Collections.emptyMap();
}
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> {
Set<PsiClass> result = new HashSet<>(getPossiblyUnresolvedJavaNicknameUsages());
GlobalSearchScope scope = new DelegatingGlobalSearchScope(GlobalSearchScope.allScope(myProject)) {
@Override
public boolean contains(@NotNull VirtualFile file) {
@@ -364,26 +361,24 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
}
};
PsiClass[] nickDeclarations = JavaPsiFacade.getInstance(myProject).findClasses(Jsr305Support.TYPE_QUALIFIER_NICKNAME, scope);
for (PsiClass tqNick : nickDeclarations) {
result.addAll(ContainerUtil.findAll(MetaAnnotationUtil.getChildren(tqNick, scope), Jsr305Support::isNullabilityNickName));
}
return Result.create(new ArrayList<>(result), PsiModificationTracker.MODIFICATION_COUNT);
Map<String, Nullability> result = StreamEx.of(nickDeclarations)
.flatCollection(tqNick -> MetaAnnotationUtil.getChildren(tqNick, scope))
.prepend(getPossiblyUnresolvedJavaNicknameUsages())
.filter(Jsr305Support::isNullabilityNickName)
.toMap(PsiClass::getQualifiedName, Jsr305Support::getNickNamedNullability, (n1, n2) -> n1 == n2 ? n1 : Nullability.UNKNOWN);
return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT);
});
}
// some frameworks use jsr305 annotations but don't have them in classpath
private @NotNull List<PsiClass> getPossiblyUnresolvedJavaNicknameUsages() {
List<PsiClass> result = new ArrayList<>();
private @NotNull StreamEx<PsiClass> getPossiblyUnresolvedJavaNicknameUsages() {
Collection<PsiAnnotation> annotations = JavaAnnotationIndex.getInstance().getAnnotations(StringUtil.getShortName(
Jsr305Support.TYPE_QUALIFIER_NICKNAME), myProject, GlobalSearchScope.allScope(myProject));
for (PsiAnnotation annotation : annotations) {
PsiElement context = annotation.getContext();
if (context instanceof PsiModifierList && context.getContext() instanceof PsiClass ownerClass &&
ownerClass.isAnnotationType() && Jsr305Support.isNullabilityNickName(ownerClass)) {
result.add(ownerClass);
}
}
return result;
return StreamEx.of(annotations).map(PsiElement::getContext)
.filter(context -> context instanceof PsiModifierList)
.map(PsiElement::getContext)
.select(PsiClass.class)
.filter(PsiClass::isAnnotationType);
}
@Override
@@ -439,7 +434,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
}
private @Unmodifiable @NotNull List<String> filterNickNames(@NotNull Nullability nullability) {
return ContainerUtil.mapNotNull(getAllNullabilityNickNames(), c -> Jsr305Support.getNickNamedNullability(c) == nullability ? c.getQualifiedName() : null);
return StreamEx.ofKeys(getAllNullabilityNickNames(), nullability::equals).toList();
}
@Override
@@ -466,12 +461,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
}
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> {
Map<String, Nullability> result = getNullabilityMap();
for (PsiClass aClass : getAllNullabilityNickNames()) {
String qName = aClass.getQualifiedName();
if (qName != null) {
result.putIfAbsent(qName, Jsr305Support.getNickNamedNullability(aClass));
}
}
getAllNullabilityNickNames().forEach(result::putIfAbsent);
NullabilityAnnotationDataHolder holder = NullabilityAnnotationDataHolder.fromMap(result);
return Result.create(holder, PsiModificationTracker.MODIFICATION_COUNT);
});