introduce option "report inaccessible members (javadoc tool may not create hyperlinks" to "javadoc reference problems" inspection

This commit is contained in:
Alexey Kudravtsev
2018-08-14 13:56:17 +03:00
parent 09ab2c7268
commit 46fdbb6e0e
2 changed files with 54 additions and 14 deletions
@@ -8,6 +8,7 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.FQNameCellRenderer;
import com.intellij.openapi.actionSystem.CommonDataKeys;
@@ -17,12 +18,16 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.proximity.PsiProximityComparator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import static com.intellij.codeInspection.ProblemHighlightType.LIKE_UNKNOWN_SYMBOL;
@@ -30,6 +35,15 @@ import static com.intellij.codeInspection.ProblemHighlightType.LIKE_UNKNOWN_SYMB
public class JavaDocReferenceInspection extends LocalInspectionTool {
private static final String SHORT_NAME = "JavadocReference";
@SuppressWarnings("WeakerAccess")
public boolean REPORT_INACCESSIBLE;
@Nullable
@Override
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel("<html>Report inaccessible symbols<br>(javadoc tool maybe unable to create hyperlink)", this, "REPORT_INACCESSIBLE");
}
protected LocalQuickFix createAddQualifierFix(PsiJavaCodeReferenceElement reference) {
List<PsiClass> classesToImport = new ImportClassFix(reference).getClassesToImport();
return classesToImport.isEmpty() ? null : new AddQualifierFix(classesToImport);
@@ -39,6 +53,29 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
return new RenameReferenceQuickFix(unboundParams);
}
private String getResolveErrorMessage(PsiElement resolved, @NotNull PsiElement context, CharSequence referenceText) {
if (resolved == null) {
return InspectionsBundle.message("inspection.javadoc.problem.cannot.resolve", "<code>" + referenceText + "</code>");
}
if (REPORT_INACCESSIBLE && !isAccessible(resolved, context)) {
return InspectionsBundle.message("inspection.javadoc.problem.inaccessible", "<code>" + referenceText + "</code>");
}
return null;
}
private static boolean isAccessible(@NotNull PsiElement resolved, @NotNull PsiElement context) {
if (!(resolved instanceof PsiMember)) {
return true;
}
if (!PsiResolveHelper.SERVICE.getInstance(resolved.getProject()).isAccessible((PsiMember)resolved, context, null)) {
return false;
}
VirtualFile file = PsiUtilCore.getVirtualFile(resolved);
return file == null || context.getResolveScope().contains(file);
}
private void visitRefInDocTag(PsiDocTag tag, JavadocManager manager, PsiElement context, ProblemsHolder holder, boolean isOnTheFly) {
PsiDocTagValue value = tag.getValueElement();
if (value == null) return;
@@ -57,14 +94,19 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
PsiReference reference = value.getReference();
if (reference == null) return;
PsiElement element = reference.resolve();
if (element != null) return;
int textOffset = value.getTextOffset();
if (textOffset == value.getTextRange().getEndOffset()) return;
PsiDocTagValue valueElement = tag.getValueElement();
if (valueElement == null) return;
CharSequence paramName = value.getContainingFile().getViewProvider().getContents().subSequence(textOffset, value.getTextRange().getEndOffset());
String params = "<code>" + paramName + "</code>";
String message = getResolveErrorMessage(element, context, paramName);
if (message == null) {
return;
}
List<LocalQuickFix> fixes = new ArrayList<>();
if (isOnTheFly && "param".equals(tagName)) {
PsiDocCommentOwner commentOwner = PsiTreeUtil.getParentOfType(tag, PsiDocCommentOwner.class);
@@ -85,9 +127,8 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
}
fixes.add(new RemoveTagFix(tagName, paramName));
LocalQuickFix[] array = fixes.toArray(LocalQuickFix.EMPTY_ARRAY);
holder.registerProblem(holder.getManager().createProblemDescriptor(
valueElement, reference.getRangeInElement(), cannotResolveSymbolMessage(params), LIKE_UNKNOWN_SYMBOL, isOnTheFly, array));
holder.registerProblem(holder.getManager().createProblemDescriptor(valueElement, reference.getRangeInElement(), message,
LIKE_UNKNOWN_SYMBOL, isOnTheFly, fixes.toArray(LocalQuickFix.EMPTY_ARRAY)));
}
@Override
@@ -152,7 +193,7 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
};
}
private void checkComment(PsiDocComment comment, PsiElement context, ProblemsHolder holder, boolean isOnTheFly) {
private void checkComment(PsiDocComment comment, @NotNull PsiElement context, ProblemsHolder holder, boolean isOnTheFly) {
if (comment == null) return;
JavadocManager javadocManager = JavadocManager.SERVICE.getInstance(holder.getProject());
@@ -166,10 +207,12 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
JavaResolveResult result = reference.advancedResolve(false);
if (result.getElement() == null && !result.isPackagePrefixPackageReference()) {
PsiElement resolved = result.getElement();
String message = getResolveErrorMessage(resolved, context, reference.getText());
if (message != null && !result.isPackagePrefixPackageReference()) {
PsiElement referenceNameElement = reference.getReferenceNameElement();
PsiElement element = referenceNameElement != null ? referenceNameElement : reference;
String message = cannotResolveSymbolMessage("<code>" + reference.getText() + "</code>");
LocalQuickFix fix = isOnTheFly ? createAddQualifierFix(reference) : null;
holder.registerProblem(holder.getManager().createProblemDescriptor(element, message, fix, LIKE_UNKNOWN_SYMBOL, isOnTheFly));
}
@@ -199,10 +242,6 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
});
}
private static String cannotResolveSymbolMessage(String params) {
return InspectionsBundle.message("inspection.javadoc.problem.cannot.resolve", params);
}
private static class RenameReferenceQuickFix implements LocalQuickFix {
private final Set<String> myUnboundParams;
@@ -240,9 +279,9 @@ public class JavaDocReferenceInspection extends LocalInspectionTool {
}
private static class AddQualifierFix implements LocalQuickFix {
private final List<PsiClass> originalClasses;
private final List<? extends PsiClass> originalClasses;
public AddQualifierFix(final List<PsiClass> originalClasses) {
public AddQualifierFix(final List<? extends PsiClass> originalClasses) {
this.originalClasses = originalClasses;
}
@@ -315,6 +315,7 @@ inspection.javadoc.problem.missing.tag=Required tag {0} is missing
inspection.javadoc.problem.missing.tag.description={0} is missing after @{1} tag
inspection.javadoc.problem.see.tag.expecting.ref=Class/method reference, quoted text, or HTML link are expected after @see tag
inspection.javadoc.problem.cannot.resolve=Cannot resolve symbol {0}
inspection.javadoc.problem.inaccessible=Symbol {0} is inaccessible from here
inspection.javadoc.problem.name.expected=Name expected
inspection.javadoc.problem.wrong.tag=Wrong tag {0}
inspection.javadoc.problem.disallowed.tag=Tag {0} is not allowed here