Skip to content

Commit

Permalink
8285488: Improve DocFinder
Browse files Browse the repository at this point in the history
8287796: Stop auto-inheriting documentation for subclasses of exceptions whose documentation is inherited
8291869: Match exceptions using types of javax.lang.model, not strings
8288045: Clean up ParamTaglet
8288046: Clean up ThrowsTaglet
8295277: Expand {@inheritdoc} in @throws fully

Reviewed-by: jjg
  • Loading branch information
pavelrappo committed Nov 16, 2022
1 parent 97ab2c3 commit 499406c
Show file tree
Hide file tree
Showing 26 changed files with 1,833 additions and 614 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -811,17 +811,17 @@ public Content getThrowsHeader() {
return HtmlTree.DT(contents.throws_);
}

@Override
public Content throwsTagOutput(Element element, ThrowsTree throwsTag, TypeMirror substituteType) {
@Deprecated(forRemoval = true)
private Content throwsTagOutput(Element element, ThrowsTree throwsTag, TypeMirror substituteType) {
ContentBuilder body = new ContentBuilder();
CommentHelper ch = utils.getCommentHelper(element);
Element exception = ch.getException(throwsTag);
Content excName;
if (substituteType != null) {
excName = htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.MEMBER,
excName = htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.MEMBER,
substituteType));
} else if (exception == null) {
excName = RawHtml.of(throwsTag.getExceptionName().toString());
excName = Text.of(throwsTag.getExceptionName().toString());
} else if (exception.asType() == null) {
excName = Text.of(utils.getFullyQualifiedName(exception));
} else {
Expand All @@ -841,9 +841,16 @@ public Content throwsTagOutput(Element element, ThrowsTree throwsTag, TypeMirror
}

@Override
public Content throwsTagOutput(TypeMirror throwsType) {
return HtmlTree.DD(HtmlTree.CODE(htmlWriter.getLink(
new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.MEMBER, throwsType))));
public Content throwsTagOutput(TypeMirror throwsType, Optional<Content> content) {
var linkInfo = new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.MEMBER, throwsType);
linkInfo.excludeTypeBounds = true;
var link = htmlWriter.getLink(linkInfo);
var concat = new ContentBuilder(HtmlTree.CODE(link));
if (content.isPresent()) {
concat.add(" - ");
concat.add(content.get());
}
return HtmlTree.DD(concat);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ doclet.link.see.no_label=missing reference label
doclet.see.class_or_package_not_found=Tag {0}: reference not found: {1}
doclet.see.class_or_package_not_accessible=Tag {0}: reference not accessible: {1}
doclet.see.nested_link=Tag {0}: nested link
doclet.throws.reference_not_found=cannot find exception type by name
doclet.throws.reference_bad_type=not an exception type: {0}
doclet.tag.invalid_usage=invalid usage of tag {0}
doclet.tag.invalid_input=invalid input: ''{0}''
doclet.tag.invalid=invalid @{0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.lang.model.element.Element;
Expand All @@ -50,6 +51,7 @@
import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
import jdk.javadoc.internal.doclets.toolkit.WriterFactory;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;

Expand Down Expand Up @@ -260,19 +262,18 @@ private void buildSummary(MemberSummaryWriter writer,
if (property != null && member instanceof ExecutableElement ee) {
configuration.cmtUtils.updatePropertyMethodComment(ee, property);
}
List<? extends DocTree> firstSentenceTags = utils.getFirstSentenceTrees(member);
if (utils.isMethod(member) && firstSentenceTags.isEmpty()) {
//Inherit comments from overridden or implemented method if
//necessary.
DocFinder.Output inheritedDoc =
DocFinder.search(configuration,
new DocFinder.Input(utils, member));
if (inheritedDoc.holder != null
&& !utils.getFirstSentenceTrees(inheritedDoc.holder).isEmpty()) {
firstSentenceTags = utils.getFirstSentenceTrees(inheritedDoc.holder);
}
if (utils.isMethod(member)) {
var docFinder = utils.docFinder();
Optional<List<? extends DocTree>> r = docFinder.search((ExecutableElement) member, (m -> {
var firstSentenceTrees = utils.getFirstSentenceTrees(m);
Optional<List<? extends DocTree>> optional = firstSentenceTrees.isEmpty() ? Optional.empty() : Optional.of(firstSentenceTrees);
return Result.fromOptional(optional);
})).toOptional();
// The fact that we use `member` for possibly unrelated tags is suspicious
writer.addMemberSummary(typeElement, member, r.orElse(List.of()));
} else {
writer.addMemberSummary(typeElement, member, utils.getFirstSentenceTrees(member));
}
writer.addMemberSummary(typeElement, member, firstSentenceTags);
}
summaryTreeList.add(writer.getSummaryTable(typeElement));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseOptions;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.DocletException;
import jdk.javadoc.internal.doclets.toolkit.MethodWriter;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;

import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;

Expand Down Expand Up @@ -165,13 +167,10 @@ protected void buildPreviewInfo(Content methodContent) {
protected void buildMethodComments(Content methodContent) {
if (!options.noComment()) {
assert utils.isMethod(currentMethod); // not all executables are methods
ExecutableElement method = currentMethod;
if (utils.getFullBody(currentMethod).isEmpty()) {
DocFinder.Output docs = DocFinder.search(configuration,
new DocFinder.Input(utils, currentMethod));
if (!docs.inlineTags.isEmpty())
method = (ExecutableElement) docs.holder;
}
var docFinder = utils.docFinder();
Optional<ExecutableElement> r = docFinder.search(currentMethod,
m -> Result.fromOptional(utils.getFullBody(m).isEmpty() ? Optional.empty() : Optional.of(m))).toOptional();
ExecutableElement method = r.orElse(currentMethod);
TypeMirror containingType = method.getEnclosingElement().asType();
writer.addComments(containingType, method, methodContent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ doclet.Factory=Factory:
doclet.UnknownTag={0} is an unknown tag.
doclet.UnknownTagLowercase={0} is an unknown tag -- same as a known tag except for case.
doclet.inheritDocWithinInappropriateTag=@inheritDoc cannot be used within this tag
doclet.inheritDocNoDoc=overridden methods do not document exception type {0}
doclet.throwsInheritDocUnsupported=@inheritDoc is not supported for exception-type type parameters \
that are not declared by a method; document such exception types directly
doclet.noInheritedDoc=@inheritDoc used but {0} does not override or implement any method.
doclet.tag_misuse=Tag {0} cannot be used in {1} documentation. It can only be used in the following types of documentation: {2}.
doclet.Package_Summary=Package Summary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
package jdk.javadoc.internal.doclets.toolkit.taglets;

import java.util.EnumSet;
import java.util.List;
import java.util.Optional;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
Expand All @@ -38,6 +40,7 @@
import jdk.javadoc.internal.doclets.toolkit.Messages;
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;

/**
Expand All @@ -61,52 +64,75 @@ public InheritDocTaglet() {
* such tag.</p>
*
* @param writer the writer that is writing the output.
* @param e the {@link Element} that we are documenting.
* @param method the method that we are documenting.
* @param inheritDoc the {@code {@inheritDoc}} tag
* @param isFirstSentence true if we only want to inherit the first sentence
*/
private Content retrieveInheritedDocumentation(TagletWriter writer,
Element e,
ExecutableElement method,
DocTree inheritDoc,
boolean isFirstSentence) {
Content replacement = writer.getOutputInstance();
BaseConfiguration configuration = writer.configuration();
Messages messages = configuration.getMessages();
Utils utils = configuration.utils;
CommentHelper ch = utils.getCommentHelper(e);
CommentHelper ch = utils.getCommentHelper(method);
var path = ch.getDocTreePath(inheritDoc).getParentPath();
DocTree holderTag = path.getLeaf();
Taglet taglet = holderTag.getKind() == DocTree.Kind.DOC_COMMENT
? null
: configuration.tagletManager.getTaglet(ch.getTagName(holderTag));
if (holderTag.getKind() == DocTree.Kind.DOC_COMMENT) {
try {
var docFinder = utils.docFinder();
Optional<Documentation> r = docFinder.trySearch(method,
m -> Result.fromOptional(extractMainDescription(m, isFirstSentence, utils))).toOptional();
if (r.isPresent()) {
replacement = writer.commentTagsToOutput(r.get().method, null,
r.get().mainDescription, isFirstSentence);
}
} catch (DocFinder.NoOverriddenMethodsFound e) {
String signature = utils.getSimpleName(method)
+ utils.flatSignature(method, writer.getCurrentPageElement());
messages.warning(method, "doclet.noInheritedDoc", signature);
}
return replacement;
}

Taglet taglet = configuration.tagletManager.getTaglet(ch.getTagName(holderTag));
if (taglet != null && !(taglet instanceof InheritableTaglet)) {
// This tag does not support inheritance.
messages.warning(path, "doclet.inheritDocWithinInappropriateTag");
return replacement;
}
var input = new DocFinder.Input(utils, e, (InheritableTaglet) taglet,
new DocFinder.DocTreeInfo(holderTag, e), isFirstSentence, true);
DocFinder.Output inheritedDoc = DocFinder.search(configuration, input);
if (inheritedDoc.isValidInheritDocTag) {
if (!inheritedDoc.inlineTags.isEmpty()) {
replacement = writer.commentTagsToOutput(inheritedDoc.holder, inheritedDoc.holderTag,
inheritedDoc.inlineTags, isFirstSentence);

InheritableTaglet.Output inheritedDoc = ((InheritableTaglet) taglet).inherit(method, holderTag, isFirstSentence, configuration);
if (inheritedDoc.isValidInheritDocTag()) {
if (!inheritedDoc.inlineTags().isEmpty()) {
replacement = writer.commentTagsToOutput(inheritedDoc.holder(), inheritedDoc.holderTag(),
inheritedDoc.inlineTags(), isFirstSentence);
}
} else {
String signature = utils.getSimpleName(e) +
((utils.isExecutableElement(e))
? utils.flatSignature((ExecutableElement) e, writer.getCurrentPageElement())
: e.toString());
messages.warning(e, "doclet.noInheritedDoc", signature);
String signature = utils.getSimpleName(method)
+ utils.flatSignature(method, writer.getCurrentPageElement());
messages.warning(method, "doclet.noInheritedDoc", signature);
}
return replacement;
}

private record Documentation(List<? extends DocTree> mainDescription, ExecutableElement method) { }

private static Optional<Documentation> extractMainDescription(ExecutableElement m,
boolean extractFirstSentenceOnly,
Utils utils) {
List<? extends DocTree> docTrees = extractFirstSentenceOnly
? utils.getFirstSentenceTrees(m)
: utils.getFullBody(m);
return docTrees.isEmpty() ? Optional.empty() : Optional.of(new Documentation(docTrees, m));
}

@Override
public Content getInlineTagOutput(Element e, DocTree inheritDoc, TagletWriter tagletWriter) {
if (e.getKind() != ElementKind.METHOD) {
return tagletWriter.getOutputInstance();
}
return retrieveInheritedDocumentation(tagletWriter, e, inheritDoc, tagletWriter.isFirstSentence);
return retrieveInheritedDocumentation(tagletWriter, (ExecutableElement) e, inheritDoc, tagletWriter.isFirstSentence);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,38 @@

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

import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;

import java.util.List;

import javax.lang.model.element.Element;

import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;

/**
* A taglet should implement this interface if it supports an {@code {@inheritDoc}}
* tag or is automatically inherited if it is missing.
*/
public interface InheritableTaglet extends Taglet {

/**
* Given an {@link jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Output}
* object, set its values with the appropriate information to inherit
* documentation.
/*
* Called by InheritDocTaglet on an inheritable taglet to expand {@inheritDoc}
* found inside a tag corresponding to that taglet.
*
* @param input the input for documentation search
* @param output the output for documentation search
* When inheriting failed some assumption, or caused an error, the taglet
* can return either of:
*
* - new Output(null, null, List.of(), false)
* - new Output(null, null, List.of(), true)
*
* In the future, this could be reworked using some other mechanism,
* such as throwing an exception.
*/
void inherit(DocFinder.Input input, DocFinder.Output output);
Output inherit(Element owner, DocTree tag, boolean isFirstSentence, BaseConfiguration configuration);

record Output(DocTree holderTag,
Element holder,
List<? extends DocTree> inlineTags,
boolean isValidInheritDocTag) {
}
}

1 comment on commit 499406c

@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.