[java-analysis] IDEA-375444 Speedup retrieving package-level nullity annotations

(cherry picked from commit 7be077c9a1a6b7fade1833689567b5947e2c77de)

IJ-CR-168171

GitOrigin-RevId: 01c5b38d7e028585b94d1596c0de8c7c323a2fa3
This commit is contained in:
Tagir Valeev
2025-07-03 18:31:10 +02:00
committed by intellij-monorepo-bot
parent b8b75d3d91
commit e02fbb2cfc
9 changed files with 75 additions and 45 deletions

View File

@@ -30,7 +30,10 @@ import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex;
import com.intellij.psi.search.DelegatingGlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider.Result;
import com.intellij.psi.util.*;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.StreamEx;
import org.jdom.Element;
@@ -290,30 +293,53 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
return result;
}
@Override
protected @NotNull ContextNullabilityInfo findNullityDefaultOnPackage(PsiAnnotation.TargetType @NotNull [] placeTargetTypes,
PsiFile file) {
boolean superPackage = false;
ContextNullabilityInfo info = ContextNullabilityInfo.EMPTY;
ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
VirtualFile vFile = file.getVirtualFile();
if (vFile == null) return info;
VirtualFile root = index.getSourceRootForFile(vFile);
boolean compiled = false;
if (root == null) {
root = index.getClassRootForFile(vFile);
if (root == null) return info;
compiled = true;
}
// Single-file source root -- no package-info processing for now
if (root.equals(vFile)) return info;
PsiDirectory directory = file.getContainingDirectory();
while (directory != null) {
PsiFile packageFile = directory.findFile(compiled ? PsiPackage.PACKAGE_INFO_CLS_FILE : PsiPackage.PACKAGE_INFO_FILE);
if (packageFile instanceof PsiJavaFile javaFile) {
PsiPackageStatement stmt = javaFile.getPackageStatement();
if (stmt != null) {
PsiModifierList modifierList = stmt.getAnnotationList();
if (modifierList != null) {
for (PsiAnnotation annotation : modifierList.getAnnotations()) {
info = info.orElse(checkNullityDefault(annotation, placeTargetTypes, superPackage));
}
}
}
}
if (root.equals(directory.getVirtualFile())) break;
directory = directory.getParentDirectory();
superPackage = true;
}
return info;
}
@Override
protected @NotNull ContextNullabilityInfo getNullityDefault(@NotNull PsiModifierListOwner container,
PsiAnnotation.TargetType @NotNull [] placeTargetTypes,
boolean superPackage) {
PsiAnnotation.TargetType @NotNull [] placeTargetTypes) {
LOG.assertTrue(!(container instanceof PsiPackage)); // Packages are handled separately in findNullityDefaultOnPackage
ContextNullabilityInfo res = ContextNullabilityInfo.EMPTY;
PsiModifierList modifierList = container.getModifierList();
if (modifierList != null) {
for (PsiAnnotation annotation : modifierList.getAnnotations()) {
ContextNullabilityInfo info = checkNullityDefault(annotation, placeTargetTypes, superPackage);
if (container instanceof PsiPackage) {
VirtualFile annotationFile = PsiUtilCore.getVirtualFile(annotation);
info = info.filtering(context -> {
VirtualFile ownerFile = PsiUtilCore.getVirtualFile(context);
if (annotationFile != null && ownerFile != null && !annotationFile.equals(ownerFile)) {
ProjectFileIndex index = ProjectRootManager.getInstance(container.getProject()).getFileIndex();
VirtualFile annotationRoot = index.getClassRootForFile(annotationFile);
VirtualFile ownerRoot = index.getClassRootForFile(ownerFile);
if (ownerRoot != null && !ownerRoot.equals(annotationRoot)) {
return false;
}
}
return true;
});
}
ContextNullabilityInfo info = checkNullityDefault(annotation, placeTargetTypes, false);
res = res.orElse(info);
}
}
@@ -407,7 +433,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
@NotNull PsiElement element) {
PsiJavaModule module = JavaPsiModuleUtil.findDescriptorByElement(element);
if (module != null) {
return getNullityDefault(module, targetTypes, false);
return getNullityDefault(module, targetTypes);
}
return ContextNullabilityInfo.EMPTY;
}

View File

@@ -273,8 +273,7 @@ public abstract class NullableNotNullManager {
}
protected abstract @NotNull ContextNullabilityInfo getNullityDefault(@NotNull PsiModifierListOwner container,
PsiAnnotation.TargetType @NotNull [] placeTargetTypes,
boolean superPackage);
PsiAnnotation.TargetType @NotNull [] placeTargetTypes);
@ApiStatus.Internal
@NotNull
@@ -315,7 +314,7 @@ public abstract class NullableNotNullManager {
if (element instanceof PsiModifierListOwner) {
PsiModifierListOwner listOwner = (PsiModifierListOwner)element;
NullabilityAnnotationInfo result = CachedValuesManager.getCachedValue(listOwner, () -> {
return CachedValueProvider.Result.create(getNullityDefault(listOwner, TYPE_USE_TARGET, false),
return CachedValueProvider.Result.create(getNullityDefault(listOwner, TYPE_USE_TARGET),
PsiModificationTracker.MODIFICATION_COUNT);
}).forContext(context);
if (result != null) {
@@ -326,9 +325,7 @@ public abstract class NullableNotNullManager {
if (element instanceof PsiClassOwner) {
PsiClassOwner classOwner = (PsiClassOwner)element;
return CachedValuesManager.getCachedValue(classOwner, () -> {
String packageName = classOwner.getPackageName();
PsiPackage psiPackage = JavaPsiFacade.getInstance(classOwner.getProject()).findPackage(packageName);
ContextNullabilityInfo fromPackage = findNullityDefaultOnPackage(TYPE_USE_TARGET, psiPackage);
ContextNullabilityInfo fromPackage = findNullityDefaultOnPackage(TYPE_USE_TARGET, classOwner.getContainingFile());
return CachedValueProvider.Result.create(fromPackage.orElse(findNullityDefaultOnModule(TYPE_USE_TARGET, classOwner)),
PsiModificationTracker.MODIFICATION_COUNT);
}).forContext(context);
@@ -356,16 +353,14 @@ public abstract class NullableNotNullManager {
PsiElement element = place.getContext();
while (element != null) {
if (element instanceof PsiModifierListOwner) {
NullabilityAnnotationInfo result = getNullityDefault((PsiModifierListOwner)element, placeTargetTypes, false).forContext(place);
NullabilityAnnotationInfo result = getNullityDefault((PsiModifierListOwner)element, placeTargetTypes).forContext(place);
if (result != null) {
return result;
}
}
if (element instanceof PsiClassOwner) {
String packageName = ((PsiClassOwner)element).getPackageName();
PsiPackage psiPackage = JavaPsiFacade.getInstance(element.getProject()).findPackage(packageName);
NullabilityAnnotationInfo fromPackage = findNullityDefaultOnPackage(placeTargetTypes, psiPackage).forContext(place);
NullabilityAnnotationInfo fromPackage = findNullityDefaultOnPackage(placeTargetTypes, element.getContainingFile()).forContext(place);
if (fromPackage != null) {
return fromPackage;
}
@@ -382,16 +377,9 @@ public abstract class NullableNotNullManager {
return ContextNullabilityInfo.EMPTY;
}
private @NotNull ContextNullabilityInfo findNullityDefaultOnPackage(PsiAnnotation.TargetType @NotNull [] placeTargetTypes,
@Nullable PsiPackage psiPackage) {
boolean superPackage = false;
ContextNullabilityInfo info = ContextNullabilityInfo.EMPTY;
while (psiPackage != null) {
info = info.orElse(getNullityDefault(psiPackage, placeTargetTypes, superPackage));
superPackage = true;
psiPackage = psiPackage.getParentPackage();
}
return info;
protected @NotNull ContextNullabilityInfo findNullityDefaultOnPackage(PsiAnnotation.TargetType @NotNull [] placeTargetTypes,
PsiFile file) {
return ContextNullabilityInfo.EMPTY;
}
public abstract @NotNull List<String> getNullables();

View File

@@ -34,6 +34,12 @@ class ClsPackageStatementImpl extends ClsElementImpl implements PsiPackageStatem
@Override
public PsiModifierList getAnnotationList() {
if (myFile != null && myFile.getName().equals("package-info.class")) {
PsiClass[] classes = myFile.getClasses();
if (classes.length == 1) {
return classes[0].getModifierList();
}
}
throw new UnsupportedOperationException("Method not implemented");
}

View File

@@ -1,4 +1,4 @@
package p;
package refactoring.introduceVariable;
import org.checkerframework.checker.nullness.qual.Nullable;

View File

@@ -1,4 +1,4 @@
package p;
package refactoring.introduceVariable;
class Test {
@org.checkerframework.checker.nullness.qual.Nullable
String s;

View File

@@ -10,6 +10,7 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer;
@@ -26,7 +27,11 @@ public abstract class DataFlowInspectionTestCase extends LightJavaCodeInsightFix
ConstantValueInspection cvInspection = new ConstantValueInspection();
inspectionMutator.accept(inspection, cvInspection);
myFixture.enableInspections(inspection, cvInspection);
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
myFixture.testHighlighting(true, false, true, getTestFileName());
}
protected @NotNull String getTestFileName() {
return getTestName(false) + ".java";
}
static void addCheckerAnnotations(JavaCodeInsightTestFixture fixture) {

View File

@@ -40,9 +40,9 @@ public class LightIntroduceVariableTest extends LightJavaCodeInsightFixtureTestC
myFixture.addClass("package org.checkerframework.framework.qual; public class DefaultQualifier {}");
myFixture.addClass("package org.checkerframework.checker.nullness.qual; @java.lang.annotation.Target({ElementType.TYPE_USE}) public class NonNull {}");
myFixture.addClass("package org.checkerframework.checker.nullness.qual; @java.lang.annotation.Target({ElementType.TYPE_USE}) public @interface Nullable {}");
myFixture.addFileToProject("p/package-info.java", """
myFixture.addFileToProject("refactoring/introduceVariable/package-info.java", """
@org.checkerframework.framework.qual.DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL)
package p;
package refactoring.introduceVariable;
import org.checkerframework.checker.nullness.qual.NonNull;
""");
MockIntroduceVariableHandler handler = new MockIntroduceVariableHandler("m", false, false, false,

View File

@@ -48,4 +48,9 @@ public class LombokDataFlowInspectionTest extends DataFlowInspectionTestCase {
doTest();
}
@Override
protected @NotNull String getTestFileName() {
return "test/" + super.getTestFileName();
}
}