diff --git a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties index e6a27416061e..935758e5f456 100644 --- a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties +++ b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties @@ -2365,6 +2365,9 @@ inspection.deconstruction.can.be.used.display.name=Record pattern can be used inspection.deconstruction.can.be.used.message=Can be replaced with record pattern inspection.deconstruction.can.be.used.fix.family.name=Replace with record pattern +markdown.documentation.comments.migration.display.name=Javadoc comment can be Markdown documentation comment +markdown.documentation.comments.migration.fix=Convert to Markdown documentation comment + array.hash.code.fix.family.name=Replace with 'Arrays.hashCode()' call objects.hash.fix.family.name=Wrap with 'Arrays.hashCode()' unqualified.static.access.fix.family.name=Qualify static access diff --git a/java/java-analysis-impl/src/com/siyeh/ig/migration/MarkdownDocumentationCommentsMigrationInspection.java b/java/java-analysis-impl/src/com/siyeh/ig/migration/MarkdownDocumentationCommentsMigrationInspection.java new file mode 100644 index 000000000000..899600641802 --- /dev/null +++ b/java/java-analysis-impl/src/com/siyeh/ig/migration/MarkdownDocumentationCommentsMigrationInspection.java @@ -0,0 +1,309 @@ +// Copyright 2000-2024 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.codeInspection.LocalQuickFix; +import com.intellij.modcommand.ModPsiUpdater; +import com.intellij.modcommand.PsiUpdateModCommandQuickFix; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaDocTokenType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef; +import com.intellij.psi.javadoc.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +/** + * @author Bas Leijdekkers + */ +final class MarkdownDocumentationCommentsMigrationInspection extends BaseInspection { + @Override + protected @NotNull String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("markdown.documentation.comments.migration.display.name"); + } + + @Override + protected @NotNull LocalQuickFix buildFix(Object... infos) { + return new MarkdownDocumentationCommentsMigrationFix(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MarkdownDocumentationCommentsMigrationVisitor(); + } + + private static class MarkdownDocumentationCommentsMigrationVisitor extends BaseInspectionVisitor { + @Override + public void visitDocComment(@NotNull PsiDocComment comment) { + super.visitDocComment(comment); + PsiElement child = comment.getFirstChild(); + if (!"/**".equals(child.getText())) { + return; + } + registerError(isVisibleHighlight(comment) ? child : comment); + } + } + + private static class MarkdownDocumentationCommentsMigrationFix extends PsiUpdateModCommandQuickFix { + + public static final TokenSet SKIP_TOKENS = TokenSet.create(JavaDocTokenType.DOC_COMMENT_START, JavaDocTokenType.DOC_COMMENT_END); + + @Override + public @NotNull String getFamilyName() { + return InspectionGadgetsBundle.message("markdown.documentation.comments.migration.fix"); + } + + @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; + StringBuilder text = appendCommentText(element, new StringBuilder()); + String markdown = convertToMarkdown(text.toString()); + String indent = getElementIndent(element); + String[] lines = markdown.split("\n"); + StringBuilder result = new StringBuilder(text.length() + (indent.length() + 4) * lines.length); + for (String line : lines) { + if (!result.isEmpty()) { + result.append(indent); + } + result.append("///").append(line).append('\n'); + } + + 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); + } + + private static StringBuilder appendCommentText(@NotNull PsiElement element, StringBuilder result) { + for (@NotNull PsiElement child : element.getChildren()) { + if (isDocToken(child, JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) { + continue; + } + else if (child instanceof PsiDocToken token && SKIP_TOKENS.contains(token.getTokenType())) { + continue; + } + else if (child instanceof PsiInlineDocTag inlineDocTag) { + PsiElement nameElement = inlineDocTag.getNameElement(); + PsiElement next = nameElement.getNextSibling(); + if (next instanceof PsiWhiteSpace && next.getText().contains("\n")) { + result.append("\n "); + } + String name = inlineDocTag.getName(); + if ("code".equals(name)) { + handleCodeInlineDocTag(inlineDocTag, result); + } + else if ("link".equals(name)) { + handleLinkInlineDocTag(inlineDocTag, result); + } + else { + handleGenericInlineDocTag(inlineDocTag, result); + } + continue; + } + if (child instanceof PsiDocTag || child instanceof PsiDocTagValue) { + appendCommentText(child, result); + continue; + } + else if (child instanceof PsiWhiteSpace) { + if (!isDocToken(child.getNextSibling(), JavaDocTokenType.DOC_COMMENT_END)) { + String text = child.getText(); + if (text.contains("\n")) { + if (!result.isEmpty()) { + result.append("\n"); + } + } + else { + result.append(text); + } + } + continue; + } + result.append(child.getText()); + } + return result; + } + + private static String convertToMarkdown(String html) { + int tag = -1; + boolean endTag = false; + boolean newLine = false; + boolean inList = false; + StringBuilder result = new StringBuilder(); + for (int i = 0, length = html.length(); i < length; i++) { + char c = html.charAt(i); + if (tag >= 0) { + if (isLetterOrDigitAscii(c)) { + continue; + } + else if (c == '/') { + if (i == tag + 1) endTag = true; + continue; + } + else if (c == '>') { + String name = html.substring(tag + (endTag ? 2 : 1), (html.charAt(i-1) == '/') ? i - 1 : i); + if ("li".equals(name)) { + if (endTag) { + inList = false; + } + else { + if (" ".equals(result.substring(result.length() - 4))) { + result.delete(result.length() - 4, result.length()); + } + result.append(" - "); + inList = true; + } + } + else if ("em".equals(name) || "i".equals(name)) { + result.append('_'); + } + else if ("b".equals(name) || "strong".equals(name)) { + result.append("**"); + } + else if ("hr".equals(name)) { + result.append("---"); + } + else if ("p".equals(name)) { + if (i + 1 < length && html.charAt(i + 1) != '\n') result.append("\n "); + } + else if ("br".equals(name)) { + result.append(" \n"); + } + else if ("ul".equals(name)) { + if (endTag) inList = false; + } + else { + result.append(html, tag, i + 1); + } + } + else { + result.append(html, tag, i + 1); + } + tag = -1; + endTag = false; + } + else { + if (c == '\n') { + if (newLine && !(i + 2 < length && html.charAt(i + 2) == '@')) { + continue; + } + result.append(inList ? "\n " : "\n"); + newLine = true; + } + else { + if (newLine && inList && c == ' ') continue; + newLine = false; + if (c == '<') { + tag = i; + } + else { + result.append(c); + } + } + } + } + return result.toString(); + } + + private static boolean isLetterOrDigitAscii(char cur) { + return cur >= 'a' && cur <= 'z' || cur >= 'A' && cur <= 'Z' || cur >= '0' && cur <= '9'; + } + + private static String getElementIndent(PsiElement element) { + PsiElement leaf = PsiTreeUtil.prevLeaf(element); + if (!(leaf instanceof PsiWhiteSpace)) { + return ""; + } + String text = leaf.getText(); + final int lineBreak = text.lastIndexOf('\n'); + return text.substring(lineBreak + 1); + } + + private static void handleGenericInlineDocTag(PsiElement element, StringBuilder result) { + PsiElement[] children = element.getChildren(); + if (children.length > 0) { + for (@NotNull PsiElement child : children) { + handleGenericInlineDocTag(child, result); + } + return; + } + if (element instanceof PsiWhiteSpace) { + String text = element.getText(); + if (text.contains("\n")) { + result.append("\n "); + } + else { + result.append(text); + } + return; + } + else if (isDocToken(element, JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) { + return; + } + result.append(element.getText()); + } + + private static void handleCodeInlineDocTag(PsiInlineDocTag inlineDocTag, StringBuilder result) { + result.append('`'); + for (PsiElement dataElement : inlineDocTag.getDataElements()) { + if (dataElement instanceof PsiDocToken) { + result.append(dataElement.getText().trim()); + } + } + result.append('`'); + } + + private static void handleLinkInlineDocTag(PsiInlineDocTag inlineDocTag, StringBuilder result) { + result.append('['); + PsiElement[] dataElements = inlineDocTag.getDataElements(); + boolean dataFound = false; + for (PsiElement dataElement : dataElements) { + if (dataElement instanceof PsiDocToken) { + String text = dataElement.getText().trim(); + if (!text.isEmpty()) { + result.append(text); + dataFound = true; + } + } + } + if (dataFound) result.append("]["); + for (PsiElement dataElement : dataElements) { + if (dataElement instanceof PsiDocMethodOrFieldRef) { + for (@NotNull PsiElement refChild : dataElement.getChildren()) { + if (refChild instanceof PsiDocToken) { + result.append(refChild.getText()); + } + else if (refChild instanceof PsiDocTagValue) { + for (@NotNull PsiElement valueChild : refChild.getChildren()) { + if (valueChild instanceof PsiWhiteSpace) { + if (valueChild.getText().contains("\n")) { + result.append("\n "); + } + continue; + } + if (isDocToken(valueChild, JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) continue; + result.append(valueChild.getText()); + } + } + } + } + else if (!(dataElement instanceof PsiDocToken) && !(dataElement instanceof PsiWhiteSpace)) { + result.append(dataElement.getText()); + } + } + result.append(']'); + } + + private static boolean isDocToken(PsiElement element, IElementType tokenType) { + return element instanceof PsiDocToken token && tokenType == token.getTokenType(); + } + } +} diff --git a/java/java-impl/src/META-INF/InspectionGadgets.xml b/java/java-impl/src/META-INF/InspectionGadgets.xml index a607052da307..d30e4173acd2 100644 --- a/java/java-impl/src/META-INF/InspectionGadgets.xml +++ b/java/java-impl/src/META-INF/InspectionGadgets.xml @@ -1338,6 +1338,10 @@ key="while.can.be.foreach.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.language.level.specific.issues.and.migration.aids5" enabledByDefault="true" level="WARNING" implementationClass="com.siyeh.ig.migration.WhileCanBeForeachInspection"/> + + +Reports Javadoc comments that can be converted to Markdown documentation comments. +

Example:

+

+  /**
+   * For blubbering of the florz.
+   *
+   * Similar to {@link com.deeps.Querp querping} class.
+   */
+  class FlorzBlubber {
+  }
+
+

After the quick-fix is applied:

+

+  /// For blubbering of the florz.
+  /// Similar to [querping][com.deeps.Querp] class.
+  class FlorzBlubber {
+  }
+
+ +

New in 2024.2

+ + + \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration/MarkdownDocumentationCommentsMigration.after.java b/java/java-tests/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration/MarkdownDocumentationCommentsMigration.after.java new file mode 100644 index 000000000000..b8e7377c606d --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration/MarkdownDocumentationCommentsMigration.after.java @@ -0,0 +1,327 @@ +import java.util.HashMap; + +/// test data file +/// --- +/// `System.out.println()` +/// [java.util.ArrayList] +/// [description][java.util.HashMap] +/// @author Bas +public class MarkdownDocumentationCommentsMigration { + /// {@return a hash code value for this object} This method is + /// supported for the benefit of hash tables such as those provided by + /// [HashMap]. + /// + /// The general contract of `hashCode` is: + /// + /// - Whenever it is invoked on the same object more than once during + /// an execution of a Java application, the `hashCode` method + /// must consistently return the same integer, provided no information + /// used in `equals` comparisons on the object is modified. + /// This integer need not remain consistent from one execution of an + /// application to another execution of the same application. + /// - If two objects are equal according to the + /// [equals][#equals(Object)] method, then calling the + /// `hashCode` method on each of the two objects must produce the + /// same integer result. + /// - It is _not_ required that if two objects are unequal + /// according to the [equals][#equals(Object)] method, then + /// calling the `hashCode` method on each of the two objects + /// must produce distinct integer results. However, the programmer + /// should be aware that producing distinct integer results for + /// unequal objects may improve the performance of hash tables. + /// + /// + /// @implSpec As far as is reasonably practical, the `hashCode` method defined + /// by class `Object` returns distinct integers for distinct objects. + /// @apiNote The [hash][#hash(Object...)] and + /// [hashCode][#hashCode(Object)] methods of + /// [Objects] can be used to help construct simple hash codes. + /// @see Object#equals(Object) + /// @see System#identityHashCode +@Override + public int hashCode() { + return super.hashCode(); + } + + /// Indicates whether some other object is "equal to" this one. + /// + /// The `equals` method implements an equivalence relation + /// on non-null object references: + /// + /// - It is _reflexive_: for any non-null reference value + /// `x`, `x.equals(x)` should return + /// `true`. + /// - It is _symmetric_: for any non-null reference values + /// `x` and `y`, `x.equals(y)` + /// should return `true` if and only if + /// `y.equals(x)` returns `true`. + /// - It is _transitive_: for any non-null reference values + /// `x`, `y`, and `z`, if + /// `x.equals(y)` returns `true` and + /// `y.equals(z)` returns `true`, then + /// `x.equals(z)` should return `true`. + /// - It is _consistent_: for any non-null reference values + /// `x` and `y`, multiple invocations of + /// `x.equals(y)` consistently return `true` + /// or consistently return `false`, provided no + /// information used in `equals` comparisons on the + /// objects is modified. + /// - For any non-null reference value `x`, + /// `x.equals(null)` should return `false`. + /// + /// + /// An equivalence relation partitions the elements it operates on + /// into _equivalence classes_; all the members of an + /// equivalence class are equal to each other. Members of an + /// equivalence class are substitutable for each other, at least + /// for some purposes. + /// + /// @param obj the reference object with which to compare. + /// @return `true` if this object is the same as the obj + /// argument; `false` otherwise. + /// @implSpec The `equals` method for class `Object` implements + /// the most discriminating possible equivalence relation on objects; + /// that is, for any non-null reference values `x` and + /// `y`, this method returns `true` if and only + /// if `x` and `y` refer to the same object + /// (`x == y` has the value `true`). + /// + /// In other words, under the reference equality equivalence + /// relation, each equivalence class only has a single element. + /// @apiNote It is generally necessary to override the [hashCode][#hashCode()] + /// method whenever this method is overridden, so as to maintain the + /// general contract for the `hashCode` method, which states + /// that equal objects must have equal hash codes. + /// + /// The two-argument [Objects.equals][#equals(Object, + /// Object)] method implements an equivalence relation + /// on two possibly-null object references. + /// @see #hashCode() + /// @see HashMap +@Override + public boolean equals(final Object obj) { + return super.equals(obj); + } + + /// Creates and returns a copy of this object. The precise meaning + /// of "copy" may depend on the class of the object. The general + /// intent is that, for any object `x`, the expression: + ///
+ ///
+  /// x.clone() != x
+ /// will be true, and that the expression: + ///
+ ///
+  /// x.clone().getClass() == x.getClass()
+ /// will be `true`, but these are not absolute requirements. + /// While it is typically the case that: + ///
+ ///
+  /// x.clone().equals(x)
+ /// will be `true`, this is not an absolute requirement. + /// + /// By convention, the returned object should be obtained by calling + /// `super.clone`. If a class and all of its superclasses (except + /// `Object`) obey this convention, it will be the case that + /// `x.clone().getClass() == x.getClass()`. + /// + /// By convention, the object returned by this method should be independent + /// of this object (which is being cloned). To achieve this independence, + /// it may be necessary to modify one or more fields of the object returned + /// by `super.clone` before returning it. Typically, this means + /// copying any mutable objects that comprise the internal "deep structure" + /// of the object being cloned and replacing the references to these + /// objects with references to the copies. If a class contains only + /// primitive fields or references to immutable objects, then it is usually + /// the case that no fields in the object returned by `super.clone` + /// need to be modified. + /// + /// @return a clone of this instance. + /// @throws CloneNotSupportedException if the object's class does not + /// support the `Cloneable` interface. Subclasses + /// that override the `clone` method can also + /// throw this exception to indicate that an instance cannot + /// be cloned. + /// @implSpec The method `clone` for class `Object` performs a + /// specific cloning operation. First, if the class of this object does + /// not implement the interface `Cloneable`, then a + /// `CloneNotSupportedException` is thrown. Note that all arrays + /// are considered to implement the interface `Cloneable` and that + /// the return type of the `clone` method of an array type `T[]` + /// is `T[]` where T is any reference or primitive type. + /// Otherwise, this method creates a new instance of the class of this + /// object and initializes all its fields with exactly the contents of + /// the corresponding fields of this object, as if by assignment; the + /// contents of the fields are not themselves cloned. Thus, this method + /// performs a "shallow copy" of this object, not a "deep copy" operation. + /// + /// The class `Object` does not itself implement the interface + /// `Cloneable`, so calling the `clone` method on an object + /// whose class is `Object` will result in throwing an + /// exception at run time. + /// @see Cloneable +@Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + /// {@return a string representation of the object} + /// + /// Satisfying this method's contract implies a non-`null` + /// result must be returned. + /// + /// @apiNote In general, the + /// `toString` method returns a string that + /// "textually represents" this object. The result should + /// be a concise but informative representation that is easy for a + /// person to read. + /// It is recommended that all subclasses override this method. + /// The string output is not necessarily stable over time or across + /// JVM invocations. + /// @implSpec The `toString` method for class `Object` + /// returns a string consisting of the name of the class of which the + /// object is an instance, the at-sign character ``@`', and + /// the unsigned hexadecimal representation of the hash code of the + /// object. In other words, this method returns a string equal to the + /// value of: + /// {@snippet lang = java: + /// getClass().getName() + '@' + Integer.toHexString(hashCode()) + /// } + /// The [Objects.toIdentityString][#toIdentityString(Object)] method returns the string for an + /// object equal to the string that would be returned if neither + /// the `toString` nor `hashCode` methods were + /// overridden by the object's class. +@Override + public String toString() { + return super.toString(); + } + + /// Called by the garbage collector on an object when garbage collection + /// determines that there are no more references to the object. + /// A subclass overrides the `finalize` method to dispose of + /// system resources or to perform other cleanup. + /// + /// **When running in a Java virtual machine in which finalization has been + /// disabled or removed, the garbage collector will never call + /// `finalize()`. In a Java virtual machine in which finalization is + /// enabled, the garbage collector might call `finalize` only after an + /// indefinite delay.** + /// + /// The general contract of `finalize` is that it is invoked + /// if and when the Java virtual + /// machine has determined that there is no longer any + /// means by which this object can be accessed by any thread that has + /// not yet died, except as a result of an action taken by the + /// finalization of some other object or class which is ready to be + /// finalized. The `finalize` method may take any action, including + /// making this object available again to other threads; the usual purpose + /// of `finalize`, however, is to perform cleanup actions before + /// the object is irrevocably discarded. For example, the finalize method + /// for an object that represents an input/output connection might perform + /// explicit I/O transactions to break the connection before the object is + /// permanently discarded. + /// + /// The `finalize` method of class `Object` performs no + /// special action; it simply returns normally. Subclasses of + /// `Object` may override this definition. + /// + /// The Java programming language does not guarantee which thread will + /// invoke the `finalize` method for any given object. It is + /// guaranteed, however, that the thread that invokes finalize will not + /// be holding any user-visible synchronization locks when finalize is + /// invoked. If an uncaught exception is thrown by the finalize method, + /// the exception is ignored and finalization of that object terminates. + /// + /// After the `finalize` method has been invoked for an object, no + /// further action is taken until the Java virtual machine has again + /// determined that there is no longer any means by which this object can + /// be accessed by any thread that has not yet died, including possible + /// actions by other objects or classes which are ready to be finalized, + /// at which point the object may be discarded. + /// + /// The `finalize` method is never invoked more than once by a Java + /// virtual machine for any given object. + /// + /// Any exception thrown by the `finalize` method causes + /// the finalization of this object to be halted, but is otherwise + /// ignored. + /// + /// @throws Throwable the `Exception` raised by this method + /// @apiNote Classes that embed non-heap resources have many options + /// for cleanup of those resources. The class must ensure that the + /// lifetime of each instance is longer than that of any resource it embeds. + /// [#reachabilityFence] can be used to ensure that + /// objects remain reachable while resources embedded in the object are in use. + /// + /// A subclass should avoid overriding the `finalize` method + /// unless the subclass embeds non-heap resources that must be cleaned up + /// before the instance is collected. + /// Finalizer invocations are not automatically chained, unlike constructors. + /// If a subclass overrides `finalize` it must invoke the superclass + /// finalizer explicitly. + /// To guard against exceptions prematurely terminating the finalize chain, + /// the subclass should use a `try-finally` block to ensure + /// `super.finalize()` is always invoked. For example, + /// {@snippet lang = "java": + /// @Override + /// protected void finalize() throws Throwable { + /// try { + /// ... // cleanup subclass state + /// } finally { + /// super.finalize(); + /// } + /// } + /// } + /// @jls 12.6 Finalization of Class Instances + /// @see WeakReference + /// @see PhantomReference + /// @deprecated Finalization is deprecated and subject to removal in a future + /// release. The use of finalization can lead to problems with security, + /// performance, and reliability. + /// See JEP 421 for + /// discussion and alternatives. + /// + /// Subclasses that override `finalize` to perform cleanup should use + /// alternative cleanup mechanisms and remove the `finalize` method. + /// Use [Cleaner] and + /// [PhantomReference] as safer ways to release resources + /// when an object becomes unreachable. Alternatively, add a `close` + /// method to explicitly release resources, and implement + /// `AutoCloseable` to enable use of the `try`-with-resources + /// statement. + /// + /// This method will remain in place until finalizers have been removed from + /// most existing code. +@Override + protected void finalize() throws Throwable { + super.finalize(); + } + +} +/// Allows an action to retrieve information about the context in which it was invoked. +/// +/// **NOTES:** +/// +/// - Do not implement, or override platform implementations! +/// Things have got more complex since the introduction of asynchronous action update. +/// If you need to alter the provided data context or create one from a set of data +/// use [CustomizedDataContext] or [com.intellij.openapi.actionSystem.impl.SimpleDataContext] instead, even in tests. +/// These classes are async-ready, optionally support [com.intellij.openapi.util.UserDataHolder], +/// and run [com.intellij.ide.impl.dataRules.GetDataRule] rules. +/// - Do not to confuse [DataProvider] with [DataContext]. +/// A [DataContext] is usually provided by the platform with [DataProvider]s as its building blocks. +/// For example, a node in a tree view could be a [DataProvider] but not a [DataContext]. +/// +/// +/// @see DataKey +/// @see DataProvider +/// @see UiDataProvider +/// @see AnActionEvent#getDataContext() +/// @see com.intellij.ide.DataManager#getDataContext(Component) +/// @see com.intellij.openapi.actionSystem.CommonDataKeys +/// @see com.intellij.openapi.actionSystem.LangDataKeys +/// @see com.intellij.openapi.actionSystem.PlatformDataKeys +/// @see com.intellij.openapi.actionSystem.PlatformCoreDataKeys +/// @see com.intellij.openapi.actionSystem.CustomizedDataContext +/// @see com.intellij.openapi.actionSystem.impl.SimpleDataContext +interface Nothing {} \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration/MarkdownDocumentationCommentsMigration.java b/java/java-tests/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration/MarkdownDocumentationCommentsMigration.java new file mode 100644 index 000000000000..9e1b1c852be7 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/migration/markdown_documentation_comments_migration/MarkdownDocumentationCommentsMigration.java @@ -0,0 +1,342 @@ +import java.util.HashMap; + +/** + * test data file + *
+ * {@code System.out.println()} + * {@link java.util.ArrayList} + * {@link java.util.HashMap description} + * @author Bas + */ +public class MarkdownDocumentationCommentsMigration { + /** + * {@return a hash code value for this object} This method is + * supported for the benefit of hash tables such as those provided by + * {@link HashMap}. + *

+ * The general contract of {@code hashCode} is: + *

    + *
  • Whenever it is invoked on the same object more than once during + * an execution of a Java application, the {@code hashCode} method + * must consistently return the same integer, provided no information + * used in {@code equals} comparisons on the object is modified. + * This integer need not remain consistent from one execution of an + * application to another execution of the same application. + *
  • If two objects are equal according to the {@link + * #equals(Object) equals} method, then calling the {@code + * hashCode} method on each of the two objects must produce the + * same integer result. + *
  • It is not required that if two objects are unequal + * according to the {@link #equals(Object) equals} method, then + * calling the {@code hashCode} method on each of the two objects + * must produce distinct integer results. However, the programmer + * should be aware that producing distinct integer results for + * unequal objects may improve the performance of hash tables. + *
+ * + * @implSpec As far as is reasonably practical, the {@code hashCode} method defined + * by class {@code Object} returns distinct integers for distinct objects. + * @apiNote The {@link Objects#hash(Object...) hash} and {@link + * Objects#hashCode(Object) hashCode} methods of {@link + * Objects} can be used to help construct simple hash codes. + * @see Object#equals(Object) + * @see System#identityHashCode + */ + @Override + public int hashCode() { + return super.hashCode(); + } + + /** + * Indicates whether some other object is "equal to" this one. + *

+ * The {@code equals} method implements an equivalence relation + * on non-null object references: + *

    + *
  • It is reflexive: for any non-null reference value + * {@code x}, {@code x.equals(x)} should return + * {@code true}. + *
  • It is symmetric: for any non-null reference values + * {@code x} and {@code y}, {@code x.equals(y)} + * should return {@code true} if and only if + * {@code y.equals(x)} returns {@code true}. + *
  • It is transitive: for any non-null reference values + * {@code x}, {@code y}, and {@code z}, if + * {@code x.equals(y)} returns {@code true} and + * {@code y.equals(z)} returns {@code true}, then + * {@code x.equals(z)} should return {@code true}. + *
  • It is consistent: for any non-null reference values + * {@code x} and {@code y}, multiple invocations of + * {@code x.equals(y)} consistently return {@code true} + * or consistently return {@code false}, provided no + * information used in {@code equals} comparisons on the + * objects is modified. + *
  • For any non-null reference value {@code x}, + * {@code x.equals(null)} should return {@code false}. + *
+ * + *

+ * An equivalence relation partitions the elements it operates on + * into equivalence classes; all the members of an + * equivalence class are equal to each other. Members of an + * equivalence class are substitutable for each other, at least + * for some purposes. + * + * @param obj the reference object with which to compare. + * @return {@code true} if this object is the same as the obj + * argument; {@code false} otherwise. + * @implSpec The {@code equals} method for class {@code Object} implements + * the most discriminating possible equivalence relation on objects; + * that is, for any non-null reference values {@code x} and + * {@code y}, this method returns {@code true} if and only + * if {@code x} and {@code y} refer to the same object + * ({@code x == y} has the value {@code true}). + *

+ * In other words, under the reference equality equivalence + * relation, each equivalence class only has a single element. + * @apiNote It is generally necessary to override the {@link #hashCode() hashCode} + * method whenever this method is overridden, so as to maintain the + * general contract for the {@code hashCode} method, which states + * that equal objects must have equal hash codes. + *

The two-argument {@link Objects#equals(Object, + * Object) Objects.equals} method implements an equivalence relation + * on two possibly-null object references. + * @see #hashCode() + * @see HashMap + */ + @Override + public boolean equals(final Object obj) { + return super.equals(obj); + } + + /** + * Creates and returns a copy of this object. The precise meaning + * of "copy" may depend on the class of the object. The general + * intent is that, for any object {@code x}, the expression: + *

+ *
+   * x.clone() != x
+ * will be true, and that the expression: + *
+ *
+   * x.clone().getClass() == x.getClass()
+ * will be {@code true}, but these are not absolute requirements. + * While it is typically the case that: + *
+ *
+   * x.clone().equals(x)
+ * will be {@code true}, this is not an absolute requirement. + *

+ * By convention, the returned object should be obtained by calling + * {@code super.clone}. If a class and all of its superclasses (except + * {@code Object}) obey this convention, it will be the case that + * {@code x.clone().getClass() == x.getClass()}. + *

+ * By convention, the object returned by this method should be independent + * of this object (which is being cloned). To achieve this independence, + * it may be necessary to modify one or more fields of the object returned + * by {@code super.clone} before returning it. Typically, this means + * copying any mutable objects that comprise the internal "deep structure" + * of the object being cloned and replacing the references to these + * objects with references to the copies. If a class contains only + * primitive fields or references to immutable objects, then it is usually + * the case that no fields in the object returned by {@code super.clone} + * need to be modified. + * + * @return a clone of this instance. + * @throws CloneNotSupportedException if the object's class does not + * support the {@code Cloneable} interface. Subclasses + * that override the {@code clone} method can also + * throw this exception to indicate that an instance cannot + * be cloned. + * @implSpec The method {@code clone} for class {@code Object} performs a + * specific cloning operation. First, if the class of this object does + * not implement the interface {@code Cloneable}, then a + * {@code CloneNotSupportedException} is thrown. Note that all arrays + * are considered to implement the interface {@code Cloneable} and that + * the return type of the {@code clone} method of an array type {@code T[]} + * is {@code T[]} where T is any reference or primitive type. + * Otherwise, this method creates a new instance of the class of this + * object and initializes all its fields with exactly the contents of + * the corresponding fields of this object, as if by assignment; the + * contents of the fields are not themselves cloned. Thus, this method + * performs a "shallow copy" of this object, not a "deep copy" operation. + *

+ * The class {@code Object} does not itself implement the interface + * {@code Cloneable}, so calling the {@code clone} method on an object + * whose class is {@code Object} will result in throwing an + * exception at run time. + * @see Cloneable + */ + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + /** + * {@return a string representation of the object} + *

+ * Satisfying this method's contract implies a non-{@code null} + * result must be returned. + * + * @apiNote In general, the + * {@code toString} method returns a string that + * "textually represents" this object. The result should + * be a concise but informative representation that is easy for a + * person to read. + * It is recommended that all subclasses override this method. + * The string output is not necessarily stable over time or across + * JVM invocations. + * @implSpec The {@code toString} method for class {@code Object} + * returns a string consisting of the name of the class of which the + * object is an instance, the at-sign character `{@code @}', and + * the unsigned hexadecimal representation of the hash code of the + * object. In other words, this method returns a string equal to the + * value of: + * {@snippet lang = java: + * getClass().getName() + '@' + Integer.toHexString(hashCode()) + *} + * The {@link Objects#toIdentityString(Object) + * Objects.toIdentityString} method returns the string for an + * object equal to the string that would be returned if neither + * the {@code toString} nor {@code hashCode} methods were + * overridden by the object's class. + */ + @Override + public String toString() { + return super.toString(); + } + + /** + * Called by the garbage collector on an object when garbage collection + * determines that there are no more references to the object. + * A subclass overrides the {@code finalize} method to dispose of + * system resources or to perform other cleanup. + *

+ * When running in a Java virtual machine in which finalization has been + * disabled or removed, the garbage collector will never call + * {@code finalize()}. In a Java virtual machine in which finalization is + * enabled, the garbage collector might call {@code finalize} only after an + * indefinite delay. + *

+ * The general contract of {@code finalize} is that it is invoked + * if and when the Java virtual + * machine has determined that there is no longer any + * means by which this object can be accessed by any thread that has + * not yet died, except as a result of an action taken by the + * finalization of some other object or class which is ready to be + * finalized. The {@code finalize} method may take any action, including + * making this object available again to other threads; the usual purpose + * of {@code finalize}, however, is to perform cleanup actions before + * the object is irrevocably discarded. For example, the finalize method + * for an object that represents an input/output connection might perform + * explicit I/O transactions to break the connection before the object is + * permanently discarded. + *

+ * The {@code finalize} method of class {@code Object} performs no + * special action; it simply returns normally. Subclasses of + * {@code Object} may override this definition. + *

+ * The Java programming language does not guarantee which thread will + * invoke the {@code finalize} method for any given object. It is + * guaranteed, however, that the thread that invokes finalize will not + * be holding any user-visible synchronization locks when finalize is + * invoked. If an uncaught exception is thrown by the finalize method, + * the exception is ignored and finalization of that object terminates. + *

+ * After the {@code finalize} method has been invoked for an object, no + * further action is taken until the Java virtual machine has again + * determined that there is no longer any means by which this object can + * be accessed by any thread that has not yet died, including possible + * actions by other objects or classes which are ready to be finalized, + * at which point the object may be discarded. + *

+ * The {@code finalize} method is never invoked more than once by a Java + * virtual machine for any given object. + *

+ * Any exception thrown by the {@code finalize} method causes + * the finalization of this object to be halted, but is otherwise + * ignored. + * + * @throws Throwable the {@code Exception} raised by this method + * @apiNote Classes that embed non-heap resources have many options + * for cleanup of those resources. The class must ensure that the + * lifetime of each instance is longer than that of any resource it embeds. + * {@link Reference#reachabilityFence} can be used to ensure that + * objects remain reachable while resources embedded in the object are in use. + *

+ * A subclass should avoid overriding the {@code finalize} method + * unless the subclass embeds non-heap resources that must be cleaned up + * before the instance is collected. + * Finalizer invocations are not automatically chained, unlike constructors. + * If a subclass overrides {@code finalize} it must invoke the superclass + * finalizer explicitly. + * To guard against exceptions prematurely terminating the finalize chain, + * the subclass should use a {@code try-finally} block to ensure + * {@code super.finalize()} is always invoked. For example, + * {@snippet lang = "java": + * @Override + * protected void finalize() throws Throwable { + * try { + * ... // cleanup subclass state + * } finally { + * super.finalize(); + * } + * } + *} + * @jls 12.6 Finalization of Class Instances + * @see WeakReference + * @see PhantomReference + * @deprecated Finalization is deprecated and subject to removal in a future + * release. The use of finalization can lead to problems with security, + * performance, and reliability. + * See JEP 421 for + * discussion and alternatives. + *

+ * Subclasses that override {@code finalize} to perform cleanup should use + * alternative cleanup mechanisms and remove the {@code finalize} method. + * Use {@link Cleaner} and + * {@link PhantomReference} as safer ways to release resources + * when an object becomes unreachable. Alternatively, add a {@code close} + * method to explicitly release resources, and implement + * {@code AutoCloseable} to enable use of the {@code try}-with-resources + * statement. + *

+ * This method will remain in place until finalizers have been removed from + * most existing code. + */ + @Override + protected void finalize() throws Throwable { + super.finalize(); + } + +} +/** + * Allows an action to retrieve information about the context in which it was invoked. + *

+ * NOTES: + *

    + *
  • Do not implement, or override platform implementations! + * Things have got more complex since the introduction of asynchronous action update. + * If you need to alter the provided data context or create one from a set of data + * use {@link CustomizedDataContext} or {@link com.intellij.openapi.actionSystem.impl.SimpleDataContext} instead, even in tests. + * These classes are async-ready, optionally support {@link com.intellij.openapi.util.UserDataHolder}, + * and run {@link com.intellij.ide.impl.dataRules.GetDataRule} rules.
  • + *
  • Do not to confuse {@link DataProvider} with {@link DataContext}. + * A {@link DataContext} is usually provided by the platform with {@link DataProvider}s as its building blocks. + * For example, a node in a tree view could be a {@link DataProvider} but not a {@link DataContext}.
  • + *
+ * + * @see DataKey + * @see DataProvider + * @see UiDataProvider + * @see AnActionEvent#getDataContext() + * @see com.intellij.ide.DataManager#getDataContext(Component) + * @see com.intellij.openapi.actionSystem.CommonDataKeys + * @see com.intellij.openapi.actionSystem.LangDataKeys + * @see com.intellij.openapi.actionSystem.PlatformDataKeys + * @see com.intellij.openapi.actionSystem.PlatformCoreDataKeys + * @see com.intellij.openapi.actionSystem.CustomizedDataContext + * @see com.intellij.openapi.actionSystem.impl.SimpleDataContext + */ +interface Nothing {} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/siyeh/ig/migration/MarkdownDocumentationCommentsMigrationInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/migration/MarkdownDocumentationCommentsMigrationInspectionTest.java new file mode 100644 index 000000000000..fe266db3bdd8 --- /dev/null +++ b/java/java-tests/testSrc/com/siyeh/ig/migration/MarkdownDocumentationCommentsMigrationInspectionTest.java @@ -0,0 +1,23 @@ +// Copyright 2000-2024 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.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightJavaInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class MarkdownDocumentationCommentsMigrationInspectionTest extends LightJavaInspectionTestCase { + + public void testMarkdownDocumentationCommentsMigration() { + doTest(); + checkQuickFixAll(); + } + + + @Override + protected @Nullable InspectionProfileEntry getInspection() { + return new MarkdownDocumentationCommentsMigrationInspection(); + } +} \ No newline at end of file diff --git a/platform/analysis-api/resources/messages/InspectionsBundle.properties b/platform/analysis-api/resources/messages/InspectionsBundle.properties index 94f18f332c05..ce401935cee2 100644 --- a/platform/analysis-api/resources/messages/InspectionsBundle.properties +++ b/platform/analysis-api/resources/messages/InspectionsBundle.properties @@ -212,6 +212,7 @@ group.names.language.level.specific.issues.and.migration.aids14=Java 14 group.names.language.level.specific.issues.and.migration.aids15=Java 15 group.names.language.level.specific.issues.and.migration.aids16=Java 16 group.names.language.level.specific.issues.and.migration.aids21=Java 21 +group.names.language.level.specific.issues.and.migration.aids23=Java 23 group.names.javabeans.issues=JavaBeans issues group.names.inheritance.issues=Inheritance issues group.names.data.flow.issues=Data flow