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

Commit

Permalink
8287379: Using @inheritdoc in an inapplicable context shouldn't crash…
Browse files Browse the repository at this point in the history
… javadoc

Reviewed-by: jjg
  • Loading branch information
pavelrappo committed Jul 11, 2022
1 parent fed3af8 commit 62fbc3f
Show file tree
Hide file tree
Showing 16 changed files with 260 additions and 276 deletions.
Expand Up @@ -1485,7 +1485,7 @@ public ContentBuilder add(CharSequence text) {
};
CommentHelper ch = utils.getCommentHelper(element);
// Array of all possible inline tags for this javadoc run
configuration.tagletManager.checkTags(element, trees, true);
configuration.tagletManager.checkTags(element, trees);
commentRemoved = false;

for (ListIterator<? extends DocTree> iterator = trees.listIterator(); iterator.hasNext();) {
Expand Down
Expand Up @@ -28,9 +28,8 @@
import java.util.EnumSet;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;

import com.sun.source.doctree.DocTree;
import jdk.javadoc.doclet.Taglet.Location;
Expand All @@ -42,17 +41,15 @@
import jdk.javadoc.internal.doclets.toolkit.util.Utils;

/**
* An inline taglet representing the {@code {@inheritDoc}} tag.
* It is used to copy documentation from superclass (but not superinterface)
* declarations and from overridden and implemented methods.
* A taglet that represents the {@code {@inheritDoc}} tag.
*/
public class InheritDocTaglet extends BaseTaglet {

/**
* Construct a new InheritDocTaglet.
*/
public InheritDocTaglet() {
super(DocTree.Kind.INHERIT_DOC, true, EnumSet.of(Location.TYPE, Location.METHOD));
super(DocTree.Kind.INHERIT_DOC, true, EnumSet.of(Location.METHOD));
}

/**
Expand Down Expand Up @@ -96,14 +93,6 @@ private Content retrieveInheritedDocumentation(TagletWriter writer,
inheritedDoc.inlineTags, isFirstSentence);
}
} else {
// This is to assert that we don't reach here for a class declaration.
// Indeed, every class except for java.lang.Object has a superclass.
// If we ever reach here, we would need a different warning; because
// the below warning is about method declarations, not class declarations.
// Unless @inheritDoc is used inside java.lang.Object itself,
// which would clearly be an error, we shouldn't reach here.
assert !(e instanceof TypeElement typeElement)
|| typeElement.getSuperclass().getKind() == TypeKind.NONE;
String signature = utils.getSimpleName(e) +
((utils.isExecutableElement(e))
? utils.flatSignature((ExecutableElement) e, writer.getCurrentPageElement())
Expand All @@ -115,6 +104,9 @@ private Content retrieveInheritedDocumentation(TagletWriter writer,

@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);
}
}
Expand Up @@ -350,16 +350,12 @@ void seenTag(String name) {
}

/**
* Given a series of {@code DocTree}s, check for spelling mistakes.
* Given a series of {@code DocTree}s, check for misuse and spelling mistakes.
*
* @param element the tags holder
* @param trees the trees containing the comments
* @param inlineTrees true if the trees are inline and false otherwise
*/
public void checkTags(Element element, Iterable<? extends DocTree> trees, boolean inlineTrees) {
if (trees == null) {
return;
}
public void checkTags(Element element, Iterable<? extends DocTree> trees) {
CommentHelper ch = utils.getCommentHelper(element);
for (DocTree tag : trees) {
String name = tag.getKind().tagName;
Expand All @@ -386,73 +382,62 @@ public void checkTags(Element element, Iterable<? extends DocTree> trees, boolea
return;
}

if (inlineTrees && !taglet.isInlineTag()) {
printTagMisuseWarn(ch, taglet, tag, "inline");
}

// nothing more to do
if (element == null) {
return;
}

if (!inlineTrees) {
new SimpleElementVisitor14<Void, Void>() {
@Override
public Void visitModule(ModuleElement e, Void p) {
if (!taglet.inModule()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "module");
}
return null;
new SimpleElementVisitor14<Void, Void>() {
@Override
public Void visitModule(ModuleElement e, Void p) {
if (!taglet.inModule()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "module");
}
return null;
}

@Override
public Void visitPackage(PackageElement e, Void p) {
if (!taglet.inPackage()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package");
}
return null;
@Override
public Void visitPackage(PackageElement e, Void p) {
if (!taglet.inPackage()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package");
}
return null;
}

@Override
public Void visitType(TypeElement e, Void p) {
if (!taglet.inType()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class");
}
return null;
@Override
public Void visitType(TypeElement e, Void p) {
if (!taglet.inType()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class");
}
return null;
}

@Override
public Void visitExecutable(ExecutableElement e, Void p) {
if (utils.isConstructor(e) && !taglet.inConstructor()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor");
} else if (!taglet.inMethod()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method");
}
return null;
@Override
public Void visitExecutable(ExecutableElement e, Void p) {
if (utils.isConstructor(e) && !taglet.inConstructor()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor");
} else if (!taglet.inMethod()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method");
}
return null;
}

@Override
public Void visitVariable(VariableElement e, Void p) {
if (utils.isField(e) && !taglet.inField()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field");
}
return null;
@Override
public Void visitVariable(VariableElement e, Void p) {
if (utils.isField(e) && !taglet.inField()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field");
}
return null;
}

@Override
public Void visitUnknown(Element e, Void p) {
if (utils.isOverviewElement(e) && !taglet.inOverview()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview");
}
return null;
@Override
public Void visitUnknown(Element e, Void p) {
if (utils.isOverviewElement(e) && !taglet.inOverview()) {
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview");
}
return null;
}

@Override
protected Void defaultAction(Element e, Void p) {
return null;
}
}.visit(element);
}
@Override
protected Void defaultAction(Element e, Void p) {
return null;
}
}.visit(element);
}
}
}
Expand Down Expand Up @@ -489,22 +474,13 @@ private void printTagMisuseWarn(CommentHelper ch, Taglet taglet, DocTree tag, St
if (taglet.inMethod()) {
locationsSet.add("method");
}
if (taglet.isInlineTag()) {
locationsSet.add("inline text");
}
if (locationsSet.isEmpty()) {
//This known tag is excluded.
return;
}
StringBuilder combined_locations = new StringBuilder();
for (String location: locationsSet) {
if (combined_locations.length() > 0) {
combined_locations.append(", ");
}
combined_locations.append(location);
}
var combined_locations = String.join(", ", locationsSet);
messages.warning(ch.getDocTreePath(tag), "doclet.tag_misuse",
"@" + taglet.getName(), holderType, combined_locations.toString());
"@" + taglet.getName(), holderType, combined_locations);
}

/**
Expand Down
Expand Up @@ -275,8 +275,8 @@ public Content getBlockTagOutput(TagletManager tagletManager,

Content output = getOutputInstance();
Utils utils = configuration().utils;
tagletManager.checkTags(element, utils.getBlockTags(element), false);
tagletManager.checkTags(element, utils.getFullBody(element), true);
tagletManager.checkTags(element, utils.getBlockTags(element));
tagletManager.checkTags(element, utils.getFullBody(element));
for (Taglet taglet : taglets) {
if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) {
// The type parameters and state components are documented in a special
Expand Down
Expand Up @@ -30,7 +30,6 @@
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
Expand Down Expand Up @@ -258,17 +257,6 @@ public static Output search(BaseConfiguration configuration, Input input) {
return output;
}
}
} else if (utils.isTypeElement(input.element)) {
TypeMirror t = ((TypeElement) input.element).getSuperclass();
Element superclass = utils.asTypeElement(t);
if (superclass != null) {
inheritedSearchInput.element = superclass;
output = search(configuration, inheritedSearchInput);
output.isValidInheritDocTag = true;
if (!output.inlineTags.isEmpty()) {
return output;
}
}
}
return output;
}
Expand Down
Expand Up @@ -193,7 +193,7 @@ public Void scan(DocCommentTree tree, TreePath p) {
reportMissing("dc.missing.comment");
}
return null;
} else if (tree.getFirstSentence().isEmpty() && !isOverridingMethod) {
} else if (tree.getFirstSentence().isEmpty() && !isOverridingMethod && !pseudoElement(p)) {
if (tree.getBlockTags().isEmpty()) {
reportMissing("dc.empty.comment");
return null;
Expand Down Expand Up @@ -224,7 +224,7 @@ public Void scan(DocCommentTree tree, TreePath p) {

// this is for html files
// ... if it is a legacy package.html, the doc comment comes after the <h1> page title
// ... otherwise, (e.g. overview file and doc-files/*.html files) no additional headings are inserted
// ... otherwise, (e.g. overview file and doc-files/**/*.html files) no additional headings are inserted
case COMPILATION_UNIT -> fo.isNameCompatible("package", JavaFileObject.Kind.HTML) ? 1 : 0;


Expand Down Expand Up @@ -292,6 +292,13 @@ private boolean isCanonicalRecordConstructor(ExecutableElement ee) {
return true;
}

// Checks if the passed tree path corresponds to an entity, such as
// the overview file and doc-files/**/*.html files.
private boolean pseudoElement(TreePath p) {
return p.getLeaf().getKind() == Tree.Kind.COMPILATION_UNIT
&& p.getCompilationUnit().getSourceFile().getKind() == JavaFileObject.Kind.HTML;
}

private void reportMissing(String code, Object... args) {
env.messages.report(MISSING, Kind.WARNING, env.currPath.getLeaf(), code, args);
}
Expand Down

1 comment on commit 62fbc3f

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