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.
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
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
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
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
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
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);
}
}
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.