Skip to content
This repository has been archived by the owner on Sep 19, 2023. It is now read-only.

Commit

Permalink
6509045: {@inheritdoc} only copies one instance of the specified exce…
Browse files Browse the repository at this point in the history
…ption

Reviewed-by: jjg
  • Loading branch information
pavelrappo committed Jul 6, 2022
1 parent 9a0fa82 commit 8f24d25
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 10 deletions.
Expand Up @@ -25,14 +25,14 @@

package jdk.javadoc.internal.doclets.toolkit.taglets;

import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.lang.model.element.Element;
Expand Down Expand Up @@ -160,34 +160,79 @@ private Content throwsTagsOutput(Map<ThrowsTree, ExecutableElement> throwsTags,
var utils = writer.configuration().utils;
Content result = writer.getOutputInstance();
var documentedInThisCall = new HashSet<String>();
for (Entry<ThrowsTree, ExecutableElement> entry : throwsTags.entrySet()) {
Element e = entry.getValue();
Map<ThrowsTree, ExecutableElement> flattenedExceptions = flatten(throwsTags, writer);
flattenedExceptions.forEach((ThrowsTree t, ExecutableElement e) -> {
var ch = utils.getCommentHelper(e);
ThrowsTree tag = entry.getKey();
Element te = ch.getException(tag);
String excName = tag.getExceptionName().toString();
Element te = ch.getException(t);
String excName = t.getExceptionName().toString();
TypeMirror substituteType = typeSubstitutions.get(excName);
if (alreadyDocumented.contains(excName)
|| (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))
|| (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
continue;
return;
}
if (alreadyDocumented.isEmpty() && documentedInThisCall.isEmpty()) {
result.add(writer.getThrowsHeader());
}
result.add(writer.throwsTagOutput(e, tag, substituteType));
result.add(writer.throwsTagOutput(e, t, substituteType));
if (substituteType != null) {
documentedInThisCall.add(substituteType.toString());
} else {
documentedInThisCall.add(te != null
? utils.getFullyQualifiedName(te, false)
: excName);
}
}
});
alreadyDocumented.addAll(documentedInThisCall);
return result;
}

/*
* A single @throws tag from an overriding method can correspond to multiple
* @throws tags from an overridden method.
*/
private Map<ThrowsTree, ExecutableElement> flatten(Map<ThrowsTree, ExecutableElement> throwsTags,
TagletWriter writer) {
Map<ThrowsTree, ExecutableElement> result = new LinkedHashMap<>();
throwsTags.forEach((tag, taggedElement) -> {
var expandedTags = expand(tag, taggedElement, writer);
assert Collections.disjoint(result.entrySet(), expandedTags.entrySet());
result.putAll(expandedTags);
});
return result;
}

private Map<ThrowsTree, ExecutableElement> expand(ThrowsTree tag,
ExecutableElement e,
TagletWriter writer) {

// This method uses Map.of() to create maps of size zero and one.
// While such maps are effectively ordered, the syntax is more
// compact than that of LinkedHashMap.

// peek into @throws description
if (tag.getDescription().stream().noneMatch(d -> d.getKind() == DocTree.Kind.INHERIT_DOC)) {
// nothing to inherit
return Map.of(tag, e);
}
var input = new DocFinder.Input(writer.configuration().utils, e, this, new DocFinder.DocTreeInfo(tag, e), false, true);
var output = DocFinder.search(writer.configuration(), input);
if (output.tagList.size() <= 1) {
// outer code will handle this trivial case of inheritance
return Map.of(tag, e);
}
if (tag.getDescription().size() > 1) {
// there's more to description than just {@inheritDoc}
// it's likely a documentation error
var ch = writer.configuration().utils.getCommentHelper(e);
writer.configuration().getMessages().error(ch.getDocTreePath(tag), "doclet.inheritDocWithinInappropriateTag");
return Map.of();
}
Map<ThrowsTree, ExecutableElement> tags = new LinkedHashMap<>();
output.tagList.forEach(t -> tags.put((ThrowsTree) t, (ExecutableElement) output.holder));
return tags;
}

/**
* Inherit throws documentation for exceptions that were declared but not
* documented.
Expand Down
Expand Up @@ -23,7 +23,7 @@

/*
* @test
* @bug 8067757
* @bug 8067757 6509045
* @library /tools/lib ../../lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
Expand Down Expand Up @@ -350,4 +350,218 @@ public interface I1 extends I {
<dd><code><a href="MySubException.html" title="class in x">MySubException</a></code> - if that</dd>
</dl>""");
}

@Test
public void testUncheckedExceptionTag(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException if this
* @throws MyRuntimeException if that
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
void m();
}
""", """
package x;
public class IImpl implements I {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
public void m() { }
}
""", """
package x;
public class C {
/**
* @throws MyRuntimeException if this
* @throws MyRuntimeException if that
*/
public void m();
}
""", """
package x;
public class C1 extends C {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
public void m() { }
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.OK);
checkOutput("x/IImpl.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
checkOutput("x/I1.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
checkOutput("x/C1.html", true, """
<dl class="notes">
<dt>Overrides:</dt>
<dd><code><a href="C.html#m()">m</a></code>&nbsp;in class&nbsp;<code><a href="C.html" title="class in x">C</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
}

@Test
public void testWholeShebang(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException always
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException sometimes
* @throws MyRuntimeException rarely
* @throws MyRuntimeException "{@inheritDoc}"
*/
@Override
void m();
}
""", """
package x;
public interface I2 extends I1 {
/**
* @throws MyRuntimeException occasionally
* @throws MyRuntimeException {@inheritDoc}
* @throws MyRuntimeException frequently
*/
@Override
void m() throws MyRuntimeException,
MyRuntimeException,
MyRuntimeException,
MyRuntimeException;
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.OK);
checkOutput("x/I.html", true, """
<dl class="notes">
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - always</dd>
</dl>""");
checkOutput("x/I1.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - sometimes</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - rarely</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - "always"</dd>
</dl>""");
checkOutput("x/I2.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Specified by:</dt>
<dd><code><a href="I1.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I1.html" title="interface in x">I1</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - occasionally</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - sometimes</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - rarely</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - "always"</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - frequently</dd>
</dl>""");
}

@Test
public void testError(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException sometimes
* @throws MyRuntimeException rarely
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException "{@inheritDoc}"
*/
@Override
void m();
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.ERROR);
checkOutput(Output.OUT, true, """
I1.java:6: error: @inheritDoc cannot be used within this tag
* @throws MyRuntimeException "{@inheritDoc}"
^
""");
}
}

1 comment on commit 8f24d25

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.