cleanup [java]: JavaPsiAnnotationUtil.processPackageAnnotations; use in MethodUtils.findAnnotationInTree

GitOrigin-RevId: 3b3dc8c976638b26fe6bdc5c3bf2e968dca861c1
This commit is contained in:
Tagir Valeev
2025-11-28 15:05:08 +00:00
committed by intellij-monorepo-bot
parent f025ab655a
commit 8aa429fe4c
7 changed files with 141 additions and 59 deletions
@@ -0,0 +1,103 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeserver.core;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.RetentionPolicy;
/**
* Utility methods related to {@link PsiAnnotation}.
*/
public final class JavaPsiAnnotationUtil {
/**
* @param annotation annotation class
* @return annotation retention policy; null if cannot be determined
*/
public static @Nullable RetentionPolicy getRetentionPolicy(@NotNull PsiClass annotation) {
PsiModifierList modifierList = annotation.getModifierList();
if (modifierList != null) {
PsiAnnotation retentionAnno = modifierList.findAnnotation(CommonClassNames.JAVA_LANG_ANNOTATION_RETENTION);
if (retentionAnno == null) return RetentionPolicy.CLASS;
PsiAnnotationMemberValue policyRef = PsiImplUtil.findAttributeValue(retentionAnno, null);
if (policyRef instanceof PsiReference ref) {
PsiElement field = ref.resolve();
if (field instanceof PsiEnumConstant constant) {
String name = constant.getName();
try {
return Enum.valueOf(RetentionPolicy.class, name);
}
catch (IllegalArgumentException ignored) {
}
}
}
}
return null;
}
/**
* Process package annotations related to the specified {@link PsiFile}.
* Only annotations declared in package-info files within the same source root will be processed.
* Both source and class files are supported.
*
* @param file context {@link PsiFile} to find and process annotations related to
* @param processor a processor function
* @param processSuperPackages whether to process annotations from super-packages.
* A super-package is a package whose qualified name is a prefix of a current package name
* (e.g., package "com.example" is a super-package of "com.example.foo")
*/
public static void processPackageAnnotations(@NotNull PsiFile file,
@NotNull PackageAnnotationProcessor processor,
boolean processSuperPackages) {
boolean superPackage = false;
ProjectFileIndex index = ProjectRootManager.getInstance(file.getProject()).getFileIndex();
VirtualFile vFile = file.getVirtualFile();
if (vFile == null) return;
VirtualFile root = index.getSourceRootForFile(vFile);
boolean compiled = false;
if (root == null) {
root = index.getClassRootForFile(vFile);
if (root == null) return;
compiled = true;
}
// Single-file source root -- no package-info processing for now
if (root.equals(vFile)) return;
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()) {
processor.process(annotation, superPackage);
}
}
}
}
if (!processSuperPackages || root.equals(directory.getVirtualFile())) break;
directory = directory.getParentDirectory();
superPackage = true;
}
}
/**
* A functional interface to be used with {@link #processPackageAnnotations(PsiFile, PackageAnnotationProcessor, boolean)}
*/
@FunctionalInterface
public interface PackageAnnotationProcessor {
/**
* @param annotation applicable package annotation
* @param superPackage whether the annotation is from a super-package
*/
void process(@NotNull PsiAnnotation annotation, boolean superPackage);
}
}
@@ -3,6 +3,7 @@ package com.intellij.java.codeserver.highlighting;
import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.java.codeserver.core.JavaPsiAnnotationUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.AnnotationValueErrorContext;
import com.intellij.java.syntax.parser.JavaKeywords;
@@ -1,8 +1,8 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.java.codeserver.core.JavaPsiAnnotationUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.util.JavaPsiAnnotationUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -10,6 +10,7 @@ import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.java.codeserver.core.JavaPsiAnnotationUtil;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommandExecutor;
@@ -19,7 +20,10 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.util.*;
import com.intellij.psi.util.JavaElementKind;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.CommentTracker;
@@ -2,13 +2,12 @@
package com.siyeh.ig.psiutils;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.java.codeserver.core.JavaPsiAnnotationUtil;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.lang.jvm.JvmModifier;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
@@ -563,28 +562,19 @@ public final class MethodUtils {
}
}
if (element instanceof PsiClassOwner classOwner) {
final String packageName = classOwner.getPackageName();
final PsiPackage aPackage = JavaPsiFacade.getInstance(element.getProject()).findPackage(packageName);
if (aPackage == null) {
return null;
}
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(aPackage, fqAnnotationNames);
if(annotation != null) {
// Check that annotation actually belongs to the same library/source root
// which could be important in case of split-packages
final VirtualFile annotationFile = PsiUtilCore.getVirtualFile(annotation);
final VirtualFile currentFile = classOwner.getVirtualFile();
if(annotationFile != null && currentFile != null) {
final ProjectFileIndex projectFileIndex = ProjectFileIndex.getInstance(element.getProject());
final VirtualFile annotationClassRoot = projectFileIndex.getClassRootForFile(annotationFile);
final VirtualFile currentClassRoot = projectFileIndex.getClassRootForFile(currentFile);
if (!Objects.equals(annotationClassRoot, currentClassRoot)) {
return null;
if (element instanceof PsiFile classOwner) {
var processor = new JavaPsiAnnotationUtil.PackageAnnotationProcessor() {
PsiAnnotation myAnnotation = null;
@Override
public void process(@NotNull PsiAnnotation annotation, boolean superPackage) {
if (fqAnnotationNames.contains(annotation.getQualifiedName())) {
myAnnotation = annotation;
}
}
}
return annotation;
};
JavaPsiAnnotationUtil.processPackageAnnotations(classOwner, processor, false);
return processor.myAnnotation;
}
element = element.getContext();
@@ -9,6 +9,7 @@ import com.intellij.codeInspection.options.OptionController;
import com.intellij.codeInspection.options.OptionControllerProvider;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.JavaBundle;
import com.intellij.java.codeserver.core.JavaPsiAnnotationUtil;
import com.intellij.java.codeserver.core.JavaPsiModuleUtil;
import com.intellij.java.library.JavaLibraryModificationTracker;
import com.intellij.java.library.JavaLibraryUtil;
@@ -23,7 +24,6 @@ import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -360,39 +360,16 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
@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));
}
}
}
var processor = new JavaPsiAnnotationUtil.PackageAnnotationProcessor() {
@NotNull ContextNullabilityInfo info = ContextNullabilityInfo.EMPTY;
@Override
public void process(@NotNull PsiAnnotation annotation, boolean superPackage) {
info = info.orElse(checkNullityDefault(annotation, placeTargetTypes, superPackage));
}
if (root.equals(directory.getVirtualFile())) break;
directory = directory.getParentDirectory();
superPackage = true;
}
return info;
};
JavaPsiAnnotationUtil.processPackageAnnotations(file, processor, true);
return processor.info;
}
@Override
@@ -3,16 +3,23 @@ package com.intellij.psi.util;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.RetentionPolicy;
/**
* @deprecated use com.intellij.java.codeserver.core.JavaPsiAnnotationUtil
*/
@ApiStatus.ScheduledForRemoval
@Deprecated
public final class JavaPsiAnnotationUtil {
/**
* @param annotation annotation class
* @return annotation retention policy; null if cannot be determined
* @deprecated use com.intellij.java.codeserver.core.JavaPsiAnnotationUtil
*/
@ApiStatus.ScheduledForRemoval
@Deprecated
public static @Nullable RetentionPolicy getRetentionPolicy(@NotNull PsiClass annotation) {
PsiModifierList modifierList = annotation.getModifierList();
if (modifierList != null) {