8325088: Overloads that differ in type parameters may be lost
Reviewed-by: jjg
This commit is contained in:
parent
6f087cbcd5
commit
7c66465763
@ -100,7 +100,7 @@ public class AnnotationTypeMemberWriter extends AbstractMemberWriter {
|
||||
buildAnnotationTypeMemberChildren(div);
|
||||
annotationContent.add(div);
|
||||
memberList.add(writer.getMemberListItem(annotationContent));
|
||||
writer.tableOfContents.addLink(htmlIds.forMember(typeElement, (ExecutableElement) member),
|
||||
writer.tableOfContents.addLink(htmlIds.forMember((ExecutableElement) member).getFirst(),
|
||||
Text.of(name(member)));
|
||||
}
|
||||
Content annotationDetails = getAnnotationDetails(annotationDetailsHeader, memberList);
|
||||
@ -210,7 +210,7 @@ public class AnnotationTypeMemberWriter extends AbstractMemberWriter {
|
||||
Text.of(name(member)));
|
||||
content.add(heading);
|
||||
return HtmlTree.SECTION(HtmlStyle.detail, content)
|
||||
.setId(htmlIds.forMember(typeElement, (ExecutableElement) member));
|
||||
.setId(htmlIds.forMember((ExecutableElement) member).getFirst());
|
||||
}
|
||||
|
||||
protected Content getSignature(Element member) {
|
||||
|
@ -116,7 +116,7 @@ public class ConstructorWriter extends AbstractExecutableMemberWriter {
|
||||
buildTagInfo(div);
|
||||
constructorContent.add(div);
|
||||
memberList.add(getMemberListItem(constructorContent));
|
||||
writer.tableOfContents.addLink(htmlIds.forMember(currentConstructor),
|
||||
writer.tableOfContents.addLink(htmlIds.forMember(currentConstructor).getFirst(),
|
||||
Text.of(utils.getSimpleName(constructor)
|
||||
+ utils.makeSignature(currentConstructor, typeElement, false, true)));
|
||||
}
|
||||
@ -189,13 +189,14 @@ public class ConstructorWriter extends AbstractExecutableMemberWriter {
|
||||
Content content = new ContentBuilder();
|
||||
var heading = HtmlTree.HEADING(Headings.TypeDeclaration.MEMBER_HEADING,
|
||||
Text.of(name(constructor)));
|
||||
HtmlId erasureAnchor = htmlIds.forErasure(constructor);
|
||||
if (erasureAnchor != null) {
|
||||
heading.setId(erasureAnchor);
|
||||
|
||||
var anchors = htmlIds.forMember(constructor);
|
||||
if (anchors.size() > 1) {
|
||||
heading.setId(anchors.getLast());
|
||||
}
|
||||
content.add(heading);
|
||||
return HtmlTree.SECTION(HtmlStyle.detail, content)
|
||||
.setId(htmlIds.forMember(constructor));
|
||||
.setId(anchors.getFirst());
|
||||
}
|
||||
|
||||
protected Content getSignature(ExecutableElement constructor) {
|
||||
|
@ -1037,7 +1037,7 @@ public abstract class HtmlDocletWriter {
|
||||
|
||||
if (utils.isExecutableElement(element)) {
|
||||
ExecutableElement ee = (ExecutableElement)element;
|
||||
HtmlId id = isProperty ? htmlIds.forProperty(ee) : htmlIds.forMember(ee);
|
||||
HtmlId id = isProperty ? htmlIds.forProperty(ee) : htmlIds.forMember(ee).getFirst();
|
||||
return getLink(new HtmlLinkInfo(configuration, context, typeElement)
|
||||
.label(label)
|
||||
.fragment(id.name())
|
||||
|
@ -25,11 +25,16 @@
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
@ -159,34 +164,89 @@ public class HtmlIds {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an id for an executable element, suitable for use when the
|
||||
* simple name and argument list will be unique within the page, such as
|
||||
* in the page for the declaration of the enclosing class or interface.
|
||||
* {@return a non-empty list of ids to a constructor or a method}
|
||||
* The ids from the returned list are alternative: the given constructor
|
||||
* or method can be equally referred to by any of those ids.
|
||||
*
|
||||
* @param element the element
|
||||
*
|
||||
* @return the id
|
||||
* @param executable a constructor or method
|
||||
*/
|
||||
HtmlId forMember(ExecutableElement element) {
|
||||
List<HtmlId> forMember(ExecutableElement executable) {
|
||||
var htmlId = ids.get(executable);
|
||||
if (htmlId != null)
|
||||
return htmlId;
|
||||
if (executable.getKind() != ElementKind.CONSTRUCTOR
|
||||
&& executable.getKind() != ElementKind.METHOD)
|
||||
throw new IllegalArgumentException(String.valueOf(executable.getKind()));
|
||||
var vmt = configuration.getVisibleMemberTable((TypeElement) executable.getEnclosingElement());
|
||||
var ctors = vmt.getVisibleMembers(VisibleMemberTable.Kind.CONSTRUCTORS);
|
||||
var methods = vmt.getVisibleMembers(VisibleMemberTable.Kind.METHODS);
|
||||
record Erased(ExecutableElement element, HtmlId id) { }
|
||||
// split elements into two buckets:
|
||||
// - elements whose erased id is present
|
||||
// - elements whose erased id is absent (i.e. is null)
|
||||
enum ErasedId { PRESENT, ABSENT }
|
||||
var buckets = Stream.concat(ctors.stream(), methods.stream())
|
||||
.map(e -> (ExecutableElement) e)
|
||||
.map(e -> new Erased(e, forErasure(e)))
|
||||
.collect(Collectors.groupingBy(erased -> erased.id == null ?
|
||||
ErasedId.ABSENT : ErasedId.PRESENT));
|
||||
var dups = new HashSet<String>();
|
||||
// the order of elements in each bucket is important for reproducibility
|
||||
// of ids: the same executable element must have the same id in any
|
||||
// javadoc run
|
||||
// Use simple id, unless we have to use erased id; for that, do the
|
||||
// following _in order_:
|
||||
// 1. Map all elements that can _only_ be addressed by the simple id
|
||||
for (var e : buckets.getOrDefault(ErasedId.ABSENT, List.of())) {
|
||||
var simpleId = forMember0(e.element);
|
||||
ids.put(e.element, List.of(simpleId));
|
||||
boolean added = dups.add(simpleId.name());
|
||||
// we assume that the simple id for an executable member that
|
||||
// does not use type parameters is unique
|
||||
assert added;
|
||||
}
|
||||
// 2. Map all elements that can be addressed by simple id or erased id;
|
||||
// if the simple id is not yet used, use it, otherwise use the erased id
|
||||
for (var e : buckets.getOrDefault(ErasedId.PRESENT, List.of())) {
|
||||
var simpleId = forMember0(e.element);
|
||||
if (dups.add(simpleId.name())) {
|
||||
ids.put(e.element, List.of(simpleId, e.id));
|
||||
} else {
|
||||
ids.put(e.element, List.of(e.id));
|
||||
boolean added = dups.add(e.id.name());
|
||||
// Not only must an erased id not clash with any simple id,
|
||||
// but it must also not clash with any other erased id.
|
||||
// The latter is because JLS 8.4.2. Method Signature:
|
||||
// it is a compile-time error to declare two methods
|
||||
// with override-equivalent signatures in a class
|
||||
assert added;
|
||||
}
|
||||
}
|
||||
// Safety net: if for whatever reason we cannot find the element
|
||||
// among those we just expanded, return the simple id. It might
|
||||
// not be always right, but at least it won't fail.
|
||||
//
|
||||
// - one example where it might happen is linking to an inherited
|
||||
// undocumented method (see test case T5093723)
|
||||
// TODO the above will need to be revisited if and when we redesign
|
||||
// VisibleMemberTable, which currently cannot correctly return the
|
||||
// owner of such a method
|
||||
//
|
||||
// - another example is annotation interface methods: they are not
|
||||
// included in VisibleMemberTable.Kind.METHODS and so cannot be
|
||||
// found among them
|
||||
return ids.computeIfAbsent(executable, e -> List.of(forMember0(e)));
|
||||
}
|
||||
|
||||
private final Map<ExecutableElement, List<HtmlId>> ids = new HashMap<>();
|
||||
|
||||
private HtmlId forMember0(ExecutableElement element) {
|
||||
String a = element.getSimpleName()
|
||||
+ utils.makeSignature(element, null, true, true);
|
||||
// utils.makeSignature includes spaces
|
||||
return HtmlId.of(a.replaceAll("\\s", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an id for an executable element, including the context
|
||||
* of its documented enclosing class or interface.
|
||||
*
|
||||
* @param typeElement the enclosing class or interface
|
||||
* @param member the element
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
HtmlId forMember(TypeElement typeElement, ExecutableElement member) {
|
||||
return HtmlId.of(utils.getSimpleName(member) + utils.signature(member, typeElement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an id for a field, suitable for use when the simple name
|
||||
* will be unique within the page, such as in the page for the
|
||||
@ -229,7 +289,7 @@ public class HtmlIds {
|
||||
* @param executableElement the element to anchor to
|
||||
* @return the 1.4.x style anchor for the executable element
|
||||
*/
|
||||
protected HtmlId forErasure(ExecutableElement executableElement) {
|
||||
private HtmlId forErasure(ExecutableElement executableElement) {
|
||||
final StringBuilder buf = new StringBuilder(executableElement.getSimpleName().toString());
|
||||
buf.append("(");
|
||||
List<? extends VariableElement> parameters = executableElement.getParameters();
|
||||
@ -483,7 +543,7 @@ public class HtmlIds {
|
||||
*/
|
||||
public HtmlId forPreviewSection(Element el) {
|
||||
return HtmlId.of("preview-" + switch (el.getKind()) {
|
||||
case CONSTRUCTOR, METHOD -> forMember((ExecutableElement) el).name();
|
||||
case CONSTRUCTOR, METHOD -> forMember((ExecutableElement) el).getFirst().name();
|
||||
case PACKAGE -> forPackage((PackageElement) el).name();
|
||||
default -> utils.getFullyQualifiedName(el, false);
|
||||
});
|
||||
@ -497,7 +557,7 @@ public class HtmlIds {
|
||||
* @return the id
|
||||
*/
|
||||
public HtmlId forRestrictedSection(ExecutableElement el) {
|
||||
return HtmlId.of("restricted-" + forMember(el).name());
|
||||
return HtmlId.of("restricted-" + forMember(el).getFirst().name());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2024, 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
|
||||
@ -128,7 +128,7 @@ public class HtmlIndexBuilder extends IndexBuilder {
|
||||
item.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
|
||||
}
|
||||
if (utils.isExecutableElement(element)) {
|
||||
String url = HtmlTree.encodeURL(htmlIds.forMember((ExecutableElement) element).name());
|
||||
String url = HtmlTree.encodeURL(htmlIds.forMember((ExecutableElement) element).getFirst().name());
|
||||
if (!url.equals(item.getLabel())) {
|
||||
item.setUrl(url);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public class MethodWriter extends AbstractExecutableMemberWriter {
|
||||
buildTagInfo(div);
|
||||
methodContent.add(div);
|
||||
memberList.add(writer.getMemberListItem(methodContent));
|
||||
writer.tableOfContents.addLink(htmlIds.forMember(currentMethod),
|
||||
writer.tableOfContents.addLink(htmlIds.forMember(currentMethod).getFirst(),
|
||||
Text.of(utils.getSimpleName(method)
|
||||
+ utils.makeSignature(currentMethod, typeElement, false, true)));
|
||||
}
|
||||
@ -204,13 +204,13 @@ public class MethodWriter extends AbstractExecutableMemberWriter {
|
||||
Content content = new ContentBuilder();
|
||||
var heading = HtmlTree.HEADING(Headings.TypeDeclaration.MEMBER_HEADING,
|
||||
Text.of(name(method)));
|
||||
HtmlId erasureAnchor;
|
||||
if ((erasureAnchor = htmlIds.forErasure(method)) != null) {
|
||||
heading.setId(erasureAnchor);
|
||||
var anchors = htmlIds.forMember(method);
|
||||
if (anchors.size() > 1) {
|
||||
heading.setId(anchors.getLast());
|
||||
}
|
||||
content.add(heading);
|
||||
return HtmlTree.SECTION(HtmlStyle.detail, content)
|
||||
.setId(htmlIds.forMember(method));
|
||||
.setId(anchors.getFirst());
|
||||
}
|
||||
|
||||
protected Content getSignature(ExecutableElement method) {
|
||||
@ -375,7 +375,7 @@ public class MethodWriter extends AbstractExecutableMemberWriter {
|
||||
var codeOverriddenTypeLink = HtmlTree.CODE(overriddenTypeLink);
|
||||
Content methlink = writer.getLink(
|
||||
new HtmlLinkInfo(writer.configuration, HtmlLinkInfo.Kind.PLAIN, holder)
|
||||
.fragment(writer.htmlIds.forMember(method).name())
|
||||
.fragment(writer.htmlIds.forMember(method).getFirst().name())
|
||||
.label(method.getSimpleName()));
|
||||
var codeMethLink = HtmlTree.CODE(methlink);
|
||||
var dd = HtmlTree.DD(codeMethLink);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2024, 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
|
||||
@ -32,12 +32,14 @@ import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.TypeParameterElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.SimpleElementVisitor14;
|
||||
import javax.lang.model.util.SimpleTypeVisitor9;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@ -143,7 +145,11 @@ public class Comparators {
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return compareModuleNames(e1, e2);
|
||||
result = compareModuleNames(e1, e2);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return compareTypeParameters(e1, e2);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -264,7 +270,10 @@ public class Comparators {
|
||||
result = compareFullyQualifiedNames(e1, e2);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return compareModuleNames(e1, e2);
|
||||
result = compareModuleNames(e1, e2);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return compareTypeParameters(e1, e2);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -364,7 +373,14 @@ public class Comparators {
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return compareModuleNames(e1, e2);
|
||||
result = compareModuleNames(e1, e2);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
// this might not be needed: if e1 != e2 and both are
|
||||
// executables they must differ in FQN and thus we
|
||||
// shouldn't reach here
|
||||
return compareTypeParameters(e1, e2);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -423,6 +439,21 @@ public class Comparators {
|
||||
}.visit(t);
|
||||
}
|
||||
|
||||
protected final int compareTypeParameters(Element e1, Element e2) {
|
||||
if (!e1.getKind().isExecutable() || !e2.getKind().isExecutable())
|
||||
return 0;
|
||||
var typeParameters1 = ((ExecutableElement) e1).getTypeParameters();
|
||||
var typeParameters2 = ((ExecutableElement) e2).getTypeParameters();
|
||||
var parameters1 = typeParameters1.toArray(new TypeParameterElement[0]);
|
||||
var parameters2 = typeParameters2.toArray(new TypeParameterElement[0]);
|
||||
return Arrays.compare(parameters1, parameters2, (p1, p2) -> {
|
||||
var bounds1 = p1.getBounds().toArray(new TypeMirror[0]);
|
||||
var bounds2 = p2.getBounds().toArray(new TypeMirror[0]);
|
||||
return Arrays.compare(bounds1, bounds2, (b1, b2) ->
|
||||
utils.compareStrings(true, b1.toString(), b2.toString()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two Elements, typically the name of a method,
|
||||
* field or constructor.
|
||||
|
381
test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java
Normal file
381
test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 8297879
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* @build toolbox.ToolBox javadoc.tester.*
|
||||
* @run main TestErasure
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class TestErasure extends JavadocTester {
|
||||
|
||||
private final ToolBox tb = new ToolBox();
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
new TestErasure().runTests();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create confusion between:
|
||||
* - a constructor/method type parameter and a like-named class
|
||||
* - similarly named but differently bounded constructor type parameters
|
||||
* - similarly named but differently bounded type parameter in methods
|
||||
*/
|
||||
@Test
|
||||
public void test1(Path base) throws IOException {
|
||||
Path src = base.resolve("src");
|
||||
// - put public class first so that writeJavaFiles is not confused
|
||||
// on the name of the file it should create
|
||||
//
|
||||
// - an _abstract_ class is used only for convenience: like an interface,
|
||||
// it allows to keep the test minimal, but unlike an interface, it
|
||||
// allows to test constructors
|
||||
tb.writeJavaFiles(src, """
|
||||
public abstract class Foo {
|
||||
public Foo(T arg) { }
|
||||
public <T extends X> Foo(T arg) { }
|
||||
public <T extends Y> Foo(T arg) { }
|
||||
public abstract T m(T arg);
|
||||
public abstract <T extends X> T m(T arg);
|
||||
public abstract <T extends Y> T m(T arg);
|
||||
}
|
||||
class T { }
|
||||
class X { }
|
||||
class Y { }
|
||||
""");
|
||||
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
src.resolve("Foo.java").toString());
|
||||
|
||||
checkExit(Exit.OK);
|
||||
// constructors
|
||||
checkOutput("Foo.html", true, """
|
||||
<section class="constructor-summary" id="constructor-summary">
|
||||
<h2>Constructor Summary</h2>
|
||||
<div class="caption"><span>Constructors</span></div>
|
||||
<div class="summary-table two-column-summary">
|
||||
<div class="table-header col-first">Constructor</div>
|
||||
<div class="table-header col-last">Description</div>
|
||||
<div class="col-constructor-name even-row-color"><code>\
|
||||
<a href="#%3Cinit%3E(T)" class="member-name-link">Foo</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last even-row-color"> </div>
|
||||
<div class="col-constructor-name odd-row-color"><code>\
|
||||
<a href="#%3Cinit%3E(X)" class="member-name-link">Foo</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last odd-row-color"> </div>
|
||||
<div class="col-constructor-name even-row-color"><code>\
|
||||
<a href="#%3Cinit%3E(Y)" class="member-name-link">Foo</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last even-row-color"> </div>
|
||||
</div>
|
||||
</section>""");
|
||||
checkOutput("Foo.html", true, """
|
||||
<li><a href="#constructor-detail" tabindex="0">Constructor Details</a>
|
||||
<ol class="toc-list">
|
||||
<li><a href="#%3Cinit%3E(T)" tabindex="0">Foo(T)</a></li>
|
||||
<li><a href="#%3Cinit%3E(X)" tabindex="0">Foo(T)</a></li>
|
||||
<li><a href="#%3Cinit%3E(Y)" tabindex="0">Foo(T)</a></li>
|
||||
</ol>
|
||||
</li>""");
|
||||
checkOutput("index-all.html", true, """
|
||||
<dt><a href="Foo.html#%3Cinit%3E(T)" class="member-name-link">Foo(T)</a>\
|
||||
- Constructor for class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>
|
||||
<dt><a href="Foo.html#%3Cinit%3E(X)" class="member-name-link">Foo(T)</a>\
|
||||
- Constructor for class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>
|
||||
<dt><a href="Foo.html#%3Cinit%3E(Y)" class="member-name-link">Foo(T)</a>\
|
||||
- Constructor for class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>""");
|
||||
checkOutput("member-search-index.js", true, """
|
||||
{"p":"<Unnamed>","c":"Foo","l":"Foo(T)","u":"%3Cinit%3E(T)"},\
|
||||
{"p":"<Unnamed>","c":"Foo","l":"Foo(T)","u":"%3Cinit%3E(X)"},\
|
||||
{"p":"<Unnamed>","c":"Foo","l":"Foo(T)","u":"%3Cinit%3E(Y)"}""");
|
||||
// methods
|
||||
checkOutput("Foo.html", true, """
|
||||
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code>abstract T</code></div>
|
||||
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code><a href="#m(T)" class="member-name-link">m</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"> </div>
|
||||
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code>abstract <T extends X><br>T</code></div>
|
||||
<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code><a href="#m(X)" class="member-name-link">m</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"> </div>
|
||||
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code>abstract <T extends Y><br>T</code></div>
|
||||
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code><a href="#m(Y)" class="member-name-link">m</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"> </div>""");
|
||||
checkOutput("Foo.html", true, """
|
||||
<li><a href="#method-detail" tabindex="0">Method Details</a>
|
||||
<ol class="toc-list">
|
||||
<li><a href="#m(T)" tabindex="0">m(T)</a></li>
|
||||
<li><a href="#m(X)" tabindex="0">m(T)</a></li>
|
||||
<li><a href="#m(Y)" tabindex="0">m(T)</a></li>
|
||||
</ol>
|
||||
</li>""");
|
||||
checkOutput("index-all.html", true, """
|
||||
<dt><a href="Foo.html#m(T)" class="member-name-link">m(T)</a>\
|
||||
- Method in class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>
|
||||
<dt><a href="Foo.html#m(X)" class="member-name-link">m(T)</a>\
|
||||
- Method in class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>
|
||||
<dt><a href="Foo.html#m(Y)" class="member-name-link">m(T)</a>\
|
||||
- Method in class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>""");
|
||||
checkOutput("member-search-index.js", true, """
|
||||
{"p":"<Unnamed>","c":"Foo","l":"m(T)"},\
|
||||
{"p":"<Unnamed>","c":"Foo","l":"m(T)","u":"m(X)"},\
|
||||
{"p":"<Unnamed>","c":"Foo","l":"m(T)","u":"m(Y)"}""");
|
||||
}
|
||||
|
||||
/*
|
||||
* Create confusion between the class type parameter
|
||||
* and a like-named constructor/method type parameter.
|
||||
*/
|
||||
@Test
|
||||
public void test2(Path base) throws IOException {
|
||||
Path src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
public abstract class Foo<T> {
|
||||
public Foo(T arg) { }
|
||||
public <T extends X> Foo(T arg) { }
|
||||
public abstract T m(T arg);
|
||||
public abstract <T extends X> T m(T arg);
|
||||
}
|
||||
class X { }
|
||||
""");
|
||||
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
src.resolve("Foo.java").toString());
|
||||
|
||||
checkExit(Exit.OK);
|
||||
// constructors
|
||||
checkOutput("Foo.html", true, """
|
||||
<section class="constructor-summary" id="constructor-summary">
|
||||
<h2>Constructor Summary</h2>
|
||||
<div class="caption"><span>Constructors</span></div>
|
||||
<div class="summary-table two-column-summary">
|
||||
<div class="table-header col-first">Constructor</div>
|
||||
<div class="table-header col-last">Description</div>
|
||||
<div class="col-constructor-name even-row-color"><code>\
|
||||
<a href="#%3Cinit%3E(T)" class="member-name-link">Foo</a>\
|
||||
<wbr>(<a href="Foo.html" title="type parameter in Foo">T</a> arg)</code></div>
|
||||
<div class="col-last even-row-color"> </div>
|
||||
<div class="col-constructor-name odd-row-color"><code>\
|
||||
<a href="#%3Cinit%3E(X)" class="member-name-link">Foo</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last odd-row-color"> </div>
|
||||
</div>
|
||||
</section>""");
|
||||
checkOutput("Foo.html", true, """
|
||||
<li><a href="#constructor-detail" tabindex="0">Constructor Details</a>
|
||||
<ol class="toc-list">
|
||||
<li><a href="#%3Cinit%3E(T)" tabindex="0">Foo(T)</a></li>
|
||||
<li><a href="#%3Cinit%3E(X)" tabindex="0">Foo(T)</a></li>
|
||||
</ol>
|
||||
</li>""");
|
||||
checkOutput("index-all.html", true, """
|
||||
<dt><a href="Foo.html#%3Cinit%3E(T)" class="member-name-link">Foo(T)</a>\
|
||||
- Constructor for class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>
|
||||
<dt><a href="Foo.html#%3Cinit%3E(X)" class="member-name-link">Foo(T)</a>\
|
||||
- Constructor for class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>""");
|
||||
checkOutput("member-search-index.js", true, """
|
||||
{"p":"<Unnamed>","c":"Foo","l":"Foo(T)","u":"%3Cinit%3E(T)"},\
|
||||
{"p":"<Unnamed>","c":"Foo","l":"Foo(T)","u":"%3Cinit%3E(X)"}""");
|
||||
// methods
|
||||
checkOutput("Foo.html", true, """
|
||||
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code>abstract <a href="Foo.html" title="type parameter in Foo">T</a></code></div>
|
||||
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code><a href="#m(T)" class="member-name-link">m</a>\
|
||||
<wbr>(<a href="Foo.html" title="type parameter in Foo">T</a> arg)</code></div>
|
||||
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"> </div>
|
||||
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code>abstract <T extends X><br>T</code></div>
|
||||
<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"><code><a href="#m(X)" class="member-name-link">m</a><wbr>(T arg)</code></div>
|
||||
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 \
|
||||
method-summary-table-tab3"> </div>""");
|
||||
checkOutput("Foo.html", true, """
|
||||
<li><a href="#method-detail" tabindex="0">Method Details</a>
|
||||
<ol class="toc-list">
|
||||
<li><a href="#m(T)" tabindex="0">m(T)</a></li>
|
||||
<li><a href="#m(X)" tabindex="0">m(T)</a></li>
|
||||
</ol>
|
||||
</li>""");
|
||||
checkOutput("index-all.html", true, """
|
||||
<dt><a href="Foo.html#m(T)" class="member-name-link">m(T)</a>\
|
||||
- Method in class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>
|
||||
<dt><a href="Foo.html#m(X)" class="member-name-link">m(T)</a>\
|
||||
- Method in class <a href="Foo.html" title="class in Unnamed Package">Foo</a></dt>
|
||||
<dd> </dd>""");
|
||||
checkOutput("member-search-index.js", true, """
|
||||
{"p":"<Unnamed>","c":"Foo","l":"m(T)"},\
|
||||
{"p":"<Unnamed>","c":"Foo","l":"m(T)","u":"m(X)"}""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewAndDeprecated(Path base) throws IOException {
|
||||
Path src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
public abstract class Foo {
|
||||
/** @since today */
|
||||
@Deprecated(since="tomorrow")
|
||||
public Foo(T arg) { }
|
||||
/** @since today */
|
||||
@Deprecated(since="tomorrow")
|
||||
public <T extends X> Foo(T arg) { }
|
||||
/** @since today */
|
||||
@Deprecated(since="tomorrow")
|
||||
public <T extends Y> Foo(T arg) { }
|
||||
/** @since today */
|
||||
@Deprecated(since="tomorrow")
|
||||
public abstract T m(T arg);
|
||||
/** @since today */
|
||||
@Deprecated(since="tomorrow")
|
||||
public abstract <T extends X> T m(T arg);
|
||||
/** @since today */
|
||||
@Deprecated(since="tomorrow")
|
||||
public abstract <T extends Y> T m(T arg);
|
||||
}
|
||||
class T { }
|
||||
class X { }
|
||||
class Y { }
|
||||
""");
|
||||
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
"--since", "today",
|
||||
src.resolve("Foo.java").toString());
|
||||
|
||||
checkExit(Exit.OK);
|
||||
checkOutput("new-list.html", true, """
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#m(T)">Foo.m<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">today</div>
|
||||
<div class="col-last even-row-color"> </div>
|
||||
<div class="col-summary-item-name odd-row-color"><a href="Foo.html#m(X)">Foo.m<wbr>(T)</a></div>
|
||||
<div class="col-second odd-row-color">today</div>
|
||||
<div class="col-last odd-row-color"> </div>
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#m(Y)">Foo.m<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">today</div>
|
||||
<div class="col-last even-row-color"> </div>""");
|
||||
checkOutput("new-list.html", true, """
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#%3Cinit%3E(T)">Foo<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">today</div>
|
||||
<div class="col-last even-row-color"> </div>
|
||||
<div class="col-summary-item-name odd-row-color"><a href="Foo.html#%3Cinit%3E(X)">Foo<wbr>(T)</a></div>
|
||||
<div class="col-second odd-row-color">today</div>
|
||||
<div class="col-last odd-row-color"> </div>
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#%3Cinit%3E(Y)">Foo<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">today</div>
|
||||
<div class="col-last even-row-color"> </div>""");
|
||||
checkOutput("deprecated-list.html", true, """
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#m(T)">Foo.m<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">tomorrow</div>
|
||||
<div class="col-last even-row-color"></div>
|
||||
<div class="col-summary-item-name odd-row-color"><a href="Foo.html#m(X)">Foo.m<wbr>(T)</a></div>
|
||||
<div class="col-second odd-row-color">tomorrow</div>
|
||||
<div class="col-last odd-row-color"></div>
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#m(Y)">Foo.m<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">tomorrow</div>
|
||||
<div class="col-last even-row-color"></div>""");
|
||||
checkOutput("deprecated-list.html", true, """
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#%3Cinit%3E(T)">Foo<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">tomorrow</div>
|
||||
<div class="col-last even-row-color"></div>
|
||||
<div class="col-summary-item-name odd-row-color"><a href="Foo.html#%3Cinit%3E(X)">Foo<wbr>(T)</a></div>
|
||||
<div class="col-second odd-row-color">tomorrow</div>
|
||||
<div class="col-last odd-row-color"></div>
|
||||
<div class="col-summary-item-name even-row-color"><a href="Foo.html#%3Cinit%3E(Y)">Foo<wbr>(T)</a></div>
|
||||
<div class="col-second even-row-color">tomorrow</div>
|
||||
<div class="col-last even-row-color"></div>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreview(Path base) throws IOException {
|
||||
// unlike that for other tests, here we cannot simulate ambiguity between
|
||||
// a type parameter and a like-named class, because for that the class
|
||||
// needs to be in the unnamed package, otherwise its FQN won't be T
|
||||
Path src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
package p;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
public abstract class Foo {
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.TEST)
|
||||
public <T extends X> Foo(T arg) { }
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.TEST)
|
||||
public <T extends Y> Foo(T arg) { }
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.TEST)
|
||||
public abstract <T extends X> T m(T arg);
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.TEST)
|
||||
public abstract <T extends Y> T m(T arg);
|
||||
}
|
||||
class X { }
|
||||
class Y { }
|
||||
""");
|
||||
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
"--patch-module", "java.base=" + src.toAbsolutePath().toString(),
|
||||
src.resolve("p").resolve("Foo.java").toString());
|
||||
|
||||
checkExit(Exit.OK);
|
||||
checkOutput("preview-list.html", true, """
|
||||
<div class="col-summary-item-name even-row-color method method-tab1">\
|
||||
<a href="java.base/p/Foo.html#m(T)">p.Foo.m<wbr>(T)</a><sup>\
|
||||
<a href="java.base/p/Foo.html#preview-m(T)">PREVIEW</a></sup></div>
|
||||
<div class="col-second even-row-color method method-tab1">Test Feature</div>
|
||||
<div class="col-last even-row-color method method-tab1"></div>
|
||||
<div class="col-summary-item-name odd-row-color method method-tab1">\
|
||||
<a href="java.base/p/Foo.html#m(p.Y)">p.Foo.m<wbr>(T)</a><sup>\
|
||||
<a href="java.base/p/Foo.html#preview-m(p.Y)">PREVIEW</a></sup></div>
|
||||
<div class="col-second odd-row-color method method-tab1">Test Feature</div>
|
||||
<div class="col-last odd-row-color method method-tab1"></div>""");
|
||||
checkOutput("preview-list.html", true, """
|
||||
<div class="col-summary-item-name even-row-color constructor constructor-tab1">\
|
||||
<a href="java.base/p/Foo.html#%3Cinit%3E(T)">p.Foo<wbr>(T)</a><sup>\
|
||||
<a href="java.base/p/Foo.html#preview-%3Cinit%3E(T)">PREVIEW</a></sup></div>
|
||||
<div class="col-second even-row-color constructor constructor-tab1">Test Feature</div>
|
||||
<div class="col-last even-row-color constructor constructor-tab1"></div>
|
||||
<div class="col-summary-item-name odd-row-color constructor constructor-tab1">\
|
||||
<a href="java.base/p/Foo.html#%3Cinit%3E(p.Y)">p.Foo<wbr>(T)</a><sup>\
|
||||
<a href="java.base/p/Foo.html#preview-%3Cinit%3E(p.Y)">PREVIEW</a></sup></div>
|
||||
<div class="col-second odd-row-color constructor constructor-tab1">Test Feature</div>
|
||||
<div class="col-last odd-row-color constructor constructor-tab1"></div>""");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user