Skip to content

Commit 67856c0

Browse files
@code and @literal tags behave differently in Markdown
The inline code tag and inline literal tag behave differently in Markdown Fix: #4695
1 parent dbbfaf8 commit 67856c0

3 files changed

Lines changed: 115 additions & 17 deletions

File tree

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2024 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -181,7 +181,6 @@ protected boolean commentParse() {
181181
boolean isDomParser = (this.kind & DOM_PARSER) != 0;
182182
boolean isFormatterParser = (this.kind & FORMATTER_COMMENT_PARSER) != 0;
183183
int lastStarPosition = -1;
184-
boolean isTagElementClose = false;
185184

186185
// Init scanner position
187186
this.markdown = this.source[this.javadocStart + 1] == '/';
@@ -350,9 +349,6 @@ protected boolean commentParse() {
350349
// Fix bug 51650
351350
this.textStart = -1;
352351
this.markdownHelper.resetAtLineEnd();
353-
if (this.inlineTagStarted && this.markdown) {
354-
isTagElementClose = true;
355-
}
356352
break;
357353
case '}' :
358354
if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) {
@@ -366,6 +362,11 @@ protected boolean commentParse() {
366362
}
367363
}
368364
boolean isLiteralOrCode = this.tagValue == TAG_LITERAL_VALUE || this.tagValue == TAG_CODE_VALUE;
365+
boolean shouldCloseInlineTag =
366+
this.inlineTagStarted
367+
&& !considerTagAsPlainText
368+
&& !(isLiteralOrCode && openingBraces != 0);
369+
369370
if (this.inlineTagStarted) {
370371
textEndPosition = this.index - 1;
371372
boolean treatAsText= considerTagAsPlainText || (this.inlineReturn && this.inlineReturnOpenBraces > 0);
@@ -378,12 +379,8 @@ protected boolean commentParse() {
378379
}
379380
if (!isFormatterParser && !treatAsText && (!this.inlineReturn || this.inlineReturnOpenBraces <= 0))
380381
this.textStart = this.index;
381-
if (!isTagElementClose && this.markdown) { //The comment parser should create a TagElement only if the previous one is closed - markdown.
382+
if (shouldCloseInlineTag) {
382383
setInlineTagStarted(false);
383-
} else if (!this.markdown) {
384-
if (!(isLiteralOrCode && openingBraces != 0)) {
385-
setInlineTagStarted(false);
386-
}
387384
}
388385
if (this.inlineReturn) {
389386
if (this.inlineReturnOpenBraces > 0) {

org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterMarkdownTest.java

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,15 +1854,13 @@ class IllegelTagElement {}
18541854
Javadoc javadoc = typedeclaration.getJavadoc();
18551855
List<TagElement> te = javadoc.tags();
18561856
assertEquals("TagElement length is grater than one", 1, te.size());
1857-
List<TagElement> tes = (te.get(0)).fragments();
1858-
assertEquals("inner TagElement length is grater than one", 1, tes.size());
1859-
assertEquals("TagName", "@link", tes.get(0).getTagName());
1860-
List<?> fragments = tes.get(0).fragments();
1857+
List<?> tes = (te.get(0)).fragments();
1858+
assertEquals("TagName", "@link", ((TagElement)tes.get(0)).getTagName());
1859+
List<?> fragments = ((TagElement)tes.get(0)).fragments();
18611860
assertEquals("fragments count does not match", 2, fragments.size());
18621861
assertTrue(fragments.get(0) instanceof MethodRef);
18631862
assertTrue(fragments.get(1) instanceof TextElement);
1864-
assertEquals("Incorrect text", "value", fragments.get(1).toString());
1865-
assertEquals("Incorrect name", "#getValue()", fragments.get(0).toString());
1863+
assertEquals("Incorrect TagElement", 1, ((TextElement) (tes.get(1))).getFlags() & ASTNode.MALFORMED); //MALFOUND flag
18661864
}
18671865
}
18681866

@@ -2234,4 +2232,106 @@ public class Markdown{}
22342232
assertEquals("incorrect Fifth tagElement", ASTNode.TAG_ELEMENT, tags.get(4).getNodeType());
22352233
}
22362234
}
2235+
2236+
public void testInconsistencyInCodeAndLiteralTagsMarkdown4609_01() throws JavaModelException {
2237+
String source = """
2238+
/// Performs:
2239+
///
2240+
/// {@code
2241+
/// for (String s : strings) {
2242+
/// if (s.equals(value)) {
2243+
/// return 0;
2244+
/// }
2245+
/// if (s.startsWith(value)) {
2246+
/// return 1;
2247+
/// }
2248+
/// return -1;
2249+
/// }
2250+
/// }
2251+
/// The general contract of `hashCode` is:
2252+
///
2253+
/// - Whenever it is invoked on the same object more than once during
2254+
/// an execution of a Java application, the `hashCode` method
2255+
/// must consistently return the same integer, provided no information
2256+
/// used in `equals` comparisons on the object is modified.
2257+
/// This integer need not remain consistent from one execution of an
2258+
/// application to another execution of the same application.
2259+
/// - If two objects are equal according to the
2260+
/// [equals][#equals(Object)] method, then calling the
2261+
/// `hashCode` method on each of the two objects must produce the
2262+
/// same integer result.
2263+
/// - It is _not_ required that if two objects are unequal
2264+
/// according to the [equals][#equals(Object)] method, then
2265+
/// calling the `hashCode` method on each of the two objects
2266+
/// must produce distinct integer results. However, the programmer
2267+
/// should be aware that producing distinct integer results for
2268+
/// unequal objects may improve the performance of hash tables.
2269+
public class Markdown{}
2270+
""";
2271+
this.workingCopies = new ICompilationUnit[1];
2272+
this.workingCopies[0] = getWorkingCopy("/Converter_25/src/markdown/Markdown.java", source, null);
2273+
if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
2274+
CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true);
2275+
TypeDeclaration typedeclaration = (TypeDeclaration) compilUnit.types().get(0);
2276+
Javadoc javadoc = typedeclaration.getJavadoc();
2277+
List<TagElement> tags = javadoc.tags();
2278+
List<ASTNode> frags = tags.get(0).fragments();
2279+
2280+
assertEquals("Incorrect Frags", 22, frags.size());
2281+
assertEquals("Invalid element", ASTNode.TAG_ELEMENT, frags.get(1).getNodeType());
2282+
assertEquals("Invalid Text element content", "The general contract of `hashCode` is:", frags.get(2).toString());
2283+
}
2284+
2285+
}
2286+
2287+
public void testInconsistencyInCodeAndLiteralTagsMarkdown4609_02() throws JavaModelException {
2288+
String source = """
2289+
/// Performs:
2290+
///
2291+
/// {@literal
2292+
/// for (String s : strings) {
2293+
/// if (s.equals(value)) {
2294+
/// return 0;
2295+
/// }
2296+
/// if (s.startsWith(value)) {
2297+
/// return 1;
2298+
/// }
2299+
/// return -1;
2300+
/// }
2301+
/// }
2302+
/// The general contract of `hashCode` is:
2303+
///
2304+
/// - Whenever it is invoked on the same object more than once during
2305+
/// an execution of a Java application, the `hashCode` method
2306+
/// must consistently return the same integer, provided no information
2307+
/// used in `equals` comparisons on the object is modified.
2308+
/// This integer need not remain consistent from one execution of an
2309+
/// application to another execution of the same application.
2310+
/// - If two objects are equal according to the
2311+
/// [equals][#equals(Object)] method, then calling the
2312+
/// `hashCode` method on each of the two objects must produce the
2313+
/// same integer result.
2314+
/// - It is _not_ required that if two objects are unequal
2315+
/// according to the [equals][#equals(Object)] method, then
2316+
/// calling the `hashCode` method on each of the two objects
2317+
/// must produce distinct integer results. However, the programmer
2318+
/// should be aware that producing distinct integer results for
2319+
/// unequal objects may improve the performance of hash tables.
2320+
public class Markdown{}
2321+
""";
2322+
this.workingCopies = new ICompilationUnit[1];
2323+
this.workingCopies[0] = getWorkingCopy("/Converter_25/src/markdown/Markdown.java", source, null);
2324+
if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
2325+
CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true);
2326+
TypeDeclaration typedeclaration = (TypeDeclaration) compilUnit.types().get(0);
2327+
Javadoc javadoc = typedeclaration.getJavadoc();
2328+
List<TagElement> tags = javadoc.tags();
2329+
List<ASTNode> frags = tags.get(0).fragments();
2330+
2331+
assertEquals("Incorrect Frags", 22, frags.size());
2332+
assertEquals("Invalid element", ASTNode.TAG_ELEMENT, frags.get(1).getNodeType());
2333+
assertEquals("Invalid Text element content", "The general contract of `hashCode` is:", frags.get(2).toString());
2334+
}
2335+
2336+
}
22372337
}

org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2025 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -2426,6 +2426,7 @@ private Javadoc convert(org.eclipse.jdt.internal.compiler.ast.Javadoc javadoc) {
24262426
for (int i = 1; i < fragments.size(); i++) {
24272427
ASTNode cur = fragments.get(i);
24282428
if (cur.getStartPosition() <= prev.getStartPosition()) cur.setFlags(ASTNode.MALFORMED);
2429+
else if ((cur.getStartPosition() + cur.getLength()) == (prev.getStartPosition() + prev.getLength())) cur.setFlags(ASTNode.MALFORMED);
24292430
prev = cur;
24302431
}
24312432
}

0 commit comments

Comments
 (0)