Java: show duplicate throws tags in Javadoc (IDEA-345275)

GitOrigin-RevId: 8487e9cdbea877c548500b9718f3de1ddd02f322
This commit is contained in:
Bas Leijdekkers
2024-08-22 15:27:14 +00:00
committed by intellij-monorepo-bot
parent 1a5e213e96
commit affc8566c8
4 changed files with 41 additions and 85 deletions
@@ -53,7 +53,6 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.javadoc.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.*;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
@@ -62,7 +61,6 @@ import com.intellij.ui.ColorUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.util.XmlStringUtil;
import kotlin.text.StringsKt;
@@ -426,7 +424,7 @@ public class JavaDocInfoGenerator {
LOG.debug(e);
result.replace(result.length() - 6, result.length(), "wrong-href=\""); // display text instead of link
}
result.append(ObjectUtils.notNull(reference, href));
result.append(reference == null ? href : reference);
lastRef = groupEnd;
}
}
@@ -594,17 +592,13 @@ public class JavaDocInfoGenerator {
if (generatePrologue) generatePrologue(buffer);
generateVariableDefinition(buffer, recordComponent, true);
PsiRecordHeader recordHeader = ObjectUtils.tryCast(recordComponent.getParent(), PsiRecordHeader.class);
if (recordHeader == null) return;
PsiRecordComponent[] components = recordHeader.getRecordComponents();
int recordIndex = ArrayUtil.indexOf(components, recordComponent);
if (!(recordComponent.getParent() instanceof PsiRecordHeader recordHeader)) return;
int recordIndex = ArrayUtil.indexOf(recordHeader.getRecordComponents(), recordComponent);
PsiClass recordClass = recordComponent.getContainingClass();
if (recordClass == null) return;
String recordComponentJavadoc = getRecordComponentJavadocFromParameterTag(recordIndex, recordClass);
if (recordComponentJavadoc != null) {
buffer.append(DocumentationMarkup.CONTENT_START);
buffer.append(recordComponentJavadoc);
buffer.append(DocumentationMarkup.CONTENT_END);
buffer.append(DocumentationMarkup.CONTENT_START).append(recordComponentJavadoc).append(DocumentationMarkup.CONTENT_END);
}
}
@@ -614,13 +608,10 @@ public class JavaDocInfoGenerator {
String docForPattern = getDocForPattern(variable);
if (docForPattern != null) {
buffer.append(DocumentationMarkup.CONTENT_START);
buffer.append(docForPattern);
buffer.append(DocumentationMarkup.CONTENT_END);
buffer.append(DocumentationMarkup.CONTENT_START).append(docForPattern).append(DocumentationMarkup.CONTENT_END);
}
}
private @Nullable String getDocForPattern(@NotNull PsiPatternVariable variable) {
PsiPattern pattern = variable.getPattern();
PsiElement parent = pattern.getParent();
@@ -2631,8 +2622,7 @@ public class JavaDocInfoGenerator {
}
if (pair != null) {
startHeaderSection(buffer, CodeInsightBundle.message("javadoc.returns"))
.append("<p>");
startHeaderSection(buffer, CodeInsightBundle.message("javadoc.returns")).append("<p>");
generateValue(buffer, pair.first.getDataElements(), mapProvider(pair.second, false));
buffer.append(DocumentationMarkup.SECTION_END);
}
@@ -2651,77 +2641,36 @@ public class JavaDocInfoGenerator {
private void generateThrowsSection(StringBuilder buffer, PsiMethod method, PsiDocComment comment) {
final PsiDocTag[] throwsJavadocTags = getThrowsTags(comment);
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;
}
}
final Map<@NotNull String, @NotNull Data> throwTags = new LinkedHashMap<>(totalThrowsToDocument, 1.0f);
for (PsiDocTag tag : throwsJavadocTags) {
final PsiDocTagValue value = tag.getValueElement();
if (value == null) continue;
final ASTNode[] children = value.getNode().getChildren(TokenSet.create(JavaDocElementType.DOC_REFERENCE_HOLDER));
if (children.length != 1) continue;
final PsiJavaCodeReferenceElement element = ObjectUtils.tryCast(children[0].getFirstChildNode(), PsiJavaCodeReferenceElement.class);
if (element != null) {
throwTags.put(element.getQualifiedName(), new Data(element, tag));
}
}
Arrays.stream(methodThrows)
.filter(e -> !throwTags.containsKey(e.getQualifiedName()))
.forEach(e -> throwTags.put(e.getQualifiedName(), new Data(e, null)));
if (throwTags.isEmpty()) return;
final PsiJavaCodeReferenceElement[] methodThrows = isRendered()
? PsiJavaCodeReferenceElement.EMPTY_ARRAY
: method.getThrowsList().getReferenceElements();
if (throwsJavadocTags.length + methodThrows.length == 0) return;
startHeaderSection(buffer, CodeInsightBundle.message("javadoc.throws"));
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;
Set<String> documentedExceptions = new HashSet<>(throwsJavadocTags.length);
for (PsiDocTag tag : throwsJavadocTags) {
buffer.append("<p>");
final PsiElement[] elements = tag != null ? tag.getDataElements() : PsiElement.EMPTY_ARRAY;
if (target != null) {
PsiElement[] dataElements = tag.getDataElements();
if (dataElements.length == 0) continue;
PsiElement grandChild = dataElements[0].getFirstChild().getFirstChild();
if (!(grandChild instanceof PsiJavaCodeReferenceElement reference)) continue;
if (reference.resolve() instanceof PsiClass target) {
generateLink(buffer, target);
}
else if (elements.length != 0) {
generateLink(buffer, elements[0].getText(), null, method, false);
}
else {
generateUnresolvedLink(buffer, exceptionType);
generateLink(buffer, dataElements[0].getText(), null, method, false);
}
if (elements.length < 2) continue;
documentedExceptions.add(reference.getQualifiedName());
if (dataElements.length < 2) continue;
buffer.append(" &ndash; ");
final Pair<PsiDocTag, InheritDocProvider<PsiDocTag>> tagToInheritDocProvider =
findInheritDocTag(method, exceptionLocator(exceptionType.getQualifiedName()));
findInheritDocTag(method, exceptionLocator(reference.getQualifiedName()));
generateValue(buffer, elements, 1, tagToInheritDocProvider == null ? null : new InheritDocProvider<>() {
generateValue(buffer, dataElements, 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)
final PsiElement[] result = Arrays.stream(tagToInheritDocProvider.first.getDataElements())
.skip(1)
.toArray(PsiElement[]::new);
@@ -2734,25 +2683,22 @@ public class JavaDocInfoGenerator {
}
});
}
for (PsiJavaCodeReferenceElement exception : methodThrows) {
if (documentedExceptions.contains(exception.getQualifiedName())) continue;
buffer.append("<p>");
if (exception.resolve() instanceof PsiClass target) generateLink(buffer, target);
else generateLink(buffer, exception.getText(), null, method, false);
}
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 static @Nullable @NlsSafe String generateLink(@NotNull PsiElement element,
String label) {
private static @Nullable @NlsSafe String generateLink(@NotNull PsiElement element, String label) {
String refText = JavaDocUtil.getReferenceText(element.getProject(), element);
if (refText != null) {
StringBuilder linkBuilder = new StringBuilder();
@@ -0,0 +1,2 @@
<html><head><base href="placeholder"></head><body><div class="bottom"><icon src="AllIcons.Nodes.Class">&nbsp;<a href="psi_element://A"><code><span style="color:#000000;">A</span></code></a></div><div class='definition'><pre><span style="color:#000080;font-weight:bold;">public</span>&nbsp;<span style="color:#000080;font-weight:bold;">void</span>&nbsp;<span style="color:#000000;">method</span><span style="">(</span><br> <span style="color:#000080;font-weight:bold;">int</span>&nbsp;<span style="color:#000000;">a</span><span style="">,</span>
<span style="color:#000080;font-weight:bold;">int</span>&nbsp;<span style="color:#000000;">b</span><br><span style="">)</span></pre></div><table class='sections'><p><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><a href="psi_element://java.lang.IllegalArgumentException"><code><span style="color:#0000ff;">IllegalArgumentException</span></code></a> &ndash; if a is negative <p><a href="psi_element://java.lang.IllegalArgumentException"><code><span style="color:#0000ff;">IllegalArgumentException</span></code></a> &ndash; if b is negative</td></table>
@@ -0,0 +1,7 @@
class A {
/**
* @throws java.lang.IllegalArgumentException if a is negative
* @throws java.lang.IllegalArgumentException if b is negative
*/
public void method(int a, int b) {}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// 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.java.codeInsight.javadoc;
import com.intellij.JavaTestUtil;
@@ -81,6 +81,7 @@ public class JavaDocInfoGeneratorTest extends JavaCodeInsightTestCase {
public void testMethodTypeParameter3() { doTestAtCaret(); }
public void testInheritedDocInThrows() { doTestMethod(); }
public void testInheritedDocInThrows1() { doTestMethod(); }
public void testMultipleThrowsSameType() { doTestMethod(); }
public void testEscapeValues() { doTestClass(); }
public void testClassTypeParameter() { doTestClass(); }
public void testClassTypeParameter1() { doTestClass(); }