mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-highlighting] Provide quick-fix for the "Package annotations should be in file package-info.java" error
IDEA-216254 GitOrigin-RevId: 9d8c4074babff861b5702caa09dd320de5d63d72
This commit is contained in:
committed by
intellij-monorepo-bot
parent
71ed47f845
commit
7bf9474dac
@@ -615,6 +615,7 @@ inspection.unsatisfied.range.display.name=Return value is outside of declared ra
|
||||
inspection.unsatisfied.range.message=Return value range ''{0}'' is outside of declared range ''{1}''
|
||||
inspection.unsatisfied.range.message.value=Return value ''{0}'' is outside of declared range ''{1}''
|
||||
intention.family.name.move.annotation.to.array=Move annotation to array
|
||||
move.annotations.to.package.info.file.family.name=Move annotations to 'package-info.java'
|
||||
|
||||
inspection.redundant.requires.statement.message.transitive.dependencies.on.can.be.used.directly=Transitive dependencies on ''{0}'' can be used directly.
|
||||
inspection.redundant.requires.statement.message.module.unused=No usages of module packages are found.
|
||||
|
||||
+7
-1
@@ -626,7 +626,13 @@ public final class AnnotationsHighlightUtil {
|
||||
PsiModifierList annotationList = statement.getAnnotationList();
|
||||
if (annotationList != null && !PsiPackage.PACKAGE_INFO_FILE.equals(file.getName())) {
|
||||
String message = JavaErrorBundle.message("invalid.package.annotation.containing.file");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(annotationList).descriptionAndTooltip(message);
|
||||
IntentionAction deleteFix =
|
||||
QUICK_FIX_FACTORY.createDeleteFix(annotationList, JavaAnalysisBundle.message("intention.text.remove.annotation"));
|
||||
HighlightInfo.Builder builder =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(annotationList).descriptionAndTooltip(message);
|
||||
IntentionAction moveAnnotationToPackageInfoFileFix = new MoveAnnotationToPackageInfoFileFix(statement);
|
||||
return builder.registerFix(deleteFix, null, null, null, null)
|
||||
.registerFix(moveAnnotationToPackageInfoFileFix, null, null, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
// Copyright 2000-2022 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.codeInsight.intention.preview.IntentionPreviewInfo;
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MoveAnnotationToPackageInfoFileFix extends LocalQuickFixAndIntentionActionOnPsiElement {
|
||||
|
||||
protected MoveAnnotationToPackageInfoFileFix(@NotNull PsiPackageStatement pkgStatement) {
|
||||
super(pkgStatement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
PsiPackageStatement packageStatement = (PsiPackageStatement)startElement;
|
||||
PsiPackage aPackage = getPackage(packageStatement);
|
||||
if (aPackage == null) return false;
|
||||
PsiFile packageInfoFile = getPackageInfoFile(aPackage);
|
||||
if (packageInfoFile == null) return true;
|
||||
if (!PsiPackage.PACKAGE_INFO_FILE.equals(packageInfoFile.getName())) return false;
|
||||
PsiPackageStatement packageStatementInPackageInfoFile = PsiTreeUtil.findChildOfType(packageInfoFile, PsiPackageStatement.class);
|
||||
if (packageStatementInPackageInfoFile == null) return false;
|
||||
PsiModifierList missingAnnotations = findMissingAnnotations(packageStatement, packageStatementInPackageInfoFile);
|
||||
return missingAnnotations != null && missingAnnotations.getAnnotations().length != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@Nullable Editor editor,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
PsiPackageStatement packageStatement = (PsiPackageStatement)startElement;
|
||||
PsiPackage aPackage = getPackage(packageStatement);
|
||||
if (aPackage == null) return;
|
||||
PsiFile packageInfoFile = getPackageInfoFile(aPackage);
|
||||
if (packageInfoFile == null) {
|
||||
PsiFile createdFile = file.getContainingDirectory().createFile(PsiPackage.PACKAGE_INFO_FILE);
|
||||
createdFile.add(packageStatement);
|
||||
createdFile.navigate(true);
|
||||
}
|
||||
else if (PsiPackage.PACKAGE_INFO_FILE.equals(packageInfoFile.getName())) {
|
||||
PsiFile modifiedPackageInfoFile = moveAnnotationsAndGetFile(packageStatement, packageInfoFile);
|
||||
if (modifiedPackageInfoFile != null) {
|
||||
modifiedPackageInfoFile.navigate(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable PsiPackage getPackage(@NotNull PsiPackageStatement packageStatement) {
|
||||
PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference();
|
||||
return ObjectUtils.tryCast(packageReference.resolve(), PsiPackage.class);
|
||||
}
|
||||
|
||||
private static @Nullable PsiFile moveAnnotationsAndGetFile(@NotNull PsiPackageStatement packageStatement,
|
||||
@NotNull PsiFile packageInfoFile) {
|
||||
PsiPackageStatement packageStatementInPackageInfoFile = PsiTreeUtil.findChildOfType(packageInfoFile, PsiPackageStatement.class);
|
||||
if (packageStatementInPackageInfoFile == null) return null;
|
||||
PsiModifierList missingAnnotations = findMissingAnnotations(packageStatement, packageStatementInPackageInfoFile);
|
||||
if (missingAnnotations == null) return null;
|
||||
PsiModifierList annotationList = packageStatementInPackageInfoFile.getAnnotationList();
|
||||
if (annotationList != null) {
|
||||
StreamEx.of(missingAnnotations.getAnnotations()).forEach(annotationList::add);
|
||||
}
|
||||
else {
|
||||
packageStatementInPackageInfoFile.addBefore(missingAnnotations, packageStatementInPackageInfoFile.getFirstChild());
|
||||
}
|
||||
return packageInfoFile;
|
||||
}
|
||||
|
||||
private static @Nullable PsiModifierList findMissingAnnotations(@NotNull PsiPackageStatement packageStatement,
|
||||
@NotNull PsiPackageStatement packageStatementInPackageInfoFile) {
|
||||
PsiModifierList annotationList = packageStatementInPackageInfoFile.getAnnotationList();
|
||||
if (annotationList == null) return packageStatement.getAnnotationList();
|
||||
PsiPackageStatement copy = (PsiPackageStatement)packageStatement.copy();
|
||||
StreamEx.of(copy.getAnnotationList().getChildren())
|
||||
.select(PsiAnnotation.class)
|
||||
.filter(annotation -> {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
return qualifiedName != null && annotationList.hasAnnotation(qualifiedName);
|
||||
})
|
||||
.forEach(PsiElement::delete);
|
||||
return copy.getAnnotationList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
PsiPackageStatement packageStatement = (PsiPackageStatement)myStartElement.getElement();
|
||||
assert packageStatement != null;
|
||||
PsiPackage aPackage = getPackage(packageStatement);
|
||||
if (aPackage == null) return IntentionPreviewInfo.EMPTY;
|
||||
PsiFile packageInfoFile = getPackageInfoFile(aPackage);
|
||||
if (packageInfoFile == null) {
|
||||
return new IntentionPreviewInfo.CustomDiff(JavaFileType.INSTANCE, PsiPackage.PACKAGE_INFO_FILE, "", packageStatement.getText());
|
||||
}
|
||||
else if (PsiPackage.PACKAGE_INFO_FILE.equals(packageInfoFile.getName())) {
|
||||
PsiFile modifiedPackageInfoFile = moveAnnotationsAndGetFile(packageStatement, (PsiFile)packageInfoFile.copy());
|
||||
if (modifiedPackageInfoFile == null) return IntentionPreviewInfo.EMPTY;
|
||||
return new IntentionPreviewInfo.CustomDiff(JavaFileType.INSTANCE, PsiPackage.PACKAGE_INFO_FILE, packageInfoFile.getText(),
|
||||
modifiedPackageInfoFile.getText());
|
||||
}
|
||||
return IntentionPreviewInfo.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getText() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return JavaAnalysisBundle.message("move.annotations.to.package.info.file.family.name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either a `package-info.java` or `package.html` file for the specified package.
|
||||
*
|
||||
* @param aPackage the package
|
||||
* @return a `package-info.java` or `package.html` file or {@code null}
|
||||
* if the package does not contain such files.
|
||||
*/
|
||||
@Contract("null -> null")
|
||||
public static @Nullable PsiFile getPackageInfoFile(@Nullable PsiPackage aPackage) {
|
||||
if (aPackage == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiDirectory[] directories = aPackage.getDirectories();
|
||||
for (PsiDirectory directory : directories) {
|
||||
final PsiFile packageInfoJava = directory.findFile(PsiPackage.PACKAGE_INFO_FILE);
|
||||
if (packageInfoJava != null) return packageInfoJava;
|
||||
final PsiFile packageHtml = directory.findFile("package.html");
|
||||
if (packageHtml != null) {
|
||||
return packageHtml;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Remove" "true-preview"
|
||||
package com.example;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PACKAGE)
|
||||
@interface Anno {
|
||||
int value();
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Remove" "true-preview"
|
||||
@Anno(value=42)<caret>
|
||||
package com.example;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PACKAGE)
|
||||
@interface Anno {
|
||||
int value();
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2022 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.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
|
||||
public class RemovePackageAnnotationFixTest extends LightQuickFixParameterizedTestCase {
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/removePackageAnnotation";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-20
@@ -1,7 +1,8 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.javadoc;
|
||||
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.MoveAnnotationToPackageInfoFileFix;
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.reference.RefPackage;
|
||||
@@ -21,7 +22,6 @@ import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.BaseSharedLocalInspection;
|
||||
import com.siyeh.ig.InspectionGadgetsFix;
|
||||
import com.siyeh.ig.PackageGlobalInspection;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -46,7 +46,8 @@ public class MissingPackageInfoInspection extends PackageGlobalInspection {
|
||||
final String packageName = refPackage.getQualifiedName();
|
||||
final Project project = globalInspectionContext.getProject();
|
||||
final PsiPackage aPackage = ReadAction.compute(() -> JavaPsiFacade.getInstance(project).findPackage(packageName));
|
||||
boolean needsPackageInfo = ReadAction.compute(() -> !hasPackageInfoFile(aPackage) && aPackage.getClasses().length > 0);
|
||||
boolean needsPackageInfo =
|
||||
ReadAction.compute(() -> MoveAnnotationToPackageInfoFileFix.getPackageInfoFile(aPackage) == null && aPackage.getClasses().length > 0);
|
||||
if (!needsPackageInfo) {
|
||||
return null;
|
||||
}
|
||||
@@ -60,22 +61,6 @@ public class MissingPackageInfoInspection extends PackageGlobalInspection {
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> true")
|
||||
static boolean hasPackageInfoFile(PsiPackage aPackage) {
|
||||
if (aPackage == null) {
|
||||
return true;
|
||||
}
|
||||
final PsiDirectory[] directories = aPackage.getDirectories();
|
||||
for (PsiDirectory directory : directories) {
|
||||
final boolean packageInfoFound = directory.findFile(PsiPackage.PACKAGE_INFO_FILE) != null;
|
||||
final boolean packageDotHtmlFound = directory.findFile("package.html") != null;
|
||||
if (packageInfoFound || packageDotHtmlFound) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class LocalMissingPackageInfoInspection extends BaseSharedLocalInspection<MissingPackageInfoInspection> {
|
||||
|
||||
LocalMissingPackageInfoInspection(MissingPackageInfoInspection settingsDelegate) {
|
||||
@@ -143,7 +128,7 @@ public class MissingPackageInfoInspection extends PackageGlobalInspection {
|
||||
return;
|
||||
}
|
||||
final PsiPackage aPackage = (PsiPackage)target;
|
||||
if (hasPackageInfoFile(aPackage)) {
|
||||
if (MoveAnnotationToPackageInfoFileFix.getPackageInfoFile(aPackage) != null) {
|
||||
return;
|
||||
}
|
||||
registerError(packageReference, packageStatement);
|
||||
|
||||
Reference in New Issue
Block a user