mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[javadoc] IDEA-275440 quick documentation shows throws list from interface JavaDocs
Do not extract the throws list from a super method, use the super method's javadoc throws section only to copy the description of a particular @throws entry when explicitly requested via {@inheritDoc}
GitOrigin-RevId: dabc6935e58addb9b03ae9065360f9bc89a13720
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d5a368f4e3
commit
8211d063f7
@@ -5,7 +5,6 @@ import com.intellij.CommonBundle;
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerProtocol;
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerUtil;
|
||||
import com.intellij.java.JavaBundle;
|
||||
@@ -32,7 +31,10 @@ import com.intellij.openapi.projectRoots.JavaSdk;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.HtmlBuilder;
|
||||
import com.intellij.openapi.util.text.HtmlChunk;
|
||||
@@ -324,7 +326,7 @@ public class JavaDocInfoGenerator {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DocTagLocator<PsiDocTag> exceptionLocator(String name) {
|
||||
private static DocTagLocator<PsiDocTag> exceptionLocator(@NotNull String name) {
|
||||
return (owner, comment) -> {
|
||||
if (comment == null) return null;
|
||||
|
||||
@@ -1533,6 +1535,7 @@ public class JavaDocInfoGenerator {
|
||||
generateLinkValue(tag, buffer, true);
|
||||
}
|
||||
else if (tagName.equals(INHERIT_DOC_TAG)) {
|
||||
if (provider == null) continue;
|
||||
Pair<PsiElement[], InheritDocProvider<PsiElement[]>> inheritInfo = provider.getInheritDoc();
|
||||
if (inheritInfo != null) {
|
||||
generateValue(buffer, inheritInfo.first, inheritInfo.second);
|
||||
@@ -1942,120 +1945,104 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
|
||||
private void generateThrowsSection(StringBuilder buffer, PsiMethod method, PsiDocComment comment) {
|
||||
PsiDocTag[] localTags = getThrowsTags(comment);
|
||||
PsiDocTag[] thrownTags = localTags;
|
||||
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(method.getProject());
|
||||
final PsiDocTag[] throwsJavadocTags = getThrowsTags(comment);
|
||||
|
||||
Set<PsiClass> reported = new HashSet<>();
|
||||
if (!isRendered()) {
|
||||
for (HierarchicalMethodSignature signature : method.getHierarchicalMethodSignature().getSuperSignatures()) {
|
||||
PsiMethod superMethod = ObjectUtils.tryCast(signature.getMethod().getNavigationElement(), PsiMethod.class);
|
||||
PsiDocComment docComment = superMethod != null ? superMethod.getDocComment() : null;
|
||||
if (docComment != null) {
|
||||
PsiDocTag[] uncheckedExceptions = Arrays.stream(getThrowsTags(docComment)).filter(tag -> {
|
||||
PsiDocTagValue valueElement = tag.getValueElement();
|
||||
if (valueElement == null) return false;
|
||||
if (Arrays.stream(localTags)
|
||||
.map(PsiDocTag::getValueElement)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(docTagValue -> areWeakEqual(docTagValue.getText(), valueElement.getText()))) {
|
||||
return false;
|
||||
}
|
||||
PsiClass exClass = psiFacade.getResolveHelper().resolveReferencedClass(valueElement.getText(), docComment);
|
||||
if (exClass == null) return false;
|
||||
return ExceptionUtil.isUncheckedException(exClass) && reported.add(exClass);
|
||||
}).toArray(PsiDocTag[]::new);
|
||||
thrownTags = ArrayUtil.mergeArrays(thrownTags, uncheckedExceptions);
|
||||
}
|
||||
final PsiJavaCodeReferenceElement[] methodThrows = isRendered() ? PsiJavaCodeReferenceElement.EMPTY_ARRAY
|
||||
: method.getThrowsList().getReferenceElements();
|
||||
|
||||
final int totalThrowsToDocument = throwsJavadocTags.length + methodThrows.length;
|
||||
if (totalThrowsToDocument == 0) return;
|
||||
|
||||
final class Data {
|
||||
final @NotNull PsiJavaCodeReferenceElement ref;
|
||||
final @Nullable PsiDocTag tag;
|
||||
|
||||
private Data(@NotNull PsiJavaCodeReferenceElement ref, @Nullable PsiDocTag tag) {
|
||||
this.ref = ref;
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
|
||||
LinkedList<Pair<PsiDocTag, InheritDocProvider<PsiDocTag>>> collectedTags = new LinkedList<>();
|
||||
List<PsiClassType> declaredThrows = isRendered() ? Collections.emptyList()
|
||||
: new ArrayList<>(Arrays.asList(method.getThrowsList().getReferencedTypes()));
|
||||
final Map<@NotNull String, @NotNull Data> throwTags = new LinkedHashMap<>(totalThrowsToDocument, 1.0f);
|
||||
|
||||
for (int i = thrownTags.length - 1; i > -1; i--) {
|
||||
PsiDocTagValue valueElement = thrownTags[i].getValueElement();
|
||||
for (PsiDocTag tag : throwsJavadocTags) {
|
||||
final PsiDocTagValue value = tag.getValueElement();
|
||||
if (value == null) continue;
|
||||
|
||||
if (valueElement != null) {
|
||||
for (Iterator<PsiClassType> iterator = declaredThrows.iterator(); iterator.hasNext(); ) {
|
||||
PsiClassType classType = iterator.next();
|
||||
if (Comparing.strEqual(valueElement.getText(), classType.getClassName()) ||
|
||||
Comparing.strEqual(valueElement.getText(), classType.getCanonicalText())) {
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
final ASTNode[] children = value.getNode().getChildren(TokenSet.create(JavaDocElementType.DOC_REFERENCE_HOLDER));
|
||||
if (children.length != 1) continue;
|
||||
|
||||
Pair<PsiDocTag, InheritDocProvider<PsiDocTag>> tag = isRendered() ? null
|
||||
: findInheritDocTag(method,
|
||||
exceptionLocator(valueElement.getText()));
|
||||
collectedTags.addFirst(new Pair<>(thrownTags[i], new InheritDocProvider<>() {
|
||||
@Override
|
||||
public Pair<PsiDocTag, InheritDocProvider<PsiDocTag>> getInheritDoc() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiClass getElement() {
|
||||
return method.getContainingClass();
|
||||
}
|
||||
}));
|
||||
final PsiJavaCodeReferenceElement element = ObjectUtils.tryCast(children[0].getFirstChildNode(), PsiJavaCodeReferenceElement.class);
|
||||
if (element != null) {
|
||||
throwTags.put(element.getQualifiedName(), new Data(element, tag));
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiClassType trouser : declaredThrows) {
|
||||
if (trouser != null) {
|
||||
String paramName = trouser.getCanonicalText();
|
||||
Pair<PsiDocTag, InheritDocProvider<PsiDocTag>> parmTag = null;
|
||||
Arrays.stream(methodThrows)
|
||||
.filter(e -> !throwTags.containsKey(e.getQualifiedName()))
|
||||
.forEach(e -> throwTags.put(e.getQualifiedName(), new Data(e, null)));
|
||||
|
||||
for (PsiDocTag localTag : thrownTags) {
|
||||
PsiDocTagValue value = localTag.getValueElement();
|
||||
if (value != null) {
|
||||
String tagName = value.getText();
|
||||
if (tagName != null && areWeakEqual(tagName, paramName)) {
|
||||
parmTag = Pair.create(localTag, ourEmptyProvider);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (throwTags.isEmpty()) return;
|
||||
|
||||
if (parmTag == null) {
|
||||
parmTag = findInheritDocTag(method, exceptionLocator(paramName));
|
||||
}
|
||||
startHeaderSection(buffer, CodeInsightBundle.message("javadoc.throws"));
|
||||
|
||||
if (parmTag != null) {
|
||||
collectedTags.addLast(parmTag);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
PsiDocTag tag = psiFacade.getElementFactory().createDocTagFromText("@exception " + paramName);
|
||||
collectedTags.addLast(Pair.create(tag, ourEmptyProvider));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Data> throwTag : throwTags.entrySet()) {
|
||||
final Data value = throwTag.getValue();
|
||||
final PsiJavaCodeReferenceElement exceptionType = value.ref;
|
||||
final PsiClass target = ObjectUtils.tryCast(exceptionType.resolve(), PsiClass.class);
|
||||
final PsiDocTag tag = value.tag;
|
||||
buffer.append("<p>");
|
||||
|
||||
final PsiElement[] elements = tag != null ? tag.getDataElements(): PsiElement.EMPTY_ARRAY;
|
||||
|
||||
if (target != null) {
|
||||
generateLink(buffer, target);
|
||||
}
|
||||
}
|
||||
|
||||
if (!collectedTags.isEmpty()) {
|
||||
startHeaderSection(buffer, CodeInsightBundle.message("javadoc.throws"));
|
||||
for (Pair<PsiDocTag, InheritDocProvider<PsiDocTag>> tag : collectedTags) {
|
||||
PsiElement[] elements = tag.first.getDataElements();
|
||||
if (elements.length == 0) continue;
|
||||
buffer.append("<p>");
|
||||
String text = elements[0].getText();
|
||||
int index = JavaDocUtil.extractReference(text);
|
||||
String refText = text.substring(0, index).trim();
|
||||
generateLink(buffer, refText, null, method, false);
|
||||
String rest = text.substring(index);
|
||||
if (!rest.isEmpty() || elements.length > 1) buffer.append(" – ");
|
||||
buffer.append(rest);
|
||||
generateValue(buffer, elements, 1, mapProvider(tag.second, true));
|
||||
else if (elements.length != 0) {
|
||||
generateLink(buffer, elements[0].getText(), null, method, false);
|
||||
}
|
||||
buffer.append(DocumentationMarkup.SECTION_END);
|
||||
else {
|
||||
generateUnresolvedLink(buffer, exceptionType);
|
||||
}
|
||||
|
||||
if (elements.length < 2) continue;
|
||||
|
||||
buffer.append(" – ");
|
||||
final Pair<PsiDocTag, InheritDocProvider<PsiDocTag>> tagToInheritDocProvider = findInheritDocTag(method, exceptionLocator(exceptionType.getQualifiedName()));
|
||||
|
||||
generateValue(buffer, elements, 1, tagToInheritDocProvider == null ? null : new InheritDocProvider<>() {
|
||||
@Override
|
||||
public Pair<PsiElement[], InheritDocProvider<PsiElement[]>> getInheritDoc() {
|
||||
final PsiElement[] docElements = tagToInheritDocProvider.first.getDataElements();
|
||||
|
||||
final PsiElement[] result = Arrays.stream(docElements)
|
||||
.skip(1)
|
||||
.toArray(PsiElement[]::new);
|
||||
|
||||
return Pair.pair(result, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiClass getElement() {
|
||||
return tagToInheritDocProvider.getSecond().getElement();
|
||||
}
|
||||
});
|
||||
}
|
||||
buffer.append(DocumentationMarkup.SECTION_END);
|
||||
}
|
||||
|
||||
private void generateUnresolvedLink(StringBuilder buffer, @NotNull PsiJavaCodeReferenceElement exceptionType) {
|
||||
final String label = JavaDocUtil.getLabelText(exceptionType.getProject(),
|
||||
exceptionType.getManager(),
|
||||
exceptionType.getQualifiedName(),
|
||||
exceptionType);
|
||||
appendMaybeUnresolvedLink(buffer, null, label, myProject, false);
|
||||
}
|
||||
|
||||
@Contract(mutates = "param1")
|
||||
private void generateLink(@NotNull StringBuilder buffer, @NotNull PsiClass target) {
|
||||
final String label = JavaDocUtil.getLabelText(target.getProject(), target.getManager(), target.getName(), target);
|
||||
appendMaybeUnresolvedLink(buffer, target, label, target.getProject(), false);
|
||||
}
|
||||
|
||||
private void generateSuperMethodsSection(StringBuilder buffer, PsiMethod method, boolean overrides) {
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><i><span style="color:#808000;">@</span><a href="psi_element://org.jetbrains.annotations.Contract"><code><span style="color:#808000;">Contract</span></code></a><span style="">(</span><span style="">pure</span><span style=""> = </span><span style="color:#000080;font-weight:bold;">true</span><span style="">)</span></i>
|
||||
<span style="color:#000080;font-weight:bold;">boolean</span> <span style="color:#000000;">contains</span><span style="">(</span><br> <a href="psi_element://java.lang.Object"><code><span style="color:#000000;">Object</span></code></a> <span style="">o</span><br><span style="">)</span></pre></div><table class='sections'><p><tr><td valign='top' class='section'><p>From interface:</td><td valign='top'><p><a href="psi_element://java.util.Collection"><code><span style="color:#000000;">java.util.Collection</span></code></a><br>
|
||||
<span style="color:#000080;font-weight:bold;">boolean</span> <span style="color:#000000;">contains</span><span style="">(</span><br> <a href="psi_element://java.lang.Object"><code><span style="color:#000000;">Object</span></code></a> <span style="">o</span><br><span style="">)</span>
|
||||
<span style="color:#000080;font-weight:bold;">throws</span> <font color=red>IOException</font></pre></div><table class='sections'><p><tr><td valign='top' class='section'><p>From interface:</td><td valign='top'><p><a href="psi_element://java.util.Collection"><code><span style="color:#000000;">java.util.Collection</span></code></a><br>
|
||||
Returns <tt>true</tt> if this collection contains the specified element.
|
||||
More formally, returns <tt>true</tt> if and only if this collection
|
||||
contains at least one element <tt>e</tt> such that
|
||||
<tt>(o==null ? e==null : o.equals(e))</tt>.
|
||||
|
||||
</td><tr><td valign='top' class='section'><p>Overrides:</td><td valign='top'><p><a href="psi_element://java.util.Collection#contains(java.lang.Object)"><code><span style="color:#000000;">contains</span></code></a> in interface <a href="psi_element://java.util.Collection"><code><span style="color:#000000;">Collection</span></code></a><br><a href="psi_element://I#contains(java.lang.Object)"><code><span style="color:#000000;">contains</span></code></a> in interface <a href="psi_element://I"><code><span style="color:#000000;">I</span></code></a></td><tr><td valign='top' class='section'><p>Params:</td><td valign='top'><code><span style="">o</span></code> – element whose presence in this collection is to be tested </td><tr><td valign='top' class='section'><p>Returns:</td><td valign='top'><p><tt>true</tt> if this collection contains the specified element </td><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><a href="psi_element://java.lang.ClassCastException"><code><span style="color:#0000ff;">ClassCastException</span></code></a> – if the type of the specified element is incompatible with this collection (<a href="psi_element://My###optional-restrictions">optional</a>) <p><a href="psi_element://java.lang.NullPointerException"><code><span style="color:#0000ff;">NullPointerException</span></code></a> – if the specified element is null and this collection does not permit null elements (<a href="psi_element://My###optional-restrictions">optional</a>)</td><tr><td valign='top' class='section'><p><i>Inferred</i><br> annotations:</td><td valign='top'><p><i><span style="color:#808000;">@</span><a href="psi_element://org.jetbrains.annotations.Contract"><span style="color:#808000;">org.jetbrains.annotations.Contract</span></a><span style="">(</span><span style="">pure</span><span style=""> = </span><span style="color:#000080;font-weight:bold;">true</span><span style="">)</span></i></td></table>
|
||||
</td><tr><td valign='top' class='section'><p>Overrides:</td><td valign='top'><p><a href="psi_element://java.util.Collection#contains(java.lang.Object)"><code><span style="color:#000000;">contains</span></code></a> in interface <a href="psi_element://java.util.Collection"><code><span style="color:#000000;">Collection</span></code></a><br><a href="psi_element://I#contains(java.lang.Object)"><code><span style="color:#000000;">contains</span></code></a> in interface <a href="psi_element://I"><code><span style="color:#000000;">I</span></code></a></td><tr><td valign='top' class='section'><p>Params:</td><td valign='top'><code><span style="">o</span></code> – element whose presence in this collection is to be tested </td><tr><td valign='top' class='section'><p>Returns:</td><td valign='top'><p><tt>true</tt> if this collection contains the specified element </td><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><a href="psi_element://java.lang.NullPointerException"><code><span style="color:#0000ff;">NullPointerException</span></code></a> – before if the specified element is null and this collection does not permit null elements (<a href="psi_element://My###optional-restrictions">optional</a>) after<p><font color=red>IOException</font></td><tr><td valign='top' class='section'><p><i>Inferred</i><br> annotations:</td><td valign='top'><p><i><span style="color:#808000;">@</span><a href="psi_element://org.jetbrains.annotations.Contract"><span style="color:#808000;">org.jetbrains.annotations.Contract</span></a><span style="">(</span><span style="">pure</span><span style=""> = </span><span style="color:#000080;font-weight:bold;">true</span><span style="">)</span></i></td></table>
|
||||
+4
-1
@@ -5,7 +5,10 @@ interface I {
|
||||
boolean contains(Object o);
|
||||
}
|
||||
interface My extends java.util.Collection, I {
|
||||
boolean contains(Object o);
|
||||
/**
|
||||
* @throws NullPointerException before {@inheritDoc} after
|
||||
*/
|
||||
boolean contains(Object o) throws IOException;
|
||||
}
|
||||
class C {
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user