From 4a55b0067967b713958e80f90f7ae749449cb9f0 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 8 May 2025 14:23:36 +0100 Subject: [PATCH 01/12] merge methods from invisible supetypes --- .../toolkit/util/VisibleMemberTable.java | 11 +- .../TestDuplicateMethodsWarn.java | 105 ++++++++++++++++++ 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java 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 93268943290b8..9cd8c287cd3f2 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 @@ -689,10 +689,15 @@ private boolean allowInheritedMethod(ExecutableElement inheritedMethod, if (inInterface) { List list = overriddenByTable.get(inheritedMethod); if (list != null) { - boolean found = list.stream() - .anyMatch(this::isDeclaredInInterface); - if (found) + // If any interface in the hierarchy re-declares it, drop the older copy + if (list.stream().anyMatch(this::isDeclaredInInterface)) { return false; + } + // If any class in the hierarchy has provided an implementation, + // drop the interface copy entirely. we'll inherit from the class + if (list.stream().anyMatch(m -> !isDeclaredInInterface(m))) { + return false; + } } } diff --git a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java new file mode 100644 index 0000000000000..f43d8e03e2759 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java @@ -0,0 +1,105 @@ +/* + * 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 8177100 + * @summary Test to check for duplicate methods + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestDuplicateMethodsWarn + */ + +import java.io.IOException; +import java.nio.file.Path; + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +public class TestDuplicateMethodsWarn extends JavadocTester { + + public static void main(String... args) throws Exception { + var tester = new TestDuplicateMethodsWarn(); + tester.runTests(); + } + + ToolBox tb = new ToolBox(); + Path src = Path.of("src"); + + + TestDuplicateMethodsWarn() throws IOException { + tb.writeJavaFiles(src, """ + package p; + interface A { + /** + * JavaDoc for method in class A. + */ + abstract void testA ( ); + }""",""" + package p; + interface B extends A { + /** + * JavaDoc for method in class B. + */ + abstract void testB ( ); + }""", """ + package p; + abstract class C implements A { + /** + * Inherited JavaDoc for method in class C. + */ + public final void testA ( ) { + // Do nothing. + } + }""",""" + package p; + public final class D extends C implements B { + /** + * Inherited JavaDoc + */ + public final void testB ( ) { + // Do nothing. + } + } + """); + + } + + @Test + public void testDuplicateMethodWarning(Path base) { + javadoc("-d", base.resolve("out").toString(), + "-sourcepath", src.toString(), + "p"); + checkExit(Exit.OK); + checkOutput("p/D.html", true, """ +
Inherited JavaDoc for method in class C.
""",""" +
+
public final void testA()
+
Inherited JavaDoc for method in class C.
"""); + checkOutput("p/D.html", false, """ +
JavaDoc for method in class A.
""", """ +
void testA()
+
JavaDoc for method in class A.
"""); + } +} From 41578ad2f0e7cd9e414e08582863125127df59c3 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 8 May 2025 17:16:16 +0100 Subject: [PATCH 02/12] simplify check --- .../toolkit/util/VisibleMemberTable.java | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) 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 9cd8c287cd3f2..ec7c4269b54fb 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 @@ -678,27 +678,16 @@ private boolean allowInheritedMethod(ExecutableElement inheritedMethod, return false; } - // Multiple-Inheritance: remove the interface method that may have - // been overridden by another interface method in the hierarchy - // - // Note: The following approach is very simplistic and is compatible - // with old VMM. A future enhancement, may include a contention breaker, - // to correctly eliminate those methods that are merely definitions - // in favor of concrete overriding methods, for instance those that have - // API documentation and are not abstract OR default methods. + // Multiple-Inheritance: No Contention. In Java’s method resolution, + // any override of a signature (whether by a subclass or by a subinterface, + // including when it is final from superclasses) always takes precedence + // over the original interface definition. All interface methods have low resolution priority. + // Therefore, when considering an interface inherited method, as soon as + // at least one overrider exists in the inheritance chain, + // we do not inherit the older interface definition. if (inInterface) { List list = overriddenByTable.get(inheritedMethod); - if (list != null) { - // If any interface in the hierarchy re-declares it, drop the older copy - if (list.stream().anyMatch(this::isDeclaredInInterface)) { - return false; - } - // If any class in the hierarchy has provided an implementation, - // drop the interface copy entirely. we'll inherit from the class - if (list.stream().anyMatch(m -> !isDeclaredInInterface(m))) { - return false; - } - } + if (list != null && !list.isEmpty()) return false; } Elements elementUtils = config.docEnv.getElementUtils(); From 8d300463deefffc3f2404e8af54d3def1acdb082 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 9 May 2025 14:24:28 +0100 Subject: [PATCH 03/12] fix comment --- .../internal/doclets/toolkit/util/VisibleMemberTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ec7c4269b54fb..58f010fb967ca 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 @@ -678,7 +678,7 @@ private boolean allowInheritedMethod(ExecutableElement inheritedMethod, return false; } - // Multiple-Inheritance: No Contention. In Java’s method resolution, + // Multiple-Inheritance: No Contention. In Java's method resolution, // any override of a signature (whether by a subclass or by a subinterface, // including when it is final from superclasses) always takes precedence // over the original interface definition. All interface methods have low resolution priority. From d630e6a55ad31e5380d5e56a92eb8c7a33f51953 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 9 May 2025 15:32:06 +0100 Subject: [PATCH 04/12] revert VisibleMemberTable back to master --- .../toolkit/util/VisibleMemberTable.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) 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 58f010fb967ca..93268943290b8 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 @@ -678,16 +678,22 @@ private boolean allowInheritedMethod(ExecutableElement inheritedMethod, return false; } - // Multiple-Inheritance: No Contention. In Java's method resolution, - // any override of a signature (whether by a subclass or by a subinterface, - // including when it is final from superclasses) always takes precedence - // over the original interface definition. All interface methods have low resolution priority. - // Therefore, when considering an interface inherited method, as soon as - // at least one overrider exists in the inheritance chain, - // we do not inherit the older interface definition. + // Multiple-Inheritance: remove the interface method that may have + // been overridden by another interface method in the hierarchy + // + // Note: The following approach is very simplistic and is compatible + // with old VMM. A future enhancement, may include a contention breaker, + // to correctly eliminate those methods that are merely definitions + // in favor of concrete overriding methods, for instance those that have + // API documentation and are not abstract OR default methods. if (inInterface) { List list = overriddenByTable.get(inheritedMethod); - if (list != null && !list.isEmpty()) return false; + if (list != null) { + boolean found = list.stream() + .anyMatch(this::isDeclaredInInterface); + if (found) + return false; + } } Elements elementUtils = config.docEnv.getElementUtils(); From d9ef1829b5300ebdb28a5c8106eff2c9ae62de1a Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Sun, 11 May 2025 17:41:48 +0100 Subject: [PATCH 05/12] new approach - make sure there is no negative effect on JDK doc output --- .../toolkit/util/VisibleMemberTable.java | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) 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 93268943290b8..3701d9c5d0379 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 @@ -262,6 +262,10 @@ public List getVisibleMembers(Kind kind, Predicate p) { * Returns a list of all enclosed members including any extra members. * Typically called by various builders. * + * For methods, any interface method coming from an undocumented supertype + * will be removed if this class (or one of its superclasses) provides + * an override. + * * @param kind the member kind * @return a list of visible enclosed members */ @@ -270,7 +274,45 @@ public List getVisibleMembers(Kind kind) { TypeElement encl = utils.getEnclosingTypeElement(e); return Objects.equals(encl, te) || utils.isUndocumentedEnclosure(encl); }; - return getVisibleMembers(kind, declaredAndLeafMembers); + List members = getVisibleMembers(kind, declaredAndLeafMembers); + + if (kind == Kind.METHODS) { + members = removeDuplicates(members); + } + + return members; + } + + /** + * Drop any that were inlined from an undocumented interface + * and that have a local override in this class hierarchy. + */ + private List removeDuplicates(List methods) { + List localMethods = getMembers(Kind.METHODS).stream() + .map(ExecutableElement.class::cast) + .toList(); + + return methods.stream() + .filter(e -> shouldKeepInheritedMethod(e, localMethods)) + .toList(); + } + + private boolean shouldKeepInheritedMethod(Element e, List localMethods) { + TypeElement encl = utils.getEnclosingTypeElement(e); + + boolean isHiddenInterfaceMethod = + !Objects.equals(encl, te) && utils.isUndocumentedEnclosure(encl) + && e instanceof ExecutableElement; + + if (isHiddenInterfaceMethod) { + ExecutableElement inherited = (ExecutableElement) e; + for (var local : localMethods) { + if (utils.elementUtils.overrides(local, inherited, te)) { + return false; + } + } + } + return true; } /** From c7b626982f019d182394c9e8b4e57d6e4b843e87 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Mon, 12 May 2025 13:52:34 +0100 Subject: [PATCH 06/12] update javadoc --- .../internal/doclets/toolkit/util/VisibleMemberTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 3701d9c5d0379..bbc3257cd281c 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 @@ -284,8 +284,8 @@ public List getVisibleMembers(Kind kind) { } /** - * Drop any that were inlined from an undocumented interface - * and that have a local override in this class hierarchy. + * Drop any methods that were inlined from an undocumented interface + * and have a local override in this class hierarchy. */ private List removeDuplicates(List methods) { List localMethods = getMembers(Kind.METHODS).stream() From 4c645c7bb272b63c13971e803afff2cd40cf08c5 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Mon, 12 May 2025 15:39:24 +0100 Subject: [PATCH 07/12] rename test to be more accurate and simplify code --- .../toolkit/util/VisibleMemberTable.java | 29 +++++++------------ ...odsWarn.java => TestDuplicateMethods.java} | 8 ++--- 2 files changed, 14 insertions(+), 23 deletions(-) rename test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/{TestDuplicateMethodsWarn.java => TestDuplicateMethods.java} (94%) 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 bbc3257cd281c..3056995bb5462 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 @@ -56,6 +56,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; @@ -288,31 +289,21 @@ public List getVisibleMembers(Kind kind) { * and have a local override in this class hierarchy. */ private List removeDuplicates(List methods) { - List localMethods = getMembers(Kind.METHODS).stream() - .map(ExecutableElement.class::cast) - .toList(); + Set localMethods = overriddenMethodTable.values().stream() + .map(info -> info.overriddenMethod) + .collect(Collectors.toSet()); return methods.stream() .filter(e -> shouldKeepInheritedMethod(e, localMethods)) .toList(); } - private boolean shouldKeepInheritedMethod(Element e, List localMethods) { - TypeElement encl = utils.getEnclosingTypeElement(e); - - boolean isHiddenInterfaceMethod = - !Objects.equals(encl, te) && utils.isUndocumentedEnclosure(encl) - && e instanceof ExecutableElement; - - if (isHiddenInterfaceMethod) { - ExecutableElement inherited = (ExecutableElement) e; - for (var local : localMethods) { - if (utils.elementUtils.overrides(local, inherited, te)) { - return false; - } - } - } - return true; + private boolean shouldKeepInheritedMethod(Element e, Set overridden) { + if (!(e instanceof ExecutableElement ee)) return true; + TypeElement encl = utils.getEnclosingTypeElement(ee); + boolean hiddenInterface = !Objects.equals(encl, te) + && utils.isUndocumentedEnclosure(encl); + return !hiddenInterface || !overridden.contains(ee); } /** diff --git a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java similarity index 94% rename from test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java rename to test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java index f43d8e03e2759..2048050514e39 100644 --- a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethodsWarn.java +++ b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java @@ -28,7 +28,7 @@ * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* - * @run main TestDuplicateMethodsWarn + * @run main TestDuplicateMethods */ import java.io.IOException; @@ -37,10 +37,10 @@ import javadoc.tester.JavadocTester; import toolbox.ToolBox; -public class TestDuplicateMethodsWarn extends JavadocTester { +public class TestDuplicateMethods extends JavadocTester { public static void main(String... args) throws Exception { - var tester = new TestDuplicateMethodsWarn(); + var tester = new TestDuplicateMethods(); tester.runTests(); } @@ -48,7 +48,7 @@ public static void main(String... args) throws Exception { Path src = Path.of("src"); - TestDuplicateMethodsWarn() throws IOException { + TestDuplicateMethods() throws IOException { tb.writeJavaFiles(src, """ package p; interface A { From 23ce62f15a3589a6eef762675bd76f34e2e328ac Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Mon, 2 Jun 2025 13:19:16 +0100 Subject: [PATCH 08/12] revert back to master --- .../toolkit/util/VisibleMemberTable.java | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) 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 3056995bb5462..93268943290b8 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 @@ -56,7 +56,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; @@ -263,10 +262,6 @@ public List getVisibleMembers(Kind kind, Predicate p) { * Returns a list of all enclosed members including any extra members. * Typically called by various builders. * - * For methods, any interface method coming from an undocumented supertype - * will be removed if this class (or one of its superclasses) provides - * an override. - * * @param kind the member kind * @return a list of visible enclosed members */ @@ -275,35 +270,7 @@ public List getVisibleMembers(Kind kind) { TypeElement encl = utils.getEnclosingTypeElement(e); return Objects.equals(encl, te) || utils.isUndocumentedEnclosure(encl); }; - List members = getVisibleMembers(kind, declaredAndLeafMembers); - - if (kind == Kind.METHODS) { - members = removeDuplicates(members); - } - - return members; - } - - /** - * Drop any methods that were inlined from an undocumented interface - * and have a local override in this class hierarchy. - */ - private List removeDuplicates(List methods) { - Set localMethods = overriddenMethodTable.values().stream() - .map(info -> info.overriddenMethod) - .collect(Collectors.toSet()); - - return methods.stream() - .filter(e -> shouldKeepInheritedMethod(e, localMethods)) - .toList(); - } - - private boolean shouldKeepInheritedMethod(Element e, Set overridden) { - if (!(e instanceof ExecutableElement ee)) return true; - TypeElement encl = utils.getEnclosingTypeElement(ee); - boolean hiddenInterface = !Objects.equals(encl, te) - && utils.isUndocumentedEnclosure(encl); - return !hiddenInterface || !overridden.contains(ee); + return getVisibleMembers(kind, declaredAndLeafMembers); } /** From b53817dfdacf6e3f694462792e4ec2650da5c8a3 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Mon, 2 Jun 2025 13:22:21 +0100 Subject: [PATCH 09/12] revert back to simple earlier fix --- .../toolkit/util/VisibleMemberTable.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) 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 93268943290b8..58f010fb967ca 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 @@ -678,22 +678,16 @@ private boolean allowInheritedMethod(ExecutableElement inheritedMethod, return false; } - // Multiple-Inheritance: remove the interface method that may have - // been overridden by another interface method in the hierarchy - // - // Note: The following approach is very simplistic and is compatible - // with old VMM. A future enhancement, may include a contention breaker, - // to correctly eliminate those methods that are merely definitions - // in favor of concrete overriding methods, for instance those that have - // API documentation and are not abstract OR default methods. + // Multiple-Inheritance: No Contention. In Java's method resolution, + // any override of a signature (whether by a subclass or by a subinterface, + // including when it is final from superclasses) always takes precedence + // over the original interface definition. All interface methods have low resolution priority. + // Therefore, when considering an interface inherited method, as soon as + // at least one overrider exists in the inheritance chain, + // we do not inherit the older interface definition. if (inInterface) { List list = overriddenByTable.get(inheritedMethod); - if (list != null) { - boolean found = list.stream() - .anyMatch(this::isDeclaredInInterface); - if (found) - return false; - } + if (list != null && !list.isEmpty()) return false; } Elements elementUtils = config.docEnv.getElementUtils(); From caeb20adab65612a2d89e00bd7552689c9616253 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Tue, 3 Jun 2025 15:57:45 +0100 Subject: [PATCH 10/12] Feedback from @hns - Expand test with new test cases --- .../TestDuplicateMethods.java | 165 ++++++++++++++---- 1 file changed, 131 insertions(+), 34 deletions(-) diff --git a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java index 2048050514e39..2774a0ff40a8c 100644 --- a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java +++ b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java @@ -24,7 +24,7 @@ /* * @test * @bug 8177100 - * @summary Test to check for duplicate methods + * @summary Test to check for duplicate methods across different inheritance patterns * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* @@ -49,57 +49,154 @@ public static void main(String... args) throws Exception { TestDuplicateMethods() throws IOException { + // Diamond class inheritance tb.writeJavaFiles(src, """ package p; - interface A { + interface A { /** - * JavaDoc for method in class A. - */ - abstract void testA ( ); - }""",""" - package p; - interface B extends A { + * JavaDoc for method in interface A. + */ + abstract void testA( ); + }""", """ + package p; + interface B extends A { /** - * JavaDoc for method in class B. - */ - abstract void testB ( ); - }""", """ - package p; - abstract class C implements A { + * JavaDoc for method in interface B. + */ + abstract void testB( ); + }""", """ + package p; + abstract class C implements A { /** - * Inherited JavaDoc for method in class C. - */ - public final void testA ( ) { - // Do nothing. + * Inherited JavaDoc for method in class C. + */ + public final void testA( ) { + // Do nothing. } - }""",""" - package p; - public final class D extends C implements B { + }""",""" + package p; + public final class D extends C implements B { + /** + * Inherited JavaDoc. + */ + public final void testB() { + // Do nothing. + } + } + """); + + // Mirrors the implementation of StringBuilder + tb.writeJavaFiles(src, + """ + package sb; + public interface I { + /** + * JavaDoc for method in public interface I. + */ + void testI(); + } + """, """ + package sb; + abstract class P implements I { /** - * Inherited JavaDoc - */ - public final void testB ( ) { - // Do nothing. + * Inherited JavaDoc for method in class P. + */ + public final void testI() { + // Do nothing. } - } - """); + } + """, """ + package sb; + public class U extends P implements I { + // No overrides + } + """ + ); + // Mirrors the implementation of HashMap + tb.writeJavaFiles(src, + """ + package hm; + public interface J { + /** + * JavaDoc for method in public interface J. + */ + void testJ(); + } + """, + """ + package hm; + public abstract class PubJ implements J { + /** + * Inherited JavaDoc for method in public abstract class PubJ. + */ + public final void testJ() { + // Do nothing. + } + } + """, + """ + package hm; + public class V extends PubJ implements J { + // No override + } + """ + ); } @Test - public void testDuplicateMethodWarning(Path base) { + public void testDiamondInheritance(Path base) { javadoc("-d", base.resolve("out").toString(), "-sourcepath", src.toString(), "p"); checkExit(Exit.OK); - checkOutput("p/D.html", true, """ -
Inherited JavaDoc for method in class C.
""",""" -
+ checkOutput("p/D.html", true, + """ +
Inherited JavaDoc for method in class C.
+ """, """
public final void testA()
-
Inherited JavaDoc for method in class C.
"""); +
Inherited JavaDoc for method in class C.
+ """ + ); + checkOutput("p/D.html", false, """ -
JavaDoc for method in class A.
""", """ +
JavaDoc for method in Interface A.
""", """
void testA()
-
JavaDoc for method in class A.
"""); +
JavaDoc for method in Interface A.
"""); + + + checkOutput("p/D.html", false, + """ +
JavaDoc for method in interface A.
+ """); + } + + @Test + public void testStringBuilderInheritance(Path base) { + javadoc("-d", base.resolve("out").toString(), + "-sourcepath", src.toString(), + "sb"); + checkExit(Exit.OK); + + checkOutput("sb/U.html", false, + """ +
+

Methods inherited from interface I

+ testI
+ """); + } + + @Test + public void testHashMapInheritance(Path base) { + javadoc("-d", base.resolve("out").toString(), + "-sourcepath", src.toString(), + "hm"); + checkExit(Exit.OK); + + checkOutput("hm/V.html", false, + """ +
+

Methods inherited from interface J

+ testJ
"""); } } From 00e7adccc424339bb658818c799a01b300b48b5f Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Tue, 24 Jun 2025 11:22:44 +0100 Subject: [PATCH 11/12] review feedback: add positive test --- .../TestDuplicateMethods.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java index 2774a0ff40a8c..bf8d4422d1f3b 100644 --- a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java +++ b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java @@ -184,6 +184,17 @@ public void testStringBuilderInheritance(Path base) {

Methods inherited from interface I

testI
"""); + + checkOutput("sb/U.html", true, + """ +

testI

+
+
public final void testI()
+
Inherited JavaDoc for method in class P.
+
+
Specified by:
+
testI in interface I
+
"""); } @Test @@ -198,5 +209,13 @@ public void testHashMapInheritance(Path base) {

Methods inherited from interface J

testJ
"""); + + checkOutput("hm/V.html", true, + """ +

V

+
+
public V()
+
+ """); } } From a34c4d5102855163ca2261b394bfc4d534c34b2c Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 4 Jul 2025 12:48:24 +0100 Subject: [PATCH 12/12] Fix test based on feedback --- .../testDuplicateMethodsWarn/TestDuplicateMethods.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java index bf8d4422d1f3b..7e191290f8a25 100644 --- a/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java +++ b/test/langtools/jdk/javadoc/doclet/testDuplicateMethodsWarn/TestDuplicateMethods.java @@ -212,10 +212,9 @@ public void testHashMapInheritance(Path base) { checkOutput("hm/V.html", true, """ -

V

-
-
public V()
-
+
+

Methods inherited from class PubJ

+ testJ
"""); } }