From 5d9760897014c9a2cf0813af3ffbfb358ef55e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Sun, 13 Apr 2025 10:08:39 +0000 Subject: [PATCH] 8254622: Hide superclasses from conditionally exported packages Reviewed-by: kcr, liach --- .../classes/jdk/internal/event/Event.java | 4 +- .../jdk/internal/vm/vector/VectorSupport.java | 13 +- .../formats/html/AbstractMemberWriter.java | 5 +- .../formats/html/AbstractTreeWriter.java | 24 +- .../formats/html/AllClassesIndexWriter.java | 2 +- .../doclets/formats/html/HtmlDoclet.java | 4 +- .../doclets/formats/html/HtmlLinkFactory.java | 2 +- .../doclets/formats/html/MethodWriter.java | 11 +- .../doclets/formats/html/Navigation.java | 5 +- .../doclets/formats/html/PropertyWriter.java | 5 +- .../formats/html/SerializedFormWriter.java | 2 +- .../doclets/formats/html/Signatures.java | 4 +- .../formats/html/taglets/LinkTaglet.java | 3 +- .../doclets/toolkit/util/ClassTree.java | 4 +- .../doclets/toolkit/util/IndexBuilder.java | 4 +- .../internal/doclets/toolkit/util/Utils.java | 63 +++- .../toolkit/util/VisibleMemberTable.java | 4 +- .../doclet/testHiddenTag/TestHiddenTag.java | 12 +- .../doclet/testHiddenTag/pkg1/Child.java | 7 +- .../testHiddenTag/pkg1/InvisibleParent.java | 18 +- .../doclet/testUnexported/TestUnexported.java | 297 ++++++++++++++++++ 21 files changed, 408 insertions(+), 85 deletions(-) create mode 100644 test/langtools/jdk/javadoc/doclet/testUnexported/TestUnexported.java diff --git a/src/java.base/share/classes/jdk/internal/event/Event.java b/src/java.base/share/classes/jdk/internal/event/Event.java index b01bb289a92..4e972dcdab8 100644 --- a/src/java.base/share/classes/jdk/internal/event/Event.java +++ b/src/java.base/share/classes/jdk/internal/event/Event.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,6 @@ package jdk.internal.event; /** * Base class for events, to be subclassed in order to define events and their * fields. - * - * @hidden */ public abstract class Event { /** diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index 6e1c363f3d9..cbf30da2289 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java @@ -157,9 +157,6 @@ public class VectorSupport { public static class VectorSpecies {} - /** - * @hidden - */ public static class VectorPayload { private final Object payload; // array of primitives @@ -172,26 +169,18 @@ public class VectorSupport { } } - /** - * @hidden - */ public static class Vector extends VectorPayload { public Vector(Object payload) { super(payload); } } - /** - * @hidden - */ public static class VectorShuffle extends VectorPayload { public VectorShuffle(Object payload) { super(payload); } } - /** - * @hidden - */ + public static class VectorMask extends VectorPayload { public VectorMask(Object payload) { super(payload); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java index 110a8866725..5c7bb7f3b37 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java @@ -266,15 +266,12 @@ public abstract class AbstractMemberWriter { var inheritedMembersFromMap = asSortedSet(visibleMemberTable.getAllVisibleMembers(kind)); for (TypeElement inheritedClass : visibleMemberTable.getVisibleTypeElements()) { - if (!(utils.isPublic(inheritedClass) || utils.isLinkable(inheritedClass))) { + if (!utils.isVisible(inheritedClass)) { continue; } if (Objects.equals(inheritedClass, typeElement)) { continue; } - if (utils.hasHiddenTag(inheritedClass)) { - continue; - } List members = inheritedMembersFromMap.stream() .filter(e -> Objects.equals(utils.getEnclosingTypeElement(e), inheritedClass)) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java index a6f3ac7dd28..6ad1d2107c6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java @@ -128,22 +128,20 @@ public abstract class AbstractTreeWriter extends HtmlDocletWriter { if (interfaces.size() > (utils.isPlainInterface(typeElement) ? 1 : 0)) { boolean isFirst = true; for (TypeElement intf : interfaces) { - if (parent != intf) { - if (utils.isPublic(intf) || utils.isLinkable(intf)) { - if (isFirst) { - isFirst = false; - if (utils.isPlainInterface(typeElement)) { - content.add(" ("); - content.add(contents.also); - content.add(" extends "); - } else { - content.add(" (implements "); - } + if (parent != intf && utils.isVisible(intf)) { + if (isFirst) { + isFirst = false; + if (utils.isPlainInterface(typeElement)) { + content.add(" ("); + content.add(contents.also); + content.add(" extends "); } else { - content.add(", "); + content.add(" (implements "); } - addPreQualifiedClassLink(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, intf, content); + } else { + content.add(", "); } + addPreQualifiedClassLink(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, intf, content); } } if (!isFirst) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java index ea0ea1f79fe..60c99dfa76a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java @@ -111,7 +111,7 @@ public class AllClassesIndexWriter extends HtmlDocletWriter { boolean noDeprecated = options.noDeprecated(); Set includedTypes = configuration.getIncludedTypeElements(); for (TypeElement typeElement : includedTypes) { - if (utils.hasHiddenTag(typeElement) || !utils.isCoreClass(typeElement)) { + if (utils.isHidden(typeElement) || !utils.isCoreClass(typeElement)) { continue; } if (noDeprecated diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index 46ae80e78bd..b768aee081f 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -409,7 +409,7 @@ public class HtmlDoclet extends AbstractDoclet { protected void generateClassFiles(SortedSet typeElems, ClassTree classTree) throws DocletException { for (TypeElement te : typeElems) { - if (utils.hasHiddenTag(te) || + if (utils.isHidden(te) || !(configuration.isGeneratedDoc(te) && utils.isIncluded(te))) { continue; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java index d404fdaab94..4dbbd5e172a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java @@ -292,7 +292,7 @@ public class HtmlLinkFactory { Content link = new ContentBuilder(); if (utils.isIncluded(typeElement)) { - if (configuration.isGeneratedDoc(typeElement) && !utils.hasHiddenTag(typeElement)) { + if (configuration.isGeneratedDoc(typeElement) && !utils.isHidden(typeElement)) { DocPath fileName = getPath(linkInfo); if (linkInfo.linkToSelf() || typeElement != m_writer.getCurrentTypeElement()) { link.add(m_writer.links.createLink( diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java index 01ce1de2106..2bab8cf6c47 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java @@ -238,12 +238,10 @@ public class MethodWriter extends AbstractExecutableMemberWriter { protected void addComments(TypeMirror holderType, ExecutableElement method, Content methodContent) { TypeElement holder = utils.asTypeElement(holderType); if (!utils.getFullBody(method).isEmpty()) { - if (holder.equals(typeElement) || - !(utils.isPublic(holder) || - utils.isLinkable(holder))) { + if (holder.equals(typeElement) || !utils.isVisible(holder)) { writer.addInlineComment(method, methodContent); } else { - if (!utils.hasHiddenTag(holder) && !utils.hasHiddenTag(method)) { + if (!utils.isHidden(holder) && !utils.isHidden(method)) { Content link = writer.getDocLink(HtmlLinkInfo.Kind.PLAIN, holder, method, @@ -349,7 +347,7 @@ public class MethodWriter extends AbstractExecutableMemberWriter { } Utils utils = writer.utils; TypeElement holder = utils.getEnclosingTypeElement(method); - if (!(utils.isPublic(holder) || utils.isLinkable(holder))) { + if (!utils.isVisible(holder) || utils.isHidden(method)) { //This is an implementation detail that should not be documented. return; } @@ -358,9 +356,6 @@ public class MethodWriter extends AbstractExecutableMemberWriter { //is not visible so don't document this. return; } - if (utils.hasHiddenTag(holder) || utils.hasHiddenTag(method)) { - return; - } Contents contents = writer.contents; Content label; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java index 045eeb0f7a7..61af26259af 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,6 @@ import jdk.javadoc.internal.html.Content; import jdk.javadoc.internal.html.ContentBuilder; import jdk.javadoc.internal.html.Entity; import jdk.javadoc.internal.html.HtmlAttr; -import jdk.javadoc.internal.html.HtmlTag; import jdk.javadoc.internal.html.HtmlTree; import jdk.javadoc.internal.html.Text; @@ -404,7 +403,7 @@ public class Navigation { : Text.of(pkg.getQualifiedName())); // Breadcrumb navigation displays nested classes as separate links. // Enclosing classes may be undocumented, in which case we just display the class name. - case TypeElement type -> (configuration.isGeneratedDoc(type) && !configuration.utils.hasHiddenTag(type)) + case TypeElement type -> (configuration.isGeneratedDoc(type) && !configuration.utils.isHidden(type)) ? links.createLink(pathToRoot.resolve( docPaths.forClass(type)), type.getSimpleName().toString()) : HtmlTree.SPAN(Text.of(type.getSimpleName().toString())); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java index b6b0b064049..8146689016d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java @@ -199,11 +199,10 @@ public class PropertyWriter extends AbstractMemberWriter { protected void addComments(ExecutableElement property, Content propertyContent) { TypeElement holder = (TypeElement)property.getEnclosingElement(); if (!utils.getFullBody(property).isEmpty()) { - if (holder.equals(typeElement) || - (!utils.isPublic(holder) || utils.isLinkable(holder))) { + if (holder.equals(typeElement) || !utils.isVisible(holder)) { writer.addInlineComment(property, propertyContent); } else { - if (!utils.hasHiddenTag(holder) && !utils.hasHiddenTag(property)) { + if (!utils.isHidden(holder) && !utils.isHidden(property)) { Content link = writer.getDocLink(HtmlLinkInfo.Kind.PLAIN, holder, property, diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java index 118f6efd0d6..dbc0c7ceaf3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java @@ -608,7 +608,7 @@ public class SerializedFormWriter extends SubWriterHolderWriter { */ public boolean isVisibleClass(TypeElement typeElement) { return visibleClasses.contains(typeElement) && configuration.isGeneratedDoc(typeElement) - && !utils.hasHiddenTag(typeElement); + && !utils.isHidden(typeElement); } Content getClassHeader(TypeElement typeElement) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java index 2a411050000..8a55d9e075a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -162,7 +162,7 @@ public class Signatures { boolean isFirst = true; for (TypeMirror type : interfaces) { TypeElement tDoc = utils.asTypeElement(type); - if (!(utils.isPublic(tDoc) || utils.isLinkable(tDoc))) { + if (!utils.isVisible(tDoc)) { continue; } if (isFirst) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java index 1f34a466f0d..914f70ced47 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java @@ -248,8 +248,7 @@ public class LinkTaglet extends BaseTaglet { containing = utils.getEnclosingTypeElement(overriddenMethod); } } - if (refSignature.trim().startsWith("#") && - ! (utils.isPublic(containing) || utils.isLinkable(containing))) { + if (refSignature.trim().startsWith("#") && !utils.isVisible(containing)) { // Since the link is relative and the holder is not even being // documented, this must be an inherited link. Redirect it. // The current class either overrides the referenced member or diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/ClassTree.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/ClassTree.java index 7cde2a99453..b7aee2a35f7 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/ClassTree.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/ClassTree.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -233,7 +233,7 @@ public class ClassTree { continue; } - if (utils.hasHiddenTag(te)) { + if (utils.isHidden(te)) { continue; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java index c432fcf46b8..be0eb592574 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -246,7 +246,7 @@ public abstract class IndexBuilder { * Should this element be added to the index? */ private boolean shouldIndex(Element element) { - if (utils.hasHiddenTag(element)) { + if (utils.isHidden(element)) { return false; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java index f82c6e5c0d6..20794160ab5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java @@ -384,7 +384,7 @@ public class Utils { public boolean isUndocumentedEnclosure(TypeElement enclosingTypeElement) { return (isPackagePrivate(enclosingTypeElement) || isPrivate(enclosingTypeElement) - || hasHiddenTag(enclosingTypeElement)) + || isHidden(enclosingTypeElement)) && !isLinkable(enclosingTypeElement); } @@ -786,7 +786,7 @@ public class Utils { if (!visited.add(e)) { continue; // seen it before } - if (isPublic(e) || isLinkable(e)) { + if (isVisible(e)) { results.add(t); } addSuperInterfaces(t, results, visited); @@ -849,7 +849,7 @@ public class Utils { return typeElem != null && ((isIncluded(typeElem) && configuration.isGeneratedDoc(typeElem) && - !hasHiddenTag(typeElem)) || + !isHidden(typeElem)) || (configuration.extern.isExternal(typeElem) && (isPublic(typeElem) || isProtected(typeElem)))); } @@ -874,7 +874,7 @@ public class Utils { return isLinkable((TypeElement) elem); // defer to existing behavior } - if (isIncluded(elem) && !hasHiddenTag(elem)) { + if (isIncluded(elem) && !isHidden(elem)) { return true; } @@ -1006,7 +1006,7 @@ public class Utils { t = supertypes.get(0); // if non-empty, the first element is always the superclass var te = asTypeElement(t); assert alreadySeen.add(te); // it should be the first time we see `te` - if (!hasHiddenTag(te) && (isPublic(te) || isLinkable(te))) { + if (isVisible(te)) { return t; } } @@ -1237,16 +1237,32 @@ public class Utils { } /** - * Returns true if the element is included or selected, contains @hidden tag, - * or if javafx flag is present and element contains @treatAsPrivate - * tag. - * @param e the queried element - * @return true if it exists, false otherwise + * Returns {@code true} if the type element is visible. This means that it is not hidden, + * and is either public or linkable (either internally or externally). + * + * @param typeElement the type element + * @return {@code true} if the type element is visible */ - public boolean hasHiddenTag(Element e) { - // Non-included elements may still be visible via "transclusion" from undocumented enclosures, - // but we don't want to run doclint on them, possibly causing warnings or errors. + public boolean isVisible(TypeElement typeElement) { + return !isHidden(typeElement) && (isPublic(typeElement) || isLinkable(typeElement)); + } + + /** + * Returns true if the element is hidden. An element is hidden if it contains a + * @hidden tag, or if javafx flag is present and the element contains a + * @treatAsPrivate tag, or the element is a type and is not included and + * not exported unconditionally by its module. + * @param e the queried element + * @return true if element is hidden, false otherwise + */ + public boolean isHidden(Element e) { + // Non-included elements may still be visible through the type hierarchy if (!isIncluded(e)) { + // Treat types that are not included and not unconditionally exported as hidden + if (isClassOrInterface(e) && isUnexportedType((TypeElement) e)) { + return true; + } + // Use unchecked method to avoid running doclint, causing warnings or errors. return hasBlockTagUnchecked(e, HIDDEN); } if (options.javafx() && @@ -1256,6 +1272,21 @@ public class Utils { return hasBlockTag(e, DocTree.Kind.HIDDEN); } + /** + * {@return true if typeElement is in a package that is not unconditionally exported + * by its module} + * @param typeElement a type element + */ + private boolean isUnexportedType(TypeElement typeElement) { + var pkg = elementUtils.getPackageOf(typeElement); + var mdl = elementUtils.getModuleOf(typeElement); + return mdl != null && !mdl.isUnnamed() + && mdl.getDirectives().stream() + .filter(d -> d.getKind() == ModuleElement.DirectiveKind.EXPORTS) + .map(d -> (ModuleElement.ExportsDirective) d) + .noneMatch(e -> e.getPackage().equals(pkg) && e.getTargetModules() == null); + } + /* * Returns true if the passed method does not change the specification it * inherited. @@ -1293,14 +1324,14 @@ public class Utils { new TreeSet<>(comparators.generalPurposeComparator()); if (!javafx) { for (TypeElement te : classlist) { - if (!hasHiddenTag(te)) { + if (!isHidden(te)) { filteredOutClasses.add(te); } } return filteredOutClasses; } for (TypeElement e : classlist) { - if (isPrivate(e) || isPackagePrivate(e) || hasHiddenTag(e)) { + if (isPrivate(e) || isPackagePrivate(e) || isHidden(e)) { continue; } filteredOutClasses.add(e); @@ -2831,7 +2862,7 @@ public class Utils { next = null; // end-of-hierarchy break; } - if (isPlainInterface(peek) && !isPublic(peek) && !isLinkable(peek)) { + if (isPlainInterface(peek) && !isVisible(peek)) { // we don't consider such interfaces directly, but may consider // their supertypes (subject to this check for each of them) continue; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberTable.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberTable.java index 70490693bf4..93268943290 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberTable.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -502,7 +502,7 @@ public class VisibleMemberTable { private boolean mustDocument(Element e) { // these checks are ordered in a particular way to avoid parsing unless absolutely necessary - return utils.shouldDocument(e) && !utils.hasHiddenTag(e); + return utils.shouldDocument(e) && !utils.isHidden(e); } private boolean allowInheritedMembers(Element e, Kind kind, LocalMemberTable lmt) { diff --git a/test/langtools/jdk/javadoc/doclet/testHiddenTag/TestHiddenTag.java b/test/langtools/jdk/javadoc/doclet/testHiddenTag/TestHiddenTag.java index 3ffb8b36ba3..88d2788e8da 100644 --- a/test/langtools/jdk/javadoc/doclet/testHiddenTag/TestHiddenTag.java +++ b/test/langtools/jdk/javadoc/doclet/testHiddenTag/TestHiddenTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8073100 8182765 8196202 8261079 8261976 + * @bug 8073100 8182765 8196202 8261079 8261976 8254622 * @summary ensure the hidden tag works as intended * @library ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -139,7 +139,8 @@ public class TestHiddenTag extends JavadocTester { "InvisibleParent.InvisibleInner", "invisibleField", "invisibleMethod", - "invisibleDefaultMethod"); + "invisibleDefaultMethod", + "InvisibleInterface"); checkOutput("pkg1/InvisibleParent.VisibleInner.html", true, """ @@ -151,12 +152,13 @@ public class TestHiddenTag extends JavadocTester { checkOutput("pkg1/package-tree.html", false, "A.InvisibleInner"); - checkOutput("pkg1/package-tree.html", false, "InvisibleParent.html"); + checkOutput("pkg1/package-tree.html", false, "InvisibleParent.html", "InvisibleInterface"); checkFiles(false, "pkg1/A.InvisibleInner.html", "pkg1/A.InvisibleInnerExtendsVisibleInner.html", "pkg1/InvisibleParent.html", - "pkg1/InvisibleParent.InvisibleInner.html"); + "pkg1/InvisibleParent.InvisibleInner.html", + "pkg1/InvisibleParent.InvisibleInterface.html"); } } diff --git a/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/Child.java b/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/Child.java index 12bbb1521b3..7185bc51e80 100644 --- a/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/Child.java +++ b/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/Child.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,10 @@ import pkg2.UndocumentedParent; /** * A visible class, extending invisible classes. + * + * @see #invisibleInterfaceDefaultMethod() + * @see #invisibleInterfaceInterfaceMethod() */ -public class Child extends UndocumentedParent { +public class Child extends UndocumentedParent implements InvisibleParent.InvisibleInterface { } diff --git a/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/InvisibleParent.java b/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/InvisibleParent.java index 7fd1a1daf29..c34ea00939a 100644 --- a/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/InvisibleParent.java +++ b/test/langtools/jdk/javadoc/doclet/testHiddenTag/pkg1/InvisibleParent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,4 +55,20 @@ public abstract class InvisibleParent implements Intf */ public static class InvisibleInner {} + /** + * An invisible interface. + * @hidden + */ + public static interface InvisibleInterface { + /** + * Default method in invisible interface. + */ + default void invisibleInterfaceDefaultMethod() {} + + /** + * Interface method in invisible interface. + */ + void invisibleInterfaceInterfaceMethod(); + } + } diff --git a/test/langtools/jdk/javadoc/doclet/testUnexported/TestUnexported.java b/test/langtools/jdk/javadoc/doclet/testUnexported/TestUnexported.java new file mode 100644 index 00000000000..aae257a0b55 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testUnexported/TestUnexported.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8254622 + * @summary Hide superclasses from conditionally exported packages + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build javadoc.tester.* toolbox.ToolBox + * @run main TestUnexported + */ + +import java.io.IOException; +import java.nio.file.Path; + +import toolbox.ModuleBuilder; +import toolbox.ToolBox; + +import javadoc.tester.JavadocTester; + +public class TestUnexported extends JavadocTester { + + final ToolBox tb; + final Path src; + + public static void main(String... args) throws Exception { + var tester = new TestUnexported(); + tester.runTests(); + } + + TestUnexported() throws IOException { + tb = new ToolBox(); + src = Path.of("src"); + new ModuleBuilder(tb, "ma") + .classes(""" + package pa; + + import pa.internal.*; + + /** + * Class with unexported super types. + */ + public abstract class A extends InternalClass implements InternalInterface {} + """, + """ + package pa.internal; + + /** + * Conditionally exported class. + */ + public class InternalClass { + /** + * Method in internal class. + */ + public void p() {} + } + """, + """ + package pa.internal; + + /** + * Conditionally exported interface. + */ + public interface InternalInterface { + /** + * Method in internal interface. + */ + public void m(); + } + """) + .exports("pa") + .exportsTo("pa.internal", "mb") + .write(src); + + new ModuleBuilder(tb, "mb") + .classes(""" + package pb; + + import pa.internal.*; + + /** + * Class with conditionally exported super types. + */ + public abstract class B extends InternalClass implements InternalInterface {} + """, + """ + package pb; + + import pa.internal.*; + + /** + * Interface with conditionally exported super interface. + */ + public interface I extends InternalInterface {} + """) + .requires("ma") + .exports("pb") + .write(src); + } + + // Types in packages that are exported conditionally are hidden in public API documentation. + @Test + public void test(Path base) throws Exception { + + Path outDir = base.resolve("out"); + + javadoc("-d", outDir.toString(), + "--no-platform-links", + "--module-source-path", src.toString(), + "--show-packages", "exported", + "--module", "ma,mb"); + + checkExit(Exit.OK); + + checkFiles(false, "ma/pa/internal/InternalClass.html", "ma/pa/internal/InternalInterface.html"); + + checkOutput("ma/pa/A.html", false, "InternalInterface", "InternalClass"); + checkOutput("mb/pb/B.html", false, "InternalInterface", "InternalClass"); + checkOutput("mb/pb/I.html", false, "InternalInterface"); + + checkOrder("ma/pa/A.html", """ +
java.lang.Object +
pa.A
+ """, + """ +
public abstract class A + extends java.lang.Object
+ """, + """ +
+

m

+
+
void m()
+
Method in internal interface.
+
+
+ """, + """ +
+

p

+
+
public void p()
+
Method in internal class.
+
+
+ """); + + checkOrder("mb/pb/B.html", """ +
java.lang.Object +
pb.B
+ """, + """ +
public abstract class B + extends java.lang.Object
+ """, + """ +
+

m

+
+
void m()
+
Method in internal interface.
+
+
+ """, + """ +
+

p

+
+
public void p()
+
Method in internal class.
+
+
+ """); + + checkOrder("mb/pb/I.html", """ +
public interface I
+ """, + """ +
+

m

+
+
void m()
+
Method in internal interface.
+
+
+ """); + } + + // Types in packages that are exported conditionally are shown when documenting + // all module packages including internal ones. + @Test + public void testIncluded(Path base) throws Exception { + + Path outDir = base.resolve("out"); + + javadoc("-d", outDir.toString(), + "--no-platform-links", + "--module-source-path", src.toString(), + "--show-module-contents", "all", + "--show-packages", "all", + "--module", "ma,mb"); + + checkExit(Exit.OK); + + checkFiles(true, "ma/pa/internal/InternalClass.html", "ma/pa/internal/InternalInterface.html"); + + checkOutput("ma/pa/A.html", true, "InternalInterface", "InternalClass"); + checkOutput("mb/pb/B.html", true, "InternalInterface", "InternalClass"); + checkOutput("mb/pb/I.html", true, "InternalInterface"); + + checkOrder("ma/pa/A.html", + """ +
java.lang.Object +
pa.internal.InternalClass +
pa.A
+ """, + """ +
public abstract class A + extends InternalClass + implements InternalInterface
+ """, + """ +

Methods inherit\ + ed from class InternalClass

+ p
+ """, + """ +

Methods inh\ + erited from interface InternalInterface

+ m
+ """); + + checkOrder("mb/pb/B.html", + """ +
java.lang.Object +
pa.internal.InternalClass +
pb.B
+ """, + """ +
public abstract class B + extends InternalClass + implements InternalInterface
+ """, + """ +

Methods inherit\ + ed from class InternalClass

+ p
+ """, + """ +

Methods inh\ + erited from interface InternalInterface

+ m
+ """); + + } +}