From b2194293d0388211bdb2dd86b3ab2cb28858fe6c Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Sun, 18 Aug 2024 16:16:34 +0200 Subject: [PATCH] [23] Parsing details for markdown comments #2824 + escaping of [] inside reference fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2824 --- .../parser/AbstractCommentParser.java | 30 +++++- .../regression/MarkdownCommentsTest.java | 31 +++++- .../tests/dom/ASTConverterMarkdownTest.java | 94 +++++++++++++++---- 3 files changed, 135 insertions(+), 20 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java index 57429c50665..8ad45926d6d 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java @@ -653,12 +653,12 @@ protected Object parseArguments(Object receiver, boolean checkVerifySpaceOrEndCo // Read possible additional type info dim = 0; isVarargs = false; - if (readToken() == TerminalTokens.TokenNameLBRACKET) { + if (readMarkdownEscapedToken(TerminalTokens.TokenNameLBRACKET)) { // array declaration - while (readToken() == TerminalTokens.TokenNameLBRACKET) { + while (readMarkdownEscapedToken(TerminalTokens.TokenNameLBRACKET)) { int dimStart = this.scanner.getCurrentTokenStartPosition(); consumeToken(); - if (readToken() != TerminalTokens.TokenNameRBRACKET) { + if (!readMarkdownEscapedToken(TerminalTokens.TokenNameRBRACKET)) { break nextArg; } consumeToken(); @@ -734,6 +734,10 @@ protected Object parseArguments(Object receiver, boolean checkVerifySpaceOrEndCo } // Something wrong happened => Invalid input + if (this.markdown) { + // skip over bogus token + this.currentTokenType = -1; + } throw Scanner.invalidInput(); } finally { // we have to make sure that this is reset to the previous value even if an exception occurs @@ -3336,6 +3340,26 @@ protected int readToken() throws InvalidInputException { return this.currentTokenType; } + protected boolean readMarkdownEscapedToken(int expectedToken) throws InvalidInputException { + if (!this.markdown) + return readToken() == expectedToken; + if (this.currentTokenType < 0) { + this.tokenPreviousPosition = this.scanner.currentPosition; + if (peekChar() != '\\') + return false; + this.scanner.currentPosition++; + this.currentTokenType = this.scanner.getNextToken(); + if (this.currentTokenType != expectedToken) { + this.scanner.currentPosition = this.tokenPreviousPosition; + this.currentTokenType = -1; + return false; + } + this.index = this.scanner.currentPosition; + this.lineStarted = true; // after having read a token, line is obviously started... + } + return this.currentTokenType == expectedToken; + } + protected int readTokenAndConsume() throws InvalidInputException { int token = readToken(); consumeToken(); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MarkdownCommentsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MarkdownCommentsTest.java index 6ffc762c163..6e72f9b8468 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MarkdownCommentsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MarkdownCommentsTest.java @@ -301,6 +301,7 @@ public void test010() { public class X { /// Some text here without the necessary tags for main method /// @param arguments array of strings + /// @return java.lang.Str -- should not raise an error /// no tags here public static void main(String[] arguments) { @@ -309,7 +310,7 @@ public static void main(String[] arguments) { } """, }, "----------\n" + - "1. ERROR in X.java (at line 6)\n" + + "1. ERROR in X.java (at line 7)\n" + " public static void main(String[] arguments) {\n" + " ^^^^^^^^^\n" + "Javadoc: Missing tag for parameter arguments\n" + @@ -596,4 +597,32 @@ public static void main(String[] args) { this.reportMissingJavadocTags = bkup; } } + public void test021() { + // arrays in method reference lack escaping. + // TODO specific error message? + String bkup = this.reportMissingJavadocTags; + try { + this.reportMissingJavadocTags = CompilerOptions.IGNORE; + this.runNegativeTest(new String[] { "X.java", + """ + /// + /// Reference to method with array parameter: [#main(String[])] + /// + public class X { + public static void main(String[] args) { + System.out.println("Hello"); + } + } + """, }, + "----------\n" + + "1. ERROR in X.java (at line 2)\n" + + " /// Reference to method with array parameter: [#main(String[])]\n" + + " ^^^^^^^^\n" + + "Javadoc: Invalid parameters declaration\n" + + "----------\n", + JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); + } finally { + this.reportMissingJavadocTags = bkup; + } + } } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterMarkdownTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterMarkdownTest.java index 028b2cc2f3b..5ca2ccc682e 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterMarkdownTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterMarkdownTest.java @@ -1337,6 +1337,27 @@ private void verifyJavadoc(Javadoc docComment) { } */ + protected void assertTagsAndTexts(List tagList, String[] tags, String[][] liness) { + assertEquals(this.prefix+"Wrong number of tags", tags.length, tagList.size()); + + for (int i = 0; i < liness.length; i++) { + ASTNode tagNode = tagList.get(i); + String tag = tags[i]; + assertEquals(this.prefix+"Invalid type for fragment ["+tagNode+"]", ASTNode.TAG_ELEMENT, tagNode.getNodeType()); + TagElement tagElement = (TagElement) tagNode; + assertEquals(this.prefix+"Invalid tag", tag, tagElement.getTagName()); + List fragments = tagElement.fragments(); + String[] lines = liness[i]; + assertEquals(this.prefix+"Wrong number of fragments", lines.length, fragments.size()); + for (int j = 0; j < lines.length; j++) { + ASTNode fragment = fragments.get(j); + String line = lines[j]; + assertEquals(this.prefix+"Invalid type for fragment ["+fragment+"]", ASTNode.TEXT_ELEMENT, fragment.getNodeType()); + assertEquals(this.prefix+"Wrong text content", line, ((TextElement) fragment).getText()); + } + } + } + /** * Check javadoc for MethodDeclaration */ @@ -3498,23 +3519,64 @@ void paraAfterCode() { } } } - protected void assertTagsAndTexts(List tagList, String[] tags, String[][] liness) { - assertEquals(this.prefix+"Wrong number of tags", tags.length, tagList.size()); + public void testGH2808_linkWithArrayReference() throws JavaModelException { + // for a negative variant see org.eclipse.jdt.core.tests.compiler.regression.MarkdownCommentsTest.test021() + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Converter_23/src/markdown/gh2808/LinkWithArray.java", + """ + package markdown.gh2808; - for (int i = 0; i < liness.length; i++) { - ASTNode tagNode = tagList.get(i); - String tag = tags[i]; - assertEquals(this.prefix+"Invalid type for fragment ["+tagNode+"]", ASTNode.TAG_ELEMENT, tagNode.getNodeType()); - TagElement tagElement = (TagElement) tagNode; - assertEquals(this.prefix+"Invalid tag", tag, tagElement.getTagName()); - List fragments = tagElement.fragments(); - String[] lines = liness[i]; - assertEquals(this.prefix+"Wrong number of fragments", lines.length, fragments.size()); - for (int j = 0; j < lines.length; j++) { - ASTNode fragment = fragments.get(j); - String line = lines[j]; - assertEquals(this.prefix+"Invalid type for fragment ["+fragment+"]", ASTNode.TEXT_ELEMENT, fragment.getNodeType()); - assertEquals(this.prefix+"Wrong text content", line, ((TextElement) fragment).getText()); + /// + /// Simple escaped link [#m1(int\\[\\])]. + /// Escaped link with custom text [method 1][#m1(int\\[\\])]. + /// + public class LinkWithArray { + public void m1(int[] i) {} + } + """ + ); + CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true); + if (this.docCommentSupport.equals(JavaCore.ENABLED)) { + List unitComments = compilUnit.getCommentList(); + assertEquals("Wrong number of comments", 1, unitComments.size()); + + Comment comment = (Comment) unitComments.get(0); + assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC); + List tagList = ((Javadoc) comment).tags(); + assertEquals("Should be one tag element", 1, tagList.size()); + TagElement tagElement = (TagElement) tagList.get(0); + tagList = tagElement.fragments(); + + String[] tags = { + null, + "@link", + null, + null, + "@link", + null + }; + String[] lines = { + "Simple escaped link ", + "m1(int [])", + ".", + "Escaped link with custom text ", + "method 1", + "." + }; + for (int i = 0; i < lines.length; i++) { + String tag = tags[i]; + String line = lines[i]; + ASTNode elem = (ASTNode) tagList.get(i); + if (tag != null) { + assertEquals("Node type", ASTNode.TAG_ELEMENT, elem.getNodeType()); + assertEquals("Tag name", tag, ((TagElement) elem).getTagName()); + elem = (ASTNode) ((TagElement) elem).fragments().get(0); + if (!(elem instanceof TextElement)) + continue; // @link without custom text + } else { + assertEquals("Node type", ASTNode.TEXT_ELEMENT, elem.getNodeType()); + } + assertEquals("Text", line, ((TextElement) elem).getText()); } } }