mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-389345 javadoc: better handling of @deprecated tag with mk comments
Also, `MissingJavadocInspection` is now enabled by default. (cherry picked from commit 153ae8c588cba01c7444cb0eb59df2a8d525cd27) IJ-CR-209257 GitOrigin-RevId: f913c5093788926ee3edbf81fab7571daaf0eb16
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c7265abc99
commit
99a88337f7
+54
-11
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.migration;
|
||||
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
import com.intellij.codeInsight.javadoc.JavaDocUtil;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
@@ -8,11 +9,19 @@ import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.util.text.Strings;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiJavaDocumentedElement;
|
||||
import com.intellij.psi.PsiModifierList;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiNameValuePair;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
|
||||
@@ -91,9 +100,27 @@ public final class MarkdownDocumentationCommentsMigrationInspection extends Base
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
if (element instanceof PsiDocToken) element = element.getParent();
|
||||
if (!(element instanceof PsiDocComment)) return;
|
||||
String markdown = convertToMarkdown(appendElementText(element, new StringBuilder()).toString());
|
||||
String indent = getElementIndent(element);
|
||||
if (!(element instanceof PsiDocComment docComment)) return;
|
||||
|
||||
String result = convertAndPostProcess(element);
|
||||
Document document = element.getContainingFile().getFileDocument();
|
||||
|
||||
if (addDeprecatedAnnotationIfNecessary(project, docComment)) {
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
|
||||
}
|
||||
|
||||
int startOffset = element.getTextOffset();
|
||||
int endOffset = element.getNextSibling() instanceof PsiWhiteSpace whiteSpace
|
||||
? whiteSpace.getTextOffset() + whiteSpace.getTextLength()
|
||||
: startOffset + element.getTextLength();
|
||||
document.replaceString(startOffset, endOffset, result);
|
||||
}
|
||||
|
||||
/// @return The converted and indent post-processed Markdown comment
|
||||
private static String convertAndPostProcess(PsiElement docComment) {
|
||||
String markdown = convertToMarkdown(appendElementText(docComment, new StringBuilder()).toString());
|
||||
|
||||
String indent = getElementIndent(docComment);
|
||||
String[] lines = markdown.split("\n");
|
||||
StringBuilder result = new StringBuilder(markdown.length() + (indent.length() + 4) * lines.length);
|
||||
for (String line : lines) {
|
||||
@@ -108,13 +135,7 @@ public final class MarkdownDocumentationCommentsMigrationInspection extends Base
|
||||
result.append('\n');
|
||||
}
|
||||
result.append(indent);
|
||||
|
||||
Document document = element.getContainingFile().getFileDocument();
|
||||
int startOffset = element.getTextOffset();
|
||||
int endOffset = element.getNextSibling() instanceof PsiWhiteSpace whiteSpace
|
||||
? whiteSpace.getTextOffset() + whiteSpace.getTextLength()
|
||||
: startOffset + element.getTextLength();
|
||||
document.replaceString(startOffset, endOffset, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static StringBuilder appendElementText(@NotNull PsiElement element, StringBuilder result) {
|
||||
@@ -197,7 +218,7 @@ public final class MarkdownDocumentationCommentsMigrationInspection extends Base
|
||||
|
||||
String result = visitor.getResult();
|
||||
|
||||
// (mbo) Not the proudest of this one but some combinations of Javadoc tag and HTML cannot reasonnably be handled with jsoup
|
||||
// (mbo) Not the proudest of this one, but some combinations of Javadoc tag and HTML cannot reasonably be handled with jsoup
|
||||
// unescape element between internal HTML tags. It is expected that internal tags are not nested.
|
||||
Matcher internalTagMatcher = Pattern.compile(
|
||||
"<(?:%s|%s)>(.*?)</(?:%s|%s)>".formatted(
|
||||
@@ -328,6 +349,28 @@ public final class MarkdownDocumentationCommentsMigrationInspection extends Base
|
||||
.append(']');
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the annotation if necessary, as the deprecated tag alone is not enough to indicate deprecation
|
||||
/// according to the javadoc Markdown specs
|
||||
///
|
||||
/// @return Whether the annotation was added (or is already there)
|
||||
@ApiStatus.Internal
|
||||
private static boolean addDeprecatedAnnotationIfNecessary(Project project, PsiDocComment docComment) {
|
||||
if (!JavaFeature.ANNOTATIONS.isSufficient(LanguageLevelProjectExtension.getInstance(project).getLanguageLevel()) ||
|
||||
docComment.findTagByName("deprecated") == null) {
|
||||
return false;
|
||||
}
|
||||
PsiJavaDocumentedElement owner = docComment.getOwner();
|
||||
if (owner instanceof PsiModifierListOwner modifierListOwner) {
|
||||
PsiModifierList modifierList = modifierListOwner.getModifierList();
|
||||
if (modifierList != null) {
|
||||
AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(CommonClassNames.JAVA_LANG_DEPRECATED, PsiNameValuePair.EMPTY_ARRAY,
|
||||
modifierList);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1261,7 +1261,7 @@
|
||||
implementationClass="com.siyeh.ig.javadoc.HtmlTagCanBeJavadocTagInspection" cleanupTool="true"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="MissingDeprecatedAnnotation" bundle="messages.InspectionGadgetsBundle"
|
||||
key="missing.deprecated.annotation.display.name" groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.javadoc.issues" enabledByDefault="false" level="WARNING" runForWholeFile="true"
|
||||
groupKey="group.names.javadoc.issues" enabledByDefault="true" level="WARNING" runForWholeFile="true"
|
||||
implementationClass="com.siyeh.ig.javadoc.MissingDeprecatedAnnotationInspection" cleanupTool="true"/>
|
||||
<globalInspection groupPath="Java" language="JAVA" shortName="MissingPackageInfo" bundle="messages.InspectionGadgetsBundle"
|
||||
key="missing.package.info.display.name" groupBundle="messages.InspectionsBundle"
|
||||
|
||||
@@ -2573,6 +2573,11 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
|
||||
private void generateDeprecatedSection(StringBuilder buffer, PsiDocComment comment) {
|
||||
if (comment.isMarkdownComment() &&
|
||||
comment.getOwner() instanceof PsiModifierListOwner owner &&
|
||||
!owner.hasAnnotation(CommonClassNames.JAVA_LANG_DEPRECATED)) {
|
||||
return;
|
||||
}
|
||||
generateSingleTagSection(buffer, comment, "deprecated", JavaBundle.messagePointer("javadoc.deprecated"));
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +602,7 @@ public final class PsiImplUtil {
|
||||
|
||||
public static boolean isDeprecatedByDocTag(@NotNull PsiJavaDocumentedElement owner) {
|
||||
PsiDocComment docComment = owner.getDocComment();
|
||||
return docComment != null && docComment.findTagByName("deprecated") != null;
|
||||
return docComment != null && !docComment.isMarkdownComment() && docComment.findTagByName("deprecated") != null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
|
||||
import com.intellij.psi.impl.java.stubs.PsiClassStub;
|
||||
import com.intellij.psi.impl.java.stubs.PsiFieldStub;
|
||||
import com.intellij.psi.impl.java.stubs.PsiModifierListStub;
|
||||
import com.intellij.psi.impl.source.tree.JavaDocElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.impl.source.tree.LightTreeUtil;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
@@ -51,6 +52,9 @@ public final class RecordUtil {
|
||||
}
|
||||
|
||||
public static boolean isDeprecatedByDocComment(@NotNull LighterAST tree, @NotNull LighterASTNode comment) {
|
||||
if (comment.getTokenType() == JavaDocElementType.DOC_MARKDOWN_COMMENT) {
|
||||
return false;
|
||||
}
|
||||
String text = LightTreeUtil.toFilteredString(tree, comment, null);
|
||||
if (text.contains(DEPRECATED_TAG)) {
|
||||
JavaDocLexer lexer = new JavaDocLexer(LanguageLevel.HIGHEST);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<html><head><base href="placeholder"></head><body><div class="bottom"><icon src="AllIcons.Nodes.Class"></icon> <a href="psi_element://X"><code><span style="color:#000000;">X</span></code></a></div><div class="definition"><pre><span style="color:#000080;font-weight:bold;">public</span> <span style=""><</span><span style="color:#20999d;">T</span><span style="color:#000080;font-weight:bold;"> extends </span><a href="psi_element://java.lang.Number"><code><span style="color:#000000;">Number</span></code></a><span style="">></span> <span style="color:#000080;font-weight:bold;">double</span> <span style="color:#000000;">calculateMd</span><span style="">(</span><br> <i><span style="color:#808000;">@</span><a href="psi_element://org.jetbrains.annotations.NotNull"><code><span style="color:#808000;">NotNull</span></code></a></i><sup><font color="808080" size="3"><i>i</i></font></sup><a href="inferred.annotations"><icon src="AllIcons.Ide.External_link_arrow"></icon></a> <span style="color:#20999d;">T</span> <span style="color:#000000;">a</span><span style="">,</span>
|
||||
<html><head><base href="placeholder"></head><body><div class="bottom"><icon src="AllIcons.Nodes.Class"></icon> <a href="psi_element://X"><code><span style="color:#000000;">X</span></code></a></div><div class="definition"><pre><span style="color:#808000;">@</span><a href="psi_element://java.lang.Deprecated"><code><span style="color:#808000;">Deprecated</span></code></a>
|
||||
<span style="color:#000080;font-weight:bold;">public</span> <span style=""><</span><span style="color:#20999d;">T</span><span style="color:#000080;font-weight:bold;"> extends </span><a href="psi_element://java.lang.Number"><code><span style="color:#000000;">Number</span></code></a><span style="">></span> <span style="color:#000080;font-weight:bold;">double</span> <span style="color:#000000;">calculateMd</span><span style="">(</span><br> <i><span style="color:#808000;">@</span><a href="psi_element://org.jetbrains.annotations.NotNull"><code><span style="color:#808000;">NotNull</span></code></a></i><sup><font color="808080" size="3"><i>i</i></font></sup><a href="inferred.annotations"><icon src="AllIcons.Ide.External_link_arrow"></icon></a> <span style="color:#20999d;">T</span> <span style="color:#000000;">a</span><span style="">,</span>
|
||||
<i><span style="color:#808000;">@</span><a href="psi_element://org.jetbrains.annotations.NotNull"><code><span style="color:#808000;">NotNull</span></code></a></i><sup><font color="808080" size="3"><i>i</i></font></sup><a href="inferred.annotations"><icon src="AllIcons.Ide.External_link_arrow"></icon></a> <span style="color:#20999d;">T</span> <span style="color:#000000;">b</span><br><span style="">)</span></pre></div><hr><div class="content"><p>Sample method demonstrating all Javadoc tags <br>
|
||||
Returns calculated result. <br>
|
||||
<code><span style="">Math.<wbr>E</span></code> <br>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import java.lang.Math;
|
||||
import java.lang.Deprecated;
|
||||
|
||||
class X {
|
||||
|
||||
@@ -30,6 +31,7 @@ class X {
|
||||
/// @provides Math
|
||||
/// @uses Math
|
||||
/// @hidden
|
||||
@Deprecated
|
||||
public <T extends Number> double calculate<caret>Md(T a, T b) {
|
||||
return a.doubleValue() + b.doubleValue();
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">X</span></pre></div><table class='sections'><p></table>
|
||||
@@ -0,0 +1,2 @@
|
||||
/// @deprecated Oops look at who forgot the annotation
|
||||
class X {}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import java.lang.Deprecated;
|
||||
public class DeprecatedUsages {
|
||||
|
||||
///<caret> @deprecated We before java 5 there were no annotations
|
||||
/// so that how you were expected to deprecate something
|
||||
@Deprecated
|
||||
void onlyTag() {}
|
||||
|
||||
/// @deprecated
|
||||
@Deprecated
|
||||
void emptyTag() {}
|
||||
|
||||
/// @deprecated
|
||||
@Deprecated
|
||||
void emptyTagWithAnnotation() {}
|
||||
|
||||
|
||||
/// @deprecated Markdown Javadoc updated the meaning of the deprecated tag.
|
||||
/// This is a bit of heresy and honestly artificial friction but so be it.
|
||||
@Deprecated
|
||||
void tagWithAnnotation() {}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import java.lang.Deprecated;
|
||||
public class DeprecatedUsages {
|
||||
|
||||
<warning descr="Javadoc comment can be Markdown documentation comment">/**<caret></warning>
|
||||
* @deprecated We before java 5 there were no annotations
|
||||
* so that how you were expected to deprecate something
|
||||
*/
|
||||
void onlyTag() {}
|
||||
|
||||
<warning descr="Javadoc comment can be Markdown documentation comment">/**</warning> @deprecated */
|
||||
void emptyTag() {}
|
||||
|
||||
<warning descr="Javadoc comment can be Markdown documentation comment">/**</warning> @deprecated */
|
||||
@Deprecated
|
||||
void emptyTagWithAnnotation() {}
|
||||
|
||||
|
||||
<warning descr="Javadoc comment can be Markdown documentation comment">/**</warning>
|
||||
* @deprecated Markdown Javadoc updated the meaning of the deprecated tag.
|
||||
* This is a bit of heresy and honestly artificial friction but so be it.
|
||||
*/
|
||||
@Deprecated
|
||||
void tagWithAnnotation() {}
|
||||
}
|
||||
+1
@@ -298,6 +298,7 @@ public class MarkdownDocumentationCommentsMigration {
|
||||
///
|
||||
/// This method will remain in place until finalizers have been removed from
|
||||
/// most existing code.
|
||||
@Deprecated
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
|
||||
@@ -6,6 +6,7 @@ public class MyClass2 extends String implements Runnable{
|
||||
*/
|
||||
int field1 = 0;
|
||||
|
||||
/// @deprecated The annotation is missing so no real deprecation
|
||||
Object field2[];
|
||||
|
||||
java.lang.Object[] field3;
|
||||
|
||||
+1
@@ -268,6 +268,7 @@ public class JavaDocInfoGeneratorTest extends JavaCodeInsightTestCase {
|
||||
public void testWrongfulInnerClassReferences() { doTestClass(); }
|
||||
public void testImplicitConstructor() { doTestClass(); }
|
||||
public void testCommatHtmlEntity() { doTestClass(); }
|
||||
public void testDeprecatedTagNoAnnotationMarkdown() { doTestClass(); }
|
||||
|
||||
public void testRepeatableAnnotations() {
|
||||
useJava8();
|
||||
|
||||
@@ -180,12 +180,18 @@ public class JavaStubsTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
}
|
||||
|
||||
public void test_deprecated_enum_constant() {
|
||||
PsiClass cls = myFixture.addClass("enum Foo { c1, @Deprecated c2, /** @deprecated */ c3 }");
|
||||
PsiClass cls = myFixture.addClass("""
|
||||
enum Foo {
|
||||
c1, @Deprecated c2, /** @deprecated */ c3,
|
||||
/// @deprecated no real deprecation
|
||||
c4
|
||||
}""");
|
||||
assertFalse(((PsiFileImpl)cls.getContainingFile()).isContentsLoaded());
|
||||
|
||||
assertFalse(cls.getFields()[0].isDeprecated());
|
||||
assertTrue(cls.getFields()[1].isDeprecated());
|
||||
assertTrue(cls.getFields()[2].isDeprecated());
|
||||
assertFalse(cls.getFields()[3].isDeprecated());
|
||||
|
||||
assertFalse(((PsiFileImpl)cls.getContainingFile()).isContentsLoaded());
|
||||
}
|
||||
|
||||
@@ -308,6 +308,9 @@ public class SrcRepositoryUseTest extends JavaPsiTestCase {
|
||||
|
||||
PsiField field = aClass.findFieldByName("field1", false);
|
||||
assertTrue(field.isDeprecated());
|
||||
|
||||
PsiField field2 = aClass.findFieldByName("field2", false);
|
||||
assertFalse(field2.isDeprecated());
|
||||
|
||||
teardownLoadingFilter();
|
||||
}
|
||||
|
||||
+3
-2
@@ -2,18 +2,20 @@
|
||||
package com.siyeh.ig.migration;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import com.siyeh.ig.LightJavaInspectionTestCase;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
@TestDataPath("$CONTENT_ROOT/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration")
|
||||
public class MarkdownDocumentationCommentsMigrationInspectionTest extends LightJavaInspectionTestCase {
|
||||
|
||||
public void testMarkdownDocumentationCommentsMigration() { check(); }
|
||||
public void testReferencesNoEscape() { check(); }
|
||||
public void testCodeBlocks() { check(); }
|
||||
|
||||
public void testDeprecatedUsages() { check(); }
|
||||
|
||||
@Override
|
||||
protected @Nullable InspectionProfileEntry getInspection() {
|
||||
@@ -24,5 +26,4 @@ public class MarkdownDocumentationCommentsMigrationInspectionTest extends LightJ
|
||||
doTest();
|
||||
checkQuickFixAll();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user