From 8aa429fe4c69eaa73dcbc80c57ad4e7d12546c28 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 28 Nov 2025 11:39:47 +0100 Subject: [PATCH] cleanup [java]: JavaPsiAnnotationUtil.processPackageAnnotations; use in MethodUtils.findAnnotationInTree GitOrigin-RevId: 3b3dc8c976638b26fe6bdc5c3bf2e968dca861c1 --- .../core/JavaPsiAnnotationUtil.java | 103 ++++++++++++++++++ .../highlighting/AnnotationChecker.java | 1 + .../analysis/AnnotationsHighlightUtil.java | 2 +- .../intention/AddAnnotationPsiFix.java | 6 +- .../com/siyeh/ig/psiutils/MethodUtils.java | 34 ++---- .../NullableNotNullManagerImpl.java | 43 ++------ .../psi/util/JavaPsiAnnotationUtil.java | 11 +- 7 files changed, 141 insertions(+), 59 deletions(-) create mode 100644 java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiAnnotationUtil.java diff --git a/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiAnnotationUtil.java b/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiAnnotationUtil.java new file mode 100644 index 000000000000..ef549b790eb0 --- /dev/null +++ b/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiAnnotationUtil.java @@ -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); + } +} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java index a3b026d1c397..b0be6e886787 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java index 0e5805661a68..13b0e60ceebe 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java index b62881d2e5e3..32903af33786 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java @@ -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; diff --git a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/MethodUtils.java b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/MethodUtils.java index ac5f935f7c34..56d3bacde550 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/MethodUtils.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/MethodUtils.java @@ -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(); diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index 7789a178df3d..3df86846682c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -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 diff --git a/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiAnnotationUtil.java b/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiAnnotationUtil.java index f1c3991120a4..1e5f5c2d35f4 100644 --- a/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiAnnotationUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiAnnotationUtil.java @@ -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) {