From 2ab8ab56130ca258bf0347ea44e74a8cad3d537d Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 31 May 2024 05:54:39 +0000 Subject: [PATCH] 8332858: References with escapes have broken positions after they are transformed Reviewed-by: vromero, jjg --- .../markdown/MarkdownTransformer.java | 19 ++++-- .../MarkdownTransformerPositionTest.java | 59 ++++++++++++++++++- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java index bd2521e4efa6e..5dca2f2374f79 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java @@ -803,8 +803,9 @@ public void visit(Link link) { // determine whether to use {@link ... } or {@linkplain ...} // based on whether the "link text" is the same as the "link destination" String ref = dest.substring(autorefScheme.length()); - int refPos = sourcePosToTreePos(getRefPos(ref, link)); - var newRefTree = m.at(refPos).newReferenceTree(ref).setEndPos(refPos + ref.length()); + int[] span = getRefSpan(ref, link); + int refPos = sourcePosToTreePos(span[0]); + var newRefTree = m.at(refPos).newReferenceTree(ref).setEndPos(sourcePosToTreePos(span[1])); Node child = link.getFirstChild(); DocTree.Kind linkKind = child.getNext() == null @@ -835,7 +836,7 @@ public void visit(Link link) { * @param ref the reference to find * @param link the link containing the reference */ - private int getRefPos(String ref, Link link) { + private int[] getRefSpan(String ref, Link link) { var spans = link.getSourceSpans(); var revSpanIter = spans.listIterator(spans.size()); while (revSpanIter.hasPrevious()) { @@ -845,11 +846,19 @@ private int getRefPos(String ref, Link link) { var s = source.substring(start, end); var index = s.lastIndexOf(ref); if (index != -1) { - return start + index; + return new int[] {start + index, start + index + ref.length()}; + } else { + String escapedRef = ref.replace("[]", "\\[\\]"); + var escapedIndex = s.lastIndexOf(escapedRef); + if (escapedIndex != -1) { + return new int[] {start + escapedIndex, + start + escapedIndex + escapedRef.length()}; + } } } - return NOPOS; + return NOSPAN; } + private static final int[] NOSPAN = new int[] {NOPOS, NOPOS}; /** * {@return the position in the original comment for a position in {@code source}, diff --git a/test/langtools/tools/javac/doctree/MarkdownTransformerPositionTest.java b/test/langtools/tools/javac/doctree/MarkdownTransformerPositionTest.java index 4b4e44ef42721..5911d5c08679c 100644 --- a/test/langtools/tools/javac/doctree/MarkdownTransformerPositionTest.java +++ b/test/langtools/tools/javac/doctree/MarkdownTransformerPositionTest.java @@ -23,13 +23,16 @@ /* * @test + * @bug 8332858 * @summary test case for Markdown positions * @run main/othervm --limit-modules jdk.compiler MarkdownTransformerPositionTest - * @run main MarkdownTransformerPositionTest + * @run main MarkdownTransformerPositionTest links */ import com.sun.source.doctree.DocCommentTree; +import com.sun.source.doctree.LinkTree; import com.sun.source.doctree.RawTextTree; +import com.sun.source.doctree.ReferenceTree; import com.sun.source.tree.*; import com.sun.source.util.*; @@ -50,6 +53,10 @@ public static void main(String... args) throws Exception { t.simpleTest(); t.testWithReplacements(); + + if (args.length > 0 && "links".equals(args[0])) { + t.linkWithEscapes(); + } } private void simpleTest() throws Exception { @@ -76,6 +83,16 @@ public class Test { "testAuthor"); } + private void linkWithEscapes() throws Exception { + runConvertedLinksTest(""" + /// Markdown comment. + /// [java.util.Arrays#asList(Object\\[\\])] + public class Test { + } + """, + "java.util.Arrays#asList(Object\\[\\])"); + } + private void runTest(String source, String... expectedRawSpans) throws Exception { JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); JavacTask task = (JavacTask)comp.getTask(null, null, null, null, null, Arrays.asList(new JavaSource(source))); @@ -107,6 +124,46 @@ public Void visitRawText(RawTextTree node, Void p) { System.err.println("Test result: success, boot modules: " + ModuleLayer.boot().modules()); } + private void runConvertedLinksTest(String source, String... expectedRawSpans) throws Exception { + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + JavacTask task = (JavacTask)comp.getTask(null, null, null, null, null, Arrays.asList(new JavaSource(source))); + CompilationUnitTree cu = task.parse().iterator().next(); + task.analyze(); + DocTrees trees = DocTrees.instance(task); + List rawSpans = new ArrayList<>(); + TreePath clazzTP = new TreePath(new TreePath(cu), cu.getTypeDecls().get(0)); + Element clazz = trees.getElement(clazzTP); + DocCommentTree docComment = trees.getDocCommentTree(clazz); + + new DocTreeScanner() { + @Override + public Void visitLink(LinkTree node, Void p) { + int start = (int) trees.getSourcePositions().getStartPosition(cu, docComment, node); + if (start != (-1)) { + throw new AssertionError("UNexpected start position for synthetic link: " + start); + } + return super.visitLink(node, p); + } + + @Override + public Void visitReference(ReferenceTree node, Void p) { + int start = (int) trees.getSourcePositions().getStartPosition(cu, docComment, node); + int end = (int) trees.getSourcePositions().getEndPosition(cu, docComment, node); + rawSpans.add(source.substring(start, end)); + return super.visitReference(node, p); + } + }.scan(docComment, null); + + List expectedRawSpansList = List.of(expectedRawSpans); + + if (!expectedRawSpansList.equals(rawSpans)) { + throw new AssertionError("Incorrect raw text spans, should be: " + + expectedRawSpansList + ", but is: " + rawSpans); + } + + System.err.println("Test result: success, boot modules: " + ModuleLayer.boot().modules()); + } + static class JavaSource extends SimpleJavaFileObject { private final String source;