From 692f53aee0b2ed175e3eeac5d45518d00be0c65f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 24 Apr 2026 14:00:21 +0200 Subject: [PATCH] IDEA-315933 javadoc: handle wrongful inner class references Also handle implicit constructor references GitOrigin-RevId: bd97341568c86156d5b410061e7e989325f85775 --- .../codeInsight/javadoc/JavaDocUtil.java | 18 +++++++++++++++++- .../source/javadoc/PsiDocMethodOrFieldRef.java | 12 ++++++++++-- .../daemonCodeAnalyzer/javaDoc/Link0.java | 10 ---------- .../javadocDeclaration/Link0.java | 2 +- .../WrongfulInnerClassReferences.java | 6 ++++++ .../javadocIG/implicitConstructor.html | 1 + .../javadocIG/implicitConstructor.java | 5 +++++ .../wrongfulInnerClassReferences.html | 1 + .../wrongfulInnerClassReferences.java | 6 ++++++ .../JavadocDeclarationHighlightingTest.java | 1 + .../daemon/JavadocHighlightingTest.java | 1 - .../javadoc/JavaDocInfoGeneratorTest.java | 2 ++ 12 files changed, 50 insertions(+), 15 deletions(-) delete mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/Link0.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/WrongfulInnerClassReferences.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.java diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java index ec30d1402ad3..f0564be72da2 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java @@ -175,12 +175,19 @@ public final class JavaDocUtil { return null; } + /// Find a field or method referenced by `refClass` + /// + /// @param poundIndex the index where `#` is in `refTextCorrected`. private static @Nullable PsiElement findReference(@NotNull PsiClass refClass, PsiElement context, boolean useNavigationElement, String refTextCorrected, int poundIndex) { PsiElement member = findReferencedMember(refClass, refTextCorrected.substring(poundIndex + 1), context); + if (poundIndex >= 0 && member instanceof PsiClass && member != refClass) { + // Psi subclasses class cannot be referred through `#` but the same class implicit constructor can + return null; + } return useNavigationElement && member != null ? member.getNavigationElement() : member; } @@ -239,6 +246,9 @@ public final class JavaDocUtil { for (PsiMethod method : methods) { if (method.getName().equals(name)) return method; } + // Implicit constructor is not part of the methods + if (memberRefText.equals(aClass.getName())) return aClass; + return null; } else { @@ -282,7 +292,13 @@ public final class JavaDocUtil { PsiMethod[] methods = PsiDocMethodOrFieldRef.findMethods(methodSignature, aClass, name, allMethods); - if (methods.length == 0) return null; + if (methods.length == 0) { + // Check for implicit constructor + if (types.length == 0 && name.equals(aClass.getName())) { + return aClass; + } + return null; + } PsiMethod found = methods[0]; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java index 7d90dfb2d02e..4df006d5919e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java @@ -69,6 +69,7 @@ import java.util.Objects; import java.util.Set; /// PsiElement that is a _guarantee_ to reference either a **method** or a **field**. +/// Implicit constructors will resolve to the referring class /// /// @see PsiDocReferenceHolder PsiDocReferenceHolder for other ways to reference methods and fields public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDocTagValue, Constants { @@ -280,8 +281,9 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo } final MethodSignature methodSignature; + List types = null; if (signature != null) { - final List types = new ArrayList<>(signature.length); + types = new ArrayList<>(signature.length); final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(element.getProject()); for (String s : signature) { try { @@ -301,7 +303,13 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo PsiMethod[] methods = findMethods(methodSignature, scope, name, getAllMethods(scope, referringElement)); - if (methods.length == 0) return null; + if (methods.length == 0) { + // Implicit constructor resolves to the class since we can't point to missing code + if (name.equals(scope.getName()) && (types == null || types.isEmpty())) { + return new MethodOrFieldReference(referringElement, new PsiElement[]{scope}); + } + return null; + } return new MethodOrFieldReference(referringElement, methods) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/Link0.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/Link0.java deleted file mode 100644 index c09a5b45e78e..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/Link0.java +++ /dev/null @@ -1,10 +0,0 @@ -class Test { - /** - * @param ppp see {@link #Test} - */ - public void i(int ppp) {} - - class A { - public void foo() {} - } -} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/Link0.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/Link0.java index c09a5b45e78e..16133443c3e4 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/Link0.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/Link0.java @@ -1,6 +1,6 @@ class Test { /** - * @param ppp see {@link #Test} + * @param ppp see {@link #Test} */ public void i(int ppp) {} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/WrongfulInnerClassReferences.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/WrongfulInnerClassReferences.java new file mode 100644 index 000000000000..cb1fcd628899 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javadocDeclaration/WrongfulInnerClassReferences.java @@ -0,0 +1,6 @@ +/// [#Inner] +/// +/// [Outer#Inner] +class Outer { + class Inner{} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.html b/java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.html new file mode 100644 index 000000000000..3749f275751f --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.html @@ -0,0 +1 @@ +
public class BeautifulDespair

I am used to listen to a song called .BeautifulDespair

(variant .BeautifulDespair())

\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.java b/java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.java new file mode 100644 index 000000000000..cbbb222a8fe3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/implicitConstructor.java @@ -0,0 +1,5 @@ + +/// I am used to listen to a song called [#BeautifulDespair] +/// +/// (variant [#BeautifulDespair()]) +public class BeautifulDespair {} diff --git a/java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.html b/java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.html new file mode 100644 index 000000000000..405ba8f27614 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.html @@ -0,0 +1 @@ +
class Outer

Inner

Outer.Inner

\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.java b/java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.java new file mode 100644 index 000000000000..dc6e526efee6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/wrongfulInnerClassReferences.java @@ -0,0 +1,6 @@ +/// [#Inner] +/// +/// [Outer#Inner] +class Outer { + class Inner{} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocDeclarationHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocDeclarationHighlightingTest.java index edf4288989dd..db0fe8457185 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocDeclarationHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocDeclarationHighlightingTest.java @@ -136,6 +136,7 @@ public class JavadocDeclarationHighlightingTest extends LightDaemonAnalyzerTestC public void testCommentsInModifierList() { doTest(); } public void testCommentsBeforeType() { doTest(); } public void testCommentsBeforeName() { doTest(); } + public void testWrongfulInnerClassReferences() { doTest(); } public void testIssueLinksInJavaDoc() { IssueNavigationConfiguration navigationConfiguration = IssueNavigationConfiguration.getInstance(getProject()); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocHighlightingTest.java index b52060749b0f..d73dabf3380d 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavadocHighlightingTest.java @@ -92,7 +92,6 @@ public class JavadocHighlightingTest extends LightDaemonAnalyzerTestCase { public void testException3() { doTest(); } public void testException4() { myMissingJavadocInspection.METHOD_SETTINGS.MINIMAL_VISIBILITY = "package"; doTest(); } public void testInheritJavaDoc() { setLanguageLevel(LanguageLevel.JDK_1_3); doTest(); } - public void testLink0() { doTest(); } public void testLinkFromInnerClassToSelfMethod() { doTest(); } public void testValueBadReference() { doTest(); } public void testValueGoodReference() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java index c5d90c9f0e08..a56c547cc1bc 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java @@ -264,6 +264,8 @@ public class JavaDocInfoGeneratorTest extends JavaCodeInsightTestCase { verifyJavadocFor("SecondSealer"); } public void testParameterizedLinks() { doTestClass(); } + public void testWrongfulInnerClassReferences() { doTestClass(); } + public void testImplicitConstructor() { doTestClass(); } public void testRepeatableAnnotations() { useJava8();