8300914: Allow @ as an escape in documentation comments

Reviewed-by: prappo
This commit is contained in:
Jonathan Gibbons 2023-02-08 21:57:22 +00:00
parent 8a9e383dba
commit 873558ee80
22 changed files with 543 additions and 25 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, 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
@ -103,6 +103,14 @@ public interface DocTree {
*/
ERRONEOUS,
/**
* Used for instances of {@link EscapeTree}
* representing some escaped documentation text.
*
* @since 21
*/
ESCAPE,
/**
* Used for instances of {@link ThrowsTree}
* representing an {@code @exception} tag.
@ -250,7 +258,7 @@ public interface DocTree {
/**
* Used for instances of {@link TextTree}
* representing some documentation text.
* representing some plain documentation text.
*/
TEXT,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, 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
@ -143,6 +143,22 @@ public interface DocTreeVisitor<R,P> {
*/
R visitErroneous(ErroneousTree node, P p);
/**
* Visits an {@code EscapeTree} node.
*
* @implSpec Visits the provided {@code EscapeTree} node
* by calling {@code visitOther(node, p)}.
*
* @param node the node being visited
* @param p a parameter value
* @return a result value
*
* @since 21
*/
default R visitEscape(EscapeTree node, P p) {
return visitOther(node, p);
}
/**
* Visits a {@code HiddenTree} node.
*

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package com.sun.source.doctree;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
/**
* A tree node for a character represented by an escape sequence.
*
* @apiNote This class does not itself constrain the set of valid escape sequences,
* although the set may be effectively constrained to those defined in the
* <a href="{@docRoot}/../specs/javadoc/doc-comment-spec.html#escape-sequences">
* Documentation Comment Specification for the Standard Doclet</a>,
* including the following context-sensitive escape sequences:
*
* <ul>
* <li>{@code @@}, representing {@code @}, where it would otherwise be treated as introducing a block or inline tag,
* <li>{@code @/}, representing {@code /}, as part of {@code *@/} to represent <code>&ast;&sol;</code>, and
* <li>{@code @*}, representing {@code *}, where it would otherwise be {@linkplain Elements#getDocComment(Element) discarded},
* after whitespace at the beginning of a line.
* </ul>
*
* @since 21
*/
public interface EscapeTree extends TextTree {
/**
* {@inheritDoc}
*
* <p>Note: this method returns the escaped character, not the original escape sequence.
*/
@Override
String getBody();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, 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,9 @@ package com.sun.source.util;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
@ -43,6 +45,7 @@ import com.sun.source.doctree.DocTypeTree;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.ErroneousTree;
import com.sun.source.doctree.EscapeTree;
import com.sun.source.doctree.HiddenTree;
import com.sun.source.doctree.IdentifierTree;
import com.sun.source.doctree.IndexTree;
@ -175,6 +178,29 @@ public interface DocTreeFactory {
*/
ErroneousTree newErroneousTree(String text, Diagnostic<JavaFileObject> diag);
/**
* Creates a new {@code EscapeTree} object, to represent an escaped character.
*
* @apiNote This method does not itself constrain the set of valid escape sequences,
* although the set may be effectively constrained to those defined in the
* <a href="{@docRoot}/../specs/javadoc/doc-comment-spec.html#escape-sequences">
* Documentation Comment Specification for the Standard Doclet</a>,
* including the following context-sensitive escape sequences:
*
* <ul>
* <li>{@code @@}, representing {@code @}, where it would otherwise be treated as introducing a block or inline tag,
* <li>{@code @/}, representing {@code /}, as part of {@code *@/} to represent <code>&ast;&sol;</code>, and
* <li>{@code @*}, representing {@code *}, where it would otherwise be {@linkplain Elements#getDocComment(Element) discarded},
* after whitespace at the beginning of a line.
* </ul>
*
* @param ch the character
* @return an {@code EscapeTree} object
*
* @since 21
*/
EscapeTree newEscapeTree(char ch);
/**
* Creates a new {@code ThrowsTree} object, to represent an {@code @exception} tag.
* @param name the name of the exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, 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
@ -277,6 +277,22 @@ public class DocTreeScanner<R,P> implements DocTreeVisitor<R,P> {
return null;
}
/**
* {@inheritDoc}
*
* @implSpec This implementation returns {@code null}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
*
* @since 21
*/
@Override
public R visitEscape(EscapeTree node, P p) {
return null;
}
/**
* {@inheritDoc}
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, 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
@ -232,6 +232,22 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> {
return defaultAction(node, p);
}
/**
* {@inheritDoc}
*
* @implSpec This implementation calls {@code defaultAction}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
*
* @since 21
*/
@Override
public R visitEscape(EscapeTree node, P p) {
return defaultAction(node, p);
}
/**
* {@inheritDoc}
*

View File

@ -152,6 +152,10 @@ public class DocCommentParser {
}
}
char peekChar() {
return buf[bp < buflen ? bp + 1 : buflen];
}
protected List<DCTree> blockContent() {
return blockContent(Phase.BODY);
}
@ -224,9 +228,32 @@ public class DocCommentParser {
break;
case '@':
// check for context-sensitive escape sequences:
// newline whitespace @@
// newline whitespace @*
// *@/
if (newline) {
addPendingText(trees, lastNonWhite);
break loop;
char peek = peekChar();
if (peek == '@' || peek == '*') {
addPendingText(trees, bp - 1);
nextChar();
trees.add(m.at(bp - 1).newEscapeTree(ch));
newline = false;
nextChar();
textStart = bp;
break;
} else {
addPendingText(trees, lastNonWhite);
break loop;
}
} else if (textStart != -1 && buf[bp - 1] == '*' && peekChar() == '/') {
addPendingText(trees, bp - 1);
nextChar();
trees.add(m.at(bp - 1).newEscapeTree('/'));
newline = false;
nextChar();
textStart = bp;
break;
}
// fallthrough
@ -294,10 +321,24 @@ public class DocCommentParser {
newline = false;
nextChar();
if (ch == '@') {
addPendingText(list, bp - 2);
list.add(inlineTag());
textStart = bp;
lastNonWhite = -1;
// check for context-sensitive escape-sequence
// {@@
if (peekChar() == '@') {
if (textStart == -1) {
textStart = bp - 1;
}
addPendingText(list, bp - 1);
nextChar();
list.add(m.at(bp - 1).newEscapeTree('@'));
nextChar();
textStart = -1;
lastNonWhite = bp;
} else {
addPendingText(list, bp - 2);
list.add(inlineTag());
textStart = bp;
lastNonWhite = -1;
}
} else {
if (textStart == -1)
textStart = bp - 1;
@ -624,6 +665,33 @@ public class DocCommentParser {
nextChar();
break;
case '@':
// check for context-sensitive escape sequences:
// newline whitespace @@
// newline whitespace @*
// *@/
if (newline) {
char peek = peekChar();
if (peek == '@' || peek == '*') {
addPendingText(trees, bp - 1);
nextChar();
trees.add(m.at(bp - 1).newEscapeTree(ch));
newline = false;
nextChar();
textStart = bp;
break;
}
} else if (textStart != -1 && buf[bp - 1] == '*' && peekChar() == '/') {
addPendingText(trees, bp - 1);
nextChar();
trees.add(m.at(bp - 1).newEscapeTree('/'));
newline = false;
nextChar();
textStart = bp;
break;
}
// fallthrough
default:
if (textStart == -1)
textStart = bp;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, 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
@ -168,6 +168,11 @@ public abstract class DCTree implements DocTree {
return err.pos + err.body.length();
}
case ESCAPE -> {
DCEscape esc = (DCEscape) this;
return esc.pos + 2;
}
case IDENTIFIER -> {
DCIdentifier ident = (DCIdentifier) this;
return ident.pos + ident.name.length();
@ -641,7 +646,29 @@ public abstract class DCTree implements DocTree {
this.prefPos = prefPos;
return this;
}
}
public static class DCEscape extends DCTree implements EscapeTree {
public final char ch;
DCEscape(char ch) {
this.ch = ch;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.ESCAPE;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(DocTreeVisitor<R, D> v, D d) {
return v.visitEscape(this, d);
}
@Override @DefinedBy(Api.COMPILER_TREE)
public String getBody() {
return String.valueOf(ch);
}
}
public static class DCHidden extends DCBlockTag implements HiddenTree {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -245,6 +245,17 @@ public class DocPretty implements DocTreeVisitor<Void,Void> {
return null;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitEscape(EscapeTree node, Void p) {
try {
out.write("@");
print(node.getBody());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return null;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitHidden(HiddenTree node, Void p) {
try {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, 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
@ -60,6 +60,7 @@ import com.sun.tools.javac.tree.DCTree.DCDocType;
import com.sun.tools.javac.tree.DCTree.DCEndElement;
import com.sun.tools.javac.tree.DCTree.DCEntity;
import com.sun.tools.javac.tree.DCTree.DCErroneous;
import com.sun.tools.javac.tree.DCTree.DCEscape;
import com.sun.tools.javac.tree.DCTree.DCHidden;
import com.sun.tools.javac.tree.DCTree.DCIdentifier;
import com.sun.tools.javac.tree.DCTree.DCIndex;
@ -276,6 +277,13 @@ public class DocTreeMaker implements DocTreeFactory {
return tree;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public DCEscape newEscapeTree(char ch) {
DCEscape tree = new DCEscape(ch);
tree.pos = pos;
return tree;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public DCThrows newExceptionTree(ReferenceTree name, List<? extends DocTree> description) {
// TODO: verify the reference is just to a type (not a field or method)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2023, 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
@ -48,6 +48,7 @@ import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.EscapeTree;
import com.sun.source.doctree.InlineTagTree;
import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.LiteralTree;
@ -218,6 +219,12 @@ public class JavadocFormatter {
return null;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Object visitEscape(EscapeTree node, Object p) {
result.append(node.getBody());
return null;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Object visitLink(LinkTree node, Object p) {
if (!node.getLabel().isEmpty()) {

View File

@ -67,6 +67,7 @@ import com.sun.source.doctree.DocTree.Kind;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.ErroneousTree;
import com.sun.source.doctree.EscapeTree;
import com.sun.source.doctree.IndexTree;
import com.sun.source.doctree.InheritDocTree;
import com.sun.source.doctree.LinkTree;
@ -1338,6 +1339,12 @@ public class HtmlDocletWriter {
return false;
}
@Override
public Boolean visitEscape(EscapeTree node, Content content) {
result.add(node.getBody());
return false;
}
@Override
public Boolean visitInheritDoc(InheritDocTree node, Content content) {
Content output = getInlineTagOutput(element, node, context);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@ -31,6 +31,7 @@ import com.sun.source.doctree.CommentTree;
import com.sun.source.doctree.DeprecatedTree;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.EscapeTree;
import com.sun.source.doctree.IdentifierTree;
import com.sun.source.doctree.InlineTagTree;
import com.sun.source.doctree.LinkTree;
@ -386,6 +387,11 @@ public class CommentHelper {
return node.getBody();
}
@Override
public List<? extends DocTree> visitEscape(EscapeTree node, Void p) {
return asList(node.getBody());
}
@Override
public List<? extends DocTree> visitLiteral(LiteralTree node, Void p) {
return asList(node.getBody().getBody());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2023, 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
@ -65,6 +65,7 @@ import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.ErroneousTree;
import com.sun.source.doctree.EscapeTree;
import com.sun.source.doctree.IdentifierTree;
import com.sun.source.doctree.IndexTree;
import com.sun.source.doctree.InheritDocTree;
@ -348,6 +349,7 @@ public class Checker extends DocTreePathScanner<Void, Void> {
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitEntity(EntityTree tree, Void ignore) {
hasNonWhitespaceText = true;
checkAllowsText(tree);
markEnclosingTag(Flag.HAS_TEXT);
String s = env.trees.getCharacters(tree);
@ -358,6 +360,14 @@ public class Checker extends DocTreePathScanner<Void, Void> {
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitEscape(EscapeTree tree, Void ignore) {
hasNonWhitespaceText = true;
checkAllowsText(tree);
markEnclosingTag(Flag.HAS_TEXT);
return null;
}
void checkAllowsText(DocTree tree) {
TagStackItem top = tagStack.peek();
if (top != null

View File

@ -66,6 +66,14 @@ public class HtmlTagsTest {
*/
public void inline_not_allowed() { }
/**
* <ul> &amp; <li> ... </li> </ul>
*/
public void entity_not_allowed() { }
/**
* <ul> *@/ <li> ... </li> </ul>
*/
public void escape_not_allowed() { }
}

View File

@ -43,5 +43,11 @@ HtmlTagsTest.java:60: error: text not allowed in <ul> element
HtmlTagsTest.java:65: error: tag not allowed here: <b>
* <ul> <b>text</b> <li> ... </li> </ul>
^
13 errors
HtmlTagsTest.java:70: error: text not allowed in <ul> element
* <ul> &amp; <li> ... </li> </ul>
^
HtmlTagsTest.java:75: error: text not allowed in <ul> element
* <ul> *@/ <li> ... </li> </ul>
^
15 errors
2 warnings

View File

@ -0,0 +1,31 @@
/*
* @test /nodynamiccopyright/
* @modules jdk.javadoc/jdk.javadoc.internal.doclint
* @build DocLintTester
* @run main DocLintTester -ref ReturnTest.out ReturnTest.java
*/
/** No comment. */
public class ReturnTest {
/**
{@return legal} **/
public int m_legal() { return 0; }
/** <p> {@return illegal} **/
public int m_illegal_html() { return 0; }
/** text {@return illegal} **/
public int m_illegal_text() { return 0; }
/** &amp;{@return illegal} **/
public int m_illegal_entity() { return 0; }
/** @@{@return illegal} **/
public int m_illegal_escape() { return 0; }
/** {@return legal} text {@return illegal} **/
public int m_illegal_repeat() { return 0; }
/** . */
private ReturnTest() { }
}

View File

@ -0,0 +1,19 @@
ReturnTest.java:14: warning: {@return} not at beginning of description
/** <p> {@return illegal} **/
^
ReturnTest.java:17: warning: {@return} not at beginning of description
/** text {@return illegal} **/
^
ReturnTest.java:20: warning: {@return} not at beginning of description
/** &amp;{@return illegal} **/
^
ReturnTest.java:23: warning: {@return} not at beginning of description
/** @@{@return illegal} **/
^
ReturnTest.java:26: warning: @return has already been specified
/** {@return legal} text {@return illegal} **/
^
ReturnTest.java:26: warning: {@return} not at beginning of description
/** {@return legal} text {@return illegal} **/
^
6 warnings

View File

@ -9,13 +9,22 @@
public class SummaryTest {
/**
{@summary legal} **/
public void m0() {}
public void m_legal() {}
/** <p> {@summary illegal} **/
public void m1() {}
public void m_illegal_html() {}
/** text {@summary illegal} **/
public void m_illegal_text() {}
/** &amp;{@summary illegal} **/
public void m_illegal_entity() {}
/** @@{@summary illegal} **/
public void m_illegal_escape() {}
/** {@summary legal} text {@summary illegal} **/
public void m2() {}
public void m_illegal_repeat() {}
/** . */
private SummaryTest() { }

View File

@ -2,6 +2,15 @@ SummaryTest.java:14: warning: invalid use of @summary
/** <p> {@summary illegal} **/
^
SummaryTest.java:17: warning: invalid use of @summary
/** text {@summary illegal} **/
^
SummaryTest.java:20: warning: invalid use of @summary
/** &amp;{@summary illegal} **/
^
SummaryTest.java:23: warning: invalid use of @summary
/** @@{@summary illegal} **/
^
SummaryTest.java:26: warning: invalid use of @summary
/** {@summary legal} text {@summary illegal} **/
^
2 warnings
5 warnings

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2023, 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 8300914
* @summary Allow `@` as an escape in documentation comments
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build DocCommentTester
* @run main DocCommentTester AtEscapeTest.java
*/
class AtEscapeTest {
/**
* abc
* @@tag
* def
*/
void escape_block_tag() { }
/*
DocComment[DOC_COMMENT, pos:1
firstSentence: 3
Text[TEXT, pos:1, abc|_]
Escape[ESCAPE, pos:6, @]
Text[TEXT, pos:8, tag|_def]
body: empty
block tags: empty
]
*/
/**
* abc {@@tag} def
*/
void escape_inline_tag() { }
/*
DocComment[DOC_COMMENT, pos:1
firstSentence: 3
Text[TEXT, pos:1, abc_{]
Escape[ESCAPE, pos:6, @]
Text[TEXT, pos:8, tag}_def]
body: empty
block tags: empty
]
*/
/**
* abc /* def *@/ ghi
*/
void escape_end_comment() { }
/*
DocComment[DOC_COMMENT, pos:1
firstSentence: 3
Text[TEXT, pos:1, abc_/*_def_*]
Escape[ESCAPE, pos:13, /]
Text[TEXT, pos:15, _ghi]
body: empty
block tags: empty
]
*/
/**
abc
@* def
ghi
*/
void escape_asterisk() { }
/*
DocComment[DOC_COMMENT, pos:5
firstSentence: 3
Text[TEXT, pos:5, abc|_____]
Escape[ESCAPE, pos:14, *]
Text[TEXT, pos:16, _def|_____ghi]
body: empty
block tags: empty
]
*/
/**
* abc.
* not an escaped tag @@tag;
* xyz.
*/
void not_escaped_tag() { }
/*
DocComment[DOC_COMMENT, pos:1
firstSentence: 1
Text[TEXT, pos:1, abc.]
body: 1
Text[TEXT, pos:7, not_an_escaped_tag_@@tag;|_xyz.]
block tags: empty
]
*/
/**
* abc.
* not an escaped asterisk @*;
* xyz.
*/
void not_escaped_asterisk() { }
/*
DocComment[DOC_COMMENT, pos:1
firstSentence: 1
Text[TEXT, pos:1, abc.]
body: 1
Text[TEXT, pos:7, not_an_escaped_asterisk_@*;|_xyz.]
block tags: empty
]
*/
/**
* abc.
* not an escaped solidus @/.
* xyz.
*/
void not_escaped_solidus() { }
/*
DocComment[DOC_COMMENT, pos:1
firstSentence: 1
Text[TEXT, pos:1, abc.]
body: 1
Text[TEXT, pos:7, not_an_escaped_solidus_@/.|_xyz.]
block tags: empty
]
*/
}

View File

@ -462,6 +462,11 @@ public class DocCommentTester {
return null;
}
public Void visitEscape(EscapeTree node, Void p) {
header(node, node.getBody());
return null;
}
public Void visitHidden(HiddenTree node, Void p) {
header(node);
indent(+1);
@ -933,7 +938,7 @@ public class DocCommentTester {
* @return the normalized content
*/
String normalize(String s, boolean normalizeTags) {
String s2 = s.trim().replaceFirst("\\.\\s*\\n *@", ".\n@");
String s2 = s.trim().replaceFirst("\\.\\s*\\n *@(?![@*])", ".\n@");
StringBuilder sb = new StringBuilder();
Pattern p = Pattern.compile("(?i)\\{@([a-z][a-z0-9.:-]*)( )?");
Matcher m = p.matcher(s2);
@ -952,7 +957,7 @@ public class DocCommentTester {
}
String normalizeFragment(String s) {
return s.replaceAll("\n[ \t]+@", "\n@");
return s.replaceAll("\n[ \t]+@(?![@*])", "\n@");
}
int copyLiteral(String s, int start, StringBuilder sb) {