[java-inspections] IJ-CR-123164 IDEA-341641 Intention to convert implicit class to explicit and vice versa

- more tests
- small optimization
- preserve first comment in created implicitly declared classes
- fixed java docs
- move highlighting to the whole declaration

GitOrigin-RevId: e7ea706151f09852473e31d3831bb3008da62475
This commit is contained in:
Mikhail Pyltsin
2024-01-09 11:04:51 +00:00
committed by intellij-monorepo-bot
parent d3b1e47df7
commit 858b2cd664
14 changed files with 94 additions and 28 deletions
@@ -3,24 +3,25 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.search.PackageScope;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public final class ExplicitToImplicitClassMigrationInspection extends AbstractBaseJavaLocalInspectionTool {
private static final String JAVA_SUFFIX = ".java";
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
@@ -32,6 +33,12 @@ public final class ExplicitToImplicitClassMigrationInspection extends AbstractBa
return;
}
PsiElement lBrace = aClass.getLBrace();
PsiElement rBrace = aClass.getRBrace();
if (lBrace == null || rBrace == null) {
return;
}
if (aClass.getContainingClass() != null) {
return;
}
@@ -50,12 +57,13 @@ public final class ExplicitToImplicitClassMigrationInspection extends AbstractBa
}
String fileName = file.getName();
if (!fileName.endsWith(JAVA_SUFFIX)) {
if (!fileName.endsWith(JavaFileType.DOT_DEFAULT_EXTENSION)) {
return;
}
String className = aClass.getName();
if (className == null || !className.equals(fileName.substring(0, fileName.length() - JAVA_SUFFIX.length()))) {
if (className == null ||
!className.equals(fileName.substring(0, fileName.length() - JavaFileType.DOT_DEFAULT_EXTENSION.length()))) {
return;
}
@@ -67,7 +75,9 @@ public final class ExplicitToImplicitClassMigrationInspection extends AbstractBa
return;
}
if (aClass.getExtendsListTypes().length != 0 || aClass.getImplementsListTypes().length != 0) {
PsiClassType[] extendsListTypes = aClass.getExtendsListTypes();
if ((extendsListTypes.length != 0 && !onlyObjectExtends(extendsListTypes)) ||
aClass.getImplementsListTypes().length != 0) {
return;
}
@@ -89,8 +99,8 @@ public final class ExplicitToImplicitClassMigrationInspection extends AbstractBa
}
}
Project project = aClass.getProject();
PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(file.getPackageName());
Project project = holder.getProject();
PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage("");
if (aPackage == null) {
return;
}
@@ -113,18 +123,19 @@ public final class ExplicitToImplicitClassMigrationInspection extends AbstractBa
if (first != null) {
return;
}
PsiElement lBrace = aClass.getLBrace();
PsiElement rBrace = aClass.getRBrace();
if (lBrace == null || rBrace == null) {
return;
}
if (PsiTreeUtil.hasErrorElements(aClass)) {
return;
}
holder.registerProblem(classIdentifier, JavaBundle.message("inspection.explicit.to.implicit.class.migration.name"),
new ReplaceWithImplicitClassFix());
holder.registerProblem(aClass, new TextRange(0, classIdentifier.getTextRangeInParent().getEndOffset()),
JavaBundle.message("inspection.explicit.to.implicit.class.migration.name"), new ReplaceWithImplicitClassFix());
}
private static boolean onlyObjectExtends(PsiClassType[] types) {
if (types.length != 1) return false;
PsiClassType type = types[0];
return type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
}
};
}
@@ -141,23 +152,18 @@ public final class ExplicitToImplicitClassMigrationInspection extends AbstractBa
@Override
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
PsiClass psiClass = ObjectUtils.tryCast(element, PsiClass.class);
if (psiClass == null) {
return;
}
StringBuilder builder = new StringBuilder();
PsiElement lBrace = psiClass.getLBrace();
PsiElement rBrace = psiClass.getRBrace();
if (lBrace == null || rBrace == null) {
if (lBrace == null || rBrace == null || lBrace.getNextSibling() == null || rBrace.getPrevSibling() == null) {
return;
}
PsiElement psiElement = lBrace.getNextSibling();
CommentTracker tracker = new CommentTracker();
while (psiElement != null && psiElement != rBrace) {
builder.append(tracker.text(psiElement));
psiElement = psiElement.getNextSibling();
}
PsiImplicitClass newClass = PsiElementFactory.getInstance(project).createImplicitClassFromText(builder.toString(), psiClass);
String body = tracker.rangeText(lBrace.getNextSibling(), rBrace.getPrevSibling());
PsiImplicitClass newClass = PsiElementFactory.getInstance(project).createImplicitClassFromText(body, psiClass);
PsiElement replaced = tracker.replace(psiClass, newClass);
tracker.insertCommentsBefore(replaced);
}
@@ -99,7 +99,7 @@ public interface PsiJavaParserFacade {
PsiParameter createParameterFromText(@NotNull @NonNls String text, @Nullable PsiElement context) throws IncorrectOperationException;
/**
* Creates an implicit class from the specified body text (the text between the braces).
* Creates an implicit class from the specified body text.
*
* @param body the body text of the class to create.
* @param context the PSI element used as context for resolving references which cannot be resolved
@@ -142,13 +142,17 @@ public class PsiJavaParserFacadeImpl implements PsiJavaParserFacade {
@NotNull
@Override
public PsiImplicitClass createImplicitClassFromText(@NotNull String body, @Nullable PsiElement context) throws IncorrectOperationException {
PsiJavaFile aFile = createDummyJavaFile(body);
PsiJavaFile aFile = createDummyJavaFile(
"int i = 0;" + //used to preserve first comments
body);
PsiClass[] classes = aFile.getClasses();
if (classes.length != 1) {
throw new IncorrectOperationException("Incorrect class '" + body + "'");
}
if (classes[0] instanceof PsiImplicitClass) {
return (PsiImplicitClass)classes[0];
PsiImplicitClass implicitClass = (PsiImplicitClass)classes[0];
implicitClass.getFirstChild().delete(); //delete stub field
return implicitClass;
}
throw new IncorrectOperationException("Incorrect implicit class '" + body + "'");
}
@@ -0,0 +1,4 @@
// "Convert into implicitly declared class" "true-preview"
public static void main(String[] args) {
System.out.println("Hello, world!");
}
@@ -0,0 +1,4 @@
// "Convert into implicitly declared class" "true-preview"
public static void main(String[] args) {
System.out.println("Hello, world!");
}
@@ -2,6 +2,9 @@
/**
* comments
*/ /*comments2*/ public static void main(String[] args) {
*/ /*comments2*/ /*comments3*/
public static void main(String[] args) {
//comments4
System.out.println("Hello, world!");
}
//comments5
@@ -0,0 +1,5 @@
// "Convert into implicitly declared class" "true-preview"
public static void main() {
System.out.println("Hello, world!");
}
@@ -0,0 +1,5 @@
// "Convert into implicitly declared class" "true-preview"
public void main() {
System.out.println("Hello, world!");
}
@@ -0,0 +1,6 @@
// "Convert into implicitly declared class" "true-preview"
public<caret> class beforeCaretAtClass {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
@@ -0,0 +1,6 @@
// "Convert into implicitly declared class" "true-preview"
public<caret> class beforeExtendsObject extends Object {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
@@ -5,8 +5,10 @@
*/
public /*comments2*/ class beforeSi<caret>mpleWithComments {
/*comments3*/
public static void main(String[] args) {
//comments4
System.out.println("Hello, world!");
}
}
//comments5
@@ -0,0 +1,7 @@
// "Convert into implicitly declared class" "false"
public class beforeWith<caret>EnhancedMain2 {
public void main(String arg) {
System.out.println("Hello, world!");
}
}
@@ -0,0 +1,7 @@
// "Convert into implicitly declared class" "true-preview"
public class be<caret>foreWithEnhancedMain {
public static void main() {
System.out.println("Hello, world!");
}
}
@@ -0,0 +1,7 @@
// "Convert into implicitly declared class" "true-preview"
public class beforeWith<caret>EnhancedMain2 {
public void main() {
System.out.println("Hello, world!");
}
}