From 52d1d2ea6c1f208b4c1d74ae460e75fe407c15d7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 2 Jan 2026 18:52:55 +0100 Subject: [PATCH] IDEA-379405 javadoc: fix parser improperly creating a broken reference. Also fixes some jdoc parsing and rendering issues. Also fixed some translation issues from the first shot related to said issue. #IDEA-379405 Fixed GitOrigin-RevId: 1daea4e1b9fd809a8f98ac7bfed1e5b09eb7e89a --- .../javadoc/PsiMarkdownReferenceLinkImpl.java | 24 ++++++++----- .../java/syntax/parser/JavaDocParser.kt | 10 +++--- .../javadocIG/markdownReferenceLink.html | 2 +- .../javadocIG/markdownReferenceLink.java | 10 ++++-- .../javadocParsing/LinkTag9Markdown.txt | 34 +++++++++---------- .../javadocParsing/LinkTag9Markdown_node.txt | 26 +++++++------- .../ReferenceLinkMarkdown13.java | 2 +- .../ReferenceLinkMarkdown13.txt | 17 ++++++++-- .../ReferenceLinkMarkdown13_node.txt | 17 ++++++++-- .../resources/messages/JavaBundle.properties | 4 +-- 10 files changed, 89 insertions(+), 57 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiMarkdownReferenceLinkImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiMarkdownReferenceLinkImpl.java index 7a7958df6af3..1afd55c55b1c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiMarkdownReferenceLinkImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiMarkdownReferenceLinkImpl.java @@ -5,6 +5,7 @@ import com.intellij.psi.JavaElementVisitor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.impl.source.tree.CompositePsiElement; +import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.javadoc.PsiMarkdownReferenceLink; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,21 +33,28 @@ public class PsiMarkdownReferenceLinkImpl extends CompositePsiElement implements @Override public @Nullable PsiElement getLabel() { - if (getChildren().length >= 1) { - return getChildren()[1]; - } - // malformed/incomplete link - return null; + // returns `null` malformed/incomplete link + return getChildAt(1); } @Override public boolean isShortLink() { - return getChildren().length < 4; + return countChildren(null) < 5; } @Override public @Nullable PsiElement getLinkElement() { - if (isShortLink()) return getLabel(); - return getChildren()[4]; + int childrenCount = countChildren(null); + return getChildAt(childrenCount - 2); + } + + /// Utility function, get PsiElement at specific index (if it exists) + private @Nullable PsiElement getChildAt(int index) { + TreeElement child = getFirstChildNode(); + for (int i = 0; i < index; i++) { + child = child.getTreeNext(); + if (child == null) return null; + } + return child == null ? null : child.getPsi(); } } diff --git a/java/java-syntax/src/com/intellij/java/syntax/parser/JavaDocParser.kt b/java/java-syntax/src/com/intellij/java/syntax/parser/JavaDocParser.kt index caa8ee1b4336..f223fcbe5c5c 100644 --- a/java/java-syntax/src/com/intellij/java/syntax/parser/JavaDocParser.kt +++ b/java/java-syntax/src/com/intellij/java/syntax/parser/JavaDocParser.kt @@ -320,7 +320,6 @@ class JavaDocParser( } private fun parseMarkdownReference() { - val refStart = builder.mark() val moduleMarker = parseModuleRef(builder.mark()) var referenceParsed = false var referenceEnded = false @@ -328,12 +327,12 @@ class JavaDocParser( if (getTokenType() === JavaDocSyntaxTokenType.DOC_RBRACKET) { if (moduleMarker == null) { - refStart.drop() return - } else { - referenceEnded = true } + referenceEnded = true } + moduleMarker?.done(JavaDocSyntaxElementType.DOC_TAG_VALUE_ELEMENT) + val refStart = builder.mark() if (!referenceEnded && getTokenType() !== JavaDocSyntaxTokenType.DOC_SHARP && getTokenType() !== JavaDocSyntaxTokenType.DOC_DOUBLE_SHARP) { builder.remapCurrentToken(JavaDocSyntaxElementType.DOC_REFERENCE_HOLDER) @@ -395,8 +394,7 @@ class JavaDocParser( } } - if (referenceParsed || moduleMarker != null) { - moduleMarker?.done(JavaDocSyntaxElementType.DOC_TAG_VALUE_ELEMENT) + if (referenceParsed) { when (fragmentReference) { true -> refStart.done(JavaDocSyntaxElementType.DOC_FRAGMENT_REF) false -> refStart.done(JavaDocSyntaxElementType.DOC_METHOD_OR_FIELD_REF) diff --git a/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.html b/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.html index e71c87cddeae..e53e588e421b 100644 --- a/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.html +++ b/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.html @@ -1 +1 @@ -
class MarkdownCodeBlock

java.util.HashMap

The default empty collection

Test for context resolution

\ No newline at end of file +
class MarkdownCodeBlock

java.base/

String

String.toString()

java.util.HashMap

The default empty collection

Test for context resolution

\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.java b/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.java index 3f7a22656621..2119a0992520 100644 --- a/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.java +++ b/java/java-tests/testData/codeInsight/javadocIG/markdownReferenceLink.java @@ -1,8 +1,12 @@ - - +/// [java.base/] +/// +/// [java.base/java.lang.String] +/// +/// [java.base/java.lang.String#toString()] +/// /// [java.util.HashMap] /// /// [The default empty collection][java.util.Collections#EMPTY_LIST] -/// +/// /// [Test for context resolution][java.util.Collection#add(Object)] class MarkdownCodeBlock {} \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown.txt index 8b751b7dcbf9..14226658a2dc 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown.txt @@ -29,29 +29,29 @@ PsiJavaFile:LinkTag9Markdown.java PsiDocToken:DOC_COMMENT_DATA('text for the link') PsiDocToken:DOC_RBRACKET(']') PsiDocToken:DOC_LBRACKET('[') + PsiElement(DOC_TAG_VALUE_ELEMENT) + PsiJavaModuleReference + PsiDocToken:DOC_COMMENT_DATA('java.base') + PsiDocToken:DOC_TAG_VALUE_SLASH('/') PsiElement(DOC_FRAGMENT_REF) - PsiElement(DOC_TAG_VALUE_ELEMENT) - PsiJavaModuleReference - PsiDocToken:DOC_COMMENT_DATA('java.base') - PsiDocToken:DOC_TAG_VALUE_SLASH('/') - PsiElement(DOC_REFERENCE_HOLDER) - PsiJavaCodeReferenceElement:java.lang.String - PsiJavaCodeReferenceElement:java.lang - PsiJavaCodeReferenceElement:java - PsiIdentifier:java('java') - PsiReferenceParameterList - - PsiJavaToken:DOT('.') - PsiIdentifier:lang('lang') + PsiElement(DOC_REFERENCE_HOLDER) + PsiJavaCodeReferenceElement:java.lang.String + PsiJavaCodeReferenceElement:java.lang + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') PsiReferenceParameterList PsiJavaToken:DOT('.') - PsiIdentifier:String('String') + PsiIdentifier:lang('lang') PsiReferenceParameterList - PsiDocToken:DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN('##') - PsiElement(DOC_FRAGMENT_NAME) - PsiDocToken:DOC_COMMENT_DATA('fragment-name') + PsiJavaToken:DOT('.') + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiDocToken:DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN('##') + PsiElement(DOC_FRAGMENT_NAME) + PsiDocToken:DOC_COMMENT_DATA('fragment-name') PsiDocToken:DOC_RBRACKET(']') PsiWhiteSpace('\n') PsiModifierList:public diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown_node.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown_node.txt index 111934a0cec1..8623231ff4e8 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown_node.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag9Markdown_node.txt @@ -29,19 +29,15 @@ java.FILE DOC_COMMENT_DATA DOC_RBRACKET DOC_LBRACKET + DOC_TAG_VALUE_ELEMENT + MODULE_REFERENCE + DOC_COMMENT_DATA + DOC_TAG_VALUE_SLASH DOC_FRAGMENT_REF - DOC_TAG_VALUE_ELEMENT - MODULE_REFERENCE - DOC_COMMENT_DATA - DOC_TAG_VALUE_SLASH - DOC_REFERENCE_HOLDER + DOC_REFERENCE_HOLDER + JAVA_CODE_REFERENCE JAVA_CODE_REFERENCE JAVA_CODE_REFERENCE - JAVA_CODE_REFERENCE - IDENTIFIER - REFERENCE_PARAMETER_LIST - - DOT IDENTIFIER REFERENCE_PARAMETER_LIST @@ -49,9 +45,13 @@ java.FILE IDENTIFIER REFERENCE_PARAMETER_LIST - DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN - DOC_FRAGMENT_NAME - DOC_COMMENT_DATA + DOT + IDENTIFIER + REFERENCE_PARAMETER_LIST + + DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN + DOC_FRAGMENT_NAME + DOC_COMMENT_DATA DOC_RBRACKET WHITE_SPACE MODIFIER_LIST diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.java b/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.java index 04ad718779de..813663ccc2ff 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.java +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.java @@ -1,2 +1,2 @@ -/// [link to module][java.base/] +/// [link to module][java.base/String#toString()] class C {} \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.txt index 90bc309d4c15..c3d53b31072f 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13.txt @@ -11,11 +11,22 @@ PsiJavaFile:ReferenceLinkMarkdown13.java PsiDocToken:DOC_COMMENT_DATA('link to module') PsiDocToken:DOC_RBRACKET(']') PsiDocToken:DOC_LBRACKET('[') + PsiElement(DOC_TAG_VALUE_ELEMENT) + PsiJavaModuleReference + PsiDocToken:DOC_COMMENT_DATA('java.base') + PsiDocToken:DOC_TAG_VALUE_SLASH('/') PsiElement(DOC_METHOD_OR_FIELD_REF) + PsiElement(DOC_REFERENCE_HOLDER) + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') + PsiDocToken:DOC_TAG_VALUE_TOKEN('toString') + PsiDocToken:DOC_LPAREN('(') PsiElement(DOC_TAG_VALUE_ELEMENT) - PsiJavaModuleReference - PsiDocToken:DOC_COMMENT_DATA('java.base') - PsiDocToken:DOC_TAG_VALUE_SLASH('/') + + PsiDocToken:DOC_RPAREN(')') PsiDocToken:DOC_RBRACKET(']') PsiWhiteSpace('\n') PsiModifierList: diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13_node.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13_node.txt index cf3649452e9c..ff552bc56b1a 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13_node.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/ReferenceLinkMarkdown13_node.txt @@ -11,11 +11,22 @@ java.FILE DOC_COMMENT_DATA DOC_RBRACKET DOC_LBRACKET + DOC_TAG_VALUE_ELEMENT + MODULE_REFERENCE + DOC_COMMENT_DATA + DOC_TAG_VALUE_SLASH DOC_METHOD_OR_FIELD_REF + DOC_REFERENCE_HOLDER + JAVA_CODE_REFERENCE + IDENTIFIER + REFERENCE_PARAMETER_LIST + + DOC_TAG_VALUE_SHARP_TOKEN + DOC_TAG_VALUE_TOKEN + DOC_LPAREN DOC_TAG_VALUE_ELEMENT - MODULE_REFERENCE - DOC_COMMENT_DATA - DOC_TAG_VALUE_SLASH + + DOC_RPAREN DOC_RBRACKET WHITE_SPACE MODIFIER_LIST diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index e106387c74db..56b134be6e79 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -1174,7 +1174,7 @@ quickfix.family.avoid.mutation.using.stream.api=Avoid mutation using Stream API quickfix.family.change.javadoc.to=Change to \u2026 quickfix.family.find.cause=Find cause quickfix.family.remove.javadoc.tag=Remove tag -quickfix.family.remove.javadoc.reference=Remove tag +quickfix.family.remove.javadoc.reference=Remove reference quickfix.family.remove.redundant.parameter=Remove redundant parameter quickfix.family.remove.redundant.parameter.types=Remove redundant parameter types quickfix.family.replace.cast.type=Replace cast type @@ -1195,7 +1195,7 @@ quickfix.text.0.may.not.work.before.jdk.11.0.2={0} (may not work before JDK 11.0 quickfix.text.avoid.mutation.using.stream.api.0.operation=Avoid mutation using Stream API ''{0}()'' operation quickfix.text.remove.javadoc.0=Remove ''@{0}'' tag quickfix.text.remove.javadoc.0.1=Remove @{0} {1} -quickfix.text.remove.javadoc.reference=Remove ''@{0}'' reference +quickfix.text.remove.javadoc.reference=Remove ''{0}'' reference quickfix.text.remove.not.null.annotation=Remove non-null annotation quickfix.text.replace.0.stream.with.1.2=Replace {0}.stream() with {1}.{2}() quickfix.text.replace.collect.0.with.1.2=Replace ''collect({0}())'' with ''{1}''{2}