From 3cb38fccaa5b35a56f48288d498dd2a6389a8c04 Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Thu, 16 Jan 2025 10:43:45 +0100 Subject: [PATCH] [java-imports] IDEA-13817 Create "favorites" for static import and completion - part of "favorites" for static import. Inspection and refactoring GitOrigin-RevId: a764a70000c3f0918e50a814908e5969b93688d7 --- .../resources/META-INF/JavaPlugin.xml | 4 + .../JavaProjectCodeInsightSettings.java | 3 +- .../impl/AddOnDemandStaticImportAction.java | 12 +- .../StaticImportCanBeUsedInspection.java | 103 +++++++++++++++ .../impl/source/codeStyle/ImportHelper.java | 52 ++++++++ .../codeStyle/JavaReferenceAdjuster.java | 38 ++---- .../StaticImportCanBeUsed.html | 10 ++ .../tree/java/PsiReferenceExpressionImpl.java | 10 +- .../AlreadyImported.java | 11 ++ .../AlreadyImported_after.java | 11 ++ .../staticImportCanBeUsed/Simple.java | 10 ++ .../staticImportCanBeUsed/Simple_after.java | 12 ++ .../staticImportCanBeUsed/WithConflicts.java | 12 ++ .../staticImportCanBeUsed/WithConflicts2.java | 13 ++ .../WithConflictsWithClass.java | 13 ++ .../WithConflictsWithField.java | 13 ++ .../StaticImportCanBeUsedInspectionTest.java | 122 ++++++++++++++++++ .../resources/messages/JavaBundle.properties | 2 + 18 files changed, 421 insertions(+), 30 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInspection/StaticImportCanBeUsedInspection.java create mode 100644 java/java-impl/src/inspectionDescriptions/StaticImportCanBeUsed.html create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported_after.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/Simple.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/Simple_after.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts2.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithClass.java create mode 100644 java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithField.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportCanBeUsedInspectionTest.java diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml index a90db3222d3a..fe2a17e12ed4 100644 --- a/java/java-impl/resources/META-INF/JavaPlugin.xml +++ b/java/java-impl/resources/META-INF/JavaPlugin.xml @@ -2292,6 +2292,10 @@ + JAVA diff --git a/java/java-impl/src/com/intellij/codeInsight/JavaProjectCodeInsightSettings.java b/java/java-impl/src/com/intellij/codeInsight/JavaProjectCodeInsightSettings.java index 56e350c9e5d6..e48c33487a70 100644 --- a/java/java-impl/src/com/intellij/codeInsight/JavaProjectCodeInsightSettings.java +++ b/java/java-impl/src/com/intellij/codeInsight/JavaProjectCodeInsightSettings.java @@ -43,8 +43,7 @@ public class JavaProjectCodeInsightSettings implements PersistentStateComponent< public boolean isStaticAutoImportClass(@Nullable String name) { if (name == null) return false; - return includedAutoStaticNames.contains(name) || - JavaIdeCodeInsightSettings.getInstance().includedAutoStaticNames.contains(name); + return getAllIncludedAutoStaticNames().contains(name); } public boolean isExcluded(@NotNull String name) { diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java index 6bafddead943..3c1664934d35 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java @@ -16,6 +16,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.wm.WindowManager; import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; @@ -114,10 +115,15 @@ public final class AddOnDemandStaticImportAction extends PsiUpdateModCommandActi @Override protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiIdentifier element) { PsiClass classToImport = getClassToPerformStaticImport(element); - if (classToImport != null) { - return Presentation.of(JavaBundle.message("intention.add.on.demand.static.import.text", classToImport.getQualifiedName())); + if (classToImport == null) { + return null; } - return null; + Project project = element.getProject(); + JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + if (codeStyleManager.isStaticAutoImportClass(classToImport.getQualifiedName())) { + return null; + } + return Presentation.of(JavaBundle.message("intention.add.on.demand.static.import.text", classToImport.getQualifiedName())); } public static boolean invoke(final Project project, PsiFile file, final Editor editor, @NotNull PsiElement element) { diff --git a/java/java-impl/src/com/intellij/codeInspection/StaticImportCanBeUsedInspection.java b/java/java-impl/src/com/intellij/codeInspection/StaticImportCanBeUsedInspection.java new file mode 100644 index 000000000000..6301001a8b84 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/StaticImportCanBeUsedInspection.java @@ -0,0 +1,103 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.codeInspection; + +import com.intellij.codeInsight.JavaProjectCodeInsightSettings; +import com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction; +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.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ObjectUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public final class StaticImportCanBeUsedInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool { + @Override + public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + JavaProjectCodeInsightSettings settings = JavaProjectCodeInsightSettings.getSettings(holder.getProject()); + List names = settings.getAllIncludedAutoStaticNames(); + if (names.isEmpty()) { + return PsiElementVisitor.EMPTY_VISITOR; + } + Set shortNames = names.stream() + .map(name -> StringUtil.getShortName(name)) + .collect(Collectors.toSet()); + return new JavaElementVisitor() { + @Override + public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) { + PsiElement reference = expression.getReferenceNameElement(); + if (reference == null) return; + PsiExpression qualifierExpression = expression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiReferenceExpression qualifierReferenceExpression)) return; + String name = qualifierReferenceExpression.getReferenceName(); + PsiElement qualifierReference = qualifierReferenceExpression.getReferenceNameElement(); + if (qualifierReference == null || name == null || !shortNames.contains(name)) return; + OnDemandStaticImportContext context = findOnDemandImportContext(expression); + if (context == null) return; + holder.registerProblem(qualifierReference, + JavaBundle.message("inspection.static.import.can.be.used.display.name"), + new OnDemandStaticImportFix()); + } + }; + } + + private static class OnDemandStaticImportFix extends PsiUpdateModCommandQuickFix { + @Override + protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) { + PsiElement parent = element.getParent(); + if (!(parent instanceof PsiReferenceExpression referenceExpression)) { + return; + } + if (!(referenceExpression.getParent() instanceof PsiReferenceExpression targetReferenceExpression)) return; + JavaCodeStyleManager.getInstance(project).shortenClassReferences(targetReferenceExpression); + } + + @Override + public @NotNull String getFamilyName() { + return JavaBundle.message("inspection.static.import.can.be.used.fix.name"); + } + } + + /** + * @param reference the reference expression to evaluate; must not be null. + * @return the {@link OnDemandStaticImportContext} if the reference can be on-demand static imported, + * or null if the criteria for such a context are not met. + */ + @Nullable + public static OnDemandStaticImportContext findOnDemandImportContext(@NotNull PsiReferenceExpression reference) { + JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(reference.getProject()); + PsiFile file = reference.getContainingFile(); + if (!(file instanceof PsiJavaFile javaFile)) return null; + if (reference instanceof PsiMethodReferenceExpression) return null; + String memberName = reference.getReferenceName(); + if (memberName == null) return null; + PsiJavaCodeReferenceElement qualifier = ObjectUtils.tryCast(reference.getQualifier(), PsiJavaCodeReferenceElement.class); + if (qualifier == null) return null; + if (GenericsUtil.isGenericReference(reference, qualifier)) return null; + if (PsiTreeUtil.getParentOfType(reference, PsiImportStatementBase.class) != null) return null; + if (!(qualifier.resolve() instanceof PsiClass psiClass)) return null; + String classQualifiedName = psiClass.getQualifiedName(); + if (!javaCodeStyleManager.isStaticAutoImportClass(classQualifiedName)) return null; + PsiElement referenceNameElement = qualifier.getReferenceNameElement(); + if (referenceNameElement == null) return null; + PsiClass anImport = AddOnDemandStaticImportAction.getClassToPerformStaticImport(referenceNameElement); + if (anImport == null) return null; + if (javaCodeStyleManager.hasConflictingOnDemandImport(javaFile, anImport, memberName)) return null; + PsiImportList importList = javaFile.getImportList(); + if (importList == null) return null; + return new OnDemandStaticImportContext(anImport, memberName, importList); + } + + public record OnDemandStaticImportContext(@NotNull PsiClass psiClass, @NotNull String memberName, @NotNull PsiImportList importList) { + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java index a41fd6885139..b19c4a072f2f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java @@ -1004,6 +1004,43 @@ public final class ImportHelper { return true; } } + JavaPsiFacade facade = JavaPsiFacade.getInstance(file.getProject()); + PsiResolveHelper resolveHelper = facade.getResolveHelper(); + for (Import anImport : resultList) { + if (anImport.isStatic()) { + String shortName = StringUtil.getShortName(anImport.name()); + String prefix = StringUtil.getPackageName(anImport.name()); + if (prefix.isEmpty()) continue; + PsiField field = psiClass.findFieldByName(shortName, true); + if (field != null && + field.hasModifierProperty(PsiModifier.STATIC) && + checkMemberAccessibility(field, resolveHelper, file, psiClass, prefix)) { + if (isOnDemandStaticImported(file, anImport)) { + return true; + } + } + else { + PsiClass inner = psiClass.findInnerClassByName(shortName, true); + if (inner != null && + inner.hasModifierProperty(PsiModifier.STATIC) && + checkMemberAccessibility(inner, resolveHelper, file, psiClass, prefix)) { + if (isOnDemandStaticImported(file, anImport)) { + return true; + } + } + else { + PsiMethod[] methods = psiClass.findMethodsByName(shortName, true); + if (ContainerUtil.exists(methods, psiMethod -> + psiMethod.hasModifierProperty(PsiModifier.STATIC) && + checkMemberAccessibility(psiMethod, resolveHelper, file, psiClass, prefix))) { + if (isOnDemandStaticImported(file, anImport)) { + return true; + } + } + } + } + } + } PsiImportList importList = file.getImportList(); if (importList == null) return false; @@ -1022,6 +1059,21 @@ public final class ImportHelper { return singleImports.contains(newImport); } + private static boolean isOnDemandStaticImported(@NotNull PsiJavaFile file, @NotNull Import anImport) { + if(!anImport.isStatic()) return false; + PsiImportList importList = file.getImportList(); + if(importList==null)return false; + for (PsiImportStaticStatement statement : importList.getImportStaticStatements()) { + if(!statement.isOnDemand()) return false; + String packageName = StringUtil.getPackageName(anImport.name); + if (statement.getImportReference() != null && + packageName.equals(statement.getImportReference().getQualifiedName())) { + return true; + } + } + return false; + } + // returns list of (name, isImportStatic) pairs private static @NotNull Collection collectNamesToImport(@NotNull PsiJavaFile file, @NotNull List comments) { Set imports = new HashSet<>(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaReferenceAdjuster.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaReferenceAdjuster.java index bf4187ef4b7a..f87254e19c99 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaReferenceAdjuster.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaReferenceAdjuster.java @@ -4,12 +4,11 @@ package com.intellij.psi.impl.source.codeStyle; import com.intellij.application.options.CodeStyle; import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil; -import com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction; +import com.intellij.codeInspection.StaticImportCanBeUsedInspection; import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.codeStyle.ReferenceAdjuster; import com.intellij.psi.impl.PsiImplUtil; @@ -22,7 +21,6 @@ import com.intellij.psi.jsp.JspFile; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.ObjectUtils; import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.ImportUtils; import one.util.streamex.StreamEx; @@ -139,27 +137,19 @@ public final class JavaReferenceAdjuster implements ReferenceAdjuster { } private static boolean tryAutoStaticallyImport(@NotNull PsiReferenceExpression reference) { - JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(reference.getProject()); - PsiFile file = reference.getContainingFile(); - if (!(file instanceof PsiJavaFile javaFile)) return false; - if (reference instanceof PsiMethodReferenceExpression) return false; - String memberName = reference.getReferenceName(); - if (memberName == null) return false; - PsiJavaCodeReferenceElement qualifier = ObjectUtils.tryCast(reference.getQualifier(), PsiJavaCodeReferenceElement.class); - if (qualifier == null) return false; - if (GenericsUtil.isGenericReference(reference, qualifier)) return false; - if (PsiTreeUtil.getParentOfType(reference, PsiImportStatementBase.class) != null) return false; - if (!(qualifier.resolve() instanceof PsiClass psiClass)) return false; - String classQualifiedName = psiClass.getQualifiedName(); - if (!javaCodeStyleManager.isStaticAutoImportClass(classQualifiedName)) return false; - PsiElement referenceNameElement = qualifier.getReferenceNameElement(); - if (referenceNameElement == null) return false; - PsiClass anImport = AddOnDemandStaticImportAction.getClassToPerformStaticImport(referenceNameElement); - if (anImport == null) return false; - if (javaCodeStyleManager.hasConflictingOnDemandImport(javaFile, anImport, memberName)) return false; - PsiImportList importList = javaFile.getImportList(); - if (importList == null) return false; - PsiReferenceExpressionImpl.bindToElementViaStaticImport(anImport, memberName, importList); + StaticImportCanBeUsedInspection.OnDemandStaticImportContext context = + StaticImportCanBeUsedInspection.findOnDemandImportContext(reference); + if (context == null) return false; + PsiClass qualifierClass = context.psiClass(); + String qualifiedName = qualifierClass.getQualifiedName(); + List refs = PsiReferenceExpressionImpl.getImportsFromClass(context.importList(), qualifiedName); + for (PsiJavaCodeReferenceElement ref : refs) { + PsiImportStaticStatement importStatement = PsiTreeUtil.getParentOfType(ref, PsiImportStaticStatement.class); + if (importStatement != null) { + importStatement.delete(); + } + } + context.importList().add(JavaPsiFacade.getElementFactory(qualifierClass.getProject()).createImportStaticStatement(qualifierClass, "*")); return true; } diff --git a/java/java-impl/src/inspectionDescriptions/StaticImportCanBeUsed.html b/java/java-impl/src/inspectionDescriptions/StaticImportCanBeUsed.html new file mode 100644 index 000000000000..468a95dd85ae --- /dev/null +++ b/java/java-impl/src/inspectionDescriptions/StaticImportCanBeUsed.html @@ -0,0 +1,10 @@ + + +Reports qualifiers, which can be replaced with on-demand import static statements. +The list of classes that will be checked can be configured in +Settings | Editor | General | Auto Import | Java | Include auto-import of static members in + completion + + +

New in 2025.1

+ \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java index 2afc8fce978c..7348583a6d6a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java @@ -122,7 +122,15 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements } } - private static @NotNull List getImportsFromClass(@NotNull PsiImportList importList, String className) { + /** + * Retrieves the static import statements from the given import list that reference + * a specified class. + * + * @param importList the list of import statements in a Java file. + * @param className the fully qualified name of the class for which static imports are to be retrieved. + * @return a list of static import references corresponding to the specified class. + */ + public static @NotNull List getImportsFromClass(@NotNull PsiImportList importList, String className) { List array = new ArrayList<>(); for (PsiImportStaticStatement staticStatement : importList.getImportStaticStatements()) { PsiClass psiClass = staticStatement.resolveTargetClass(); diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported.java new file mode 100644 index 000000000000..2e5dad0c42f1 --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported.java @@ -0,0 +1,11 @@ +package staticImportCanBeUsed; + +import java.util.Arrays; +import static java.util.Arrays.sort; + +class Foo { + void test(String[] baz) { + Arrays.sort(baz); + Arrays.sort(baz); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported_after.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported_after.java new file mode 100644 index 000000000000..ca8e6e3ea88a --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/AlreadyImported_after.java @@ -0,0 +1,11 @@ +package staticImportCanBeUsed; + +import java.util.Arrays; +import static java.util.Arrays.sort; + +class Foo { + void test(String[] baz) { + sort(baz); + sort(baz); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/Simple.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/Simple.java new file mode 100644 index 000000000000..427a84cd1f28 --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/Simple.java @@ -0,0 +1,10 @@ +package staticImportCanBeUsed; + +import java.util.Arrays; + +class Foo { + void test(String[] baz) { + Arrays.sort(baz); + Arrays.sort(baz); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/Simple_after.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/Simple_after.java new file mode 100644 index 000000000000..013cd86257c1 --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/Simple_after.java @@ -0,0 +1,12 @@ +package staticImportCanBeUsed; + +import java.util.Arrays; + +import static java.util.Arrays.*; + +class Foo { + void test(String[] baz) { + sort(baz); + sort(baz); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts.java new file mode 100644 index 000000000000..d764e927603b --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts.java @@ -0,0 +1,12 @@ +package staticImportCanBeUsed; + +import java.util.Arrays; + +class Foo { + void test(String[] baz) { + Arrays.sort(baz); + Arrays.sort(baz); + } + + public static void sort(String[] a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts2.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts2.java new file mode 100644 index 000000000000..961476c2a974 --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflicts2.java @@ -0,0 +1,13 @@ +package staticImportCanBeUsed; + +import java.util.Arrays; + +import static org.Foo2.*; + +class Foo { + void test(String[] baz) { + Arrays.sort(baz); + Arrays.sort(baz); + binarySearch(baz, "a"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithClass.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithClass.java new file mode 100644 index 000000000000..82649a16fe76 --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithClass.java @@ -0,0 +1,13 @@ +package staticImportCanBeUsed; + +import org.Foo2; + +import static org.Foo3.*; + +class Foo { + void test(String[] baz) { + Foo2.sort(baz); + Foo2.sort(baz); + new Calculus(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithField.java b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithField.java new file mode 100644 index 000000000000..0c90113a32e6 --- /dev/null +++ b/java/java-tests/testData/inspection/staticImportCanBeUsed/WithConflictsWithField.java @@ -0,0 +1,13 @@ +package staticImportCanBeUsed; + +import org.Foo2; + +import static org.Foo3.*; + +class Foo { + void test(String[] baz) { + Foo2.sort(baz); + Foo2.sort(baz); + System.out.println(PI); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportCanBeUsedInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportCanBeUsedInspectionTest.java new file mode 100644 index 000000000000..894dad510c2c --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportCanBeUsedInspectionTest.java @@ -0,0 +1,122 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.JavaTestUtil; +import com.intellij.analysis.AnalysisBundle; +import com.intellij.codeInsight.JavaProjectCodeInsightSettings; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.codeInspection.StaticImportCanBeUsedInspection; +import com.siyeh.ig.LightJavaInspectionTestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + +public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTestCase { + + @Override + public void tearDown() throws Exception { + try { + JavaProjectCodeInsightSettings.getSettings(getProject()).includedAutoStaticNames.clear(); + } + catch (Throwable e) { + addSuppressedException(e); + } + finally { + super.tearDown(); + } + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new StaticImportCanBeUsedInspection(); + } + + public void testSimple() { + addStaticAutoImport("java.util.Arrays"); + doTest(); + cleanupTest(); + } + + public void testWithConflicts() { + addStaticAutoImport("java.util.Arrays"); + doTest(); + } + + public void testWithConflicts2() { + addStaticAutoImport("java.util.Arrays"); + myFixture.addClass(""" + package org; + + public final class Foo2 { + public static void binarySearch(Object[] args, Object key) {} + } + """); + doTest(); + } + + public void testWithConflictsWithField() { + addStaticAutoImport("org.Foo2"); + myFixture.addClass(""" + package org; + + public final class Foo2 { + public static final String PI = "3.14" + public static void sort(String[] args) {} + } + """); + myFixture.addClass(""" + package org; + + public final class Foo3 { + public static final String PI = "3.14" + } + """); + doTest(); + } + + public void testWithConflictsWithClass() { + addStaticAutoImport("org.Foo2"); + myFixture.addClass(""" + package org; + + public final class Foo2 { + public static class Calculus{} + public static void sort(String[] args) {} + } + """); + myFixture.addClass(""" + package org; + + public final class Foo3 { + public static class Calculus{} + } + """); + doTest(); + } + + public void testAlreadyImported() { + addStaticAutoImport("java.util.Arrays"); + doTest(); + cleanupTest(); + } + + //todo test for inner class and for field + + private void cleanupTest() { + IntentionAction intention = myFixture.getAvailableIntention(AnalysisBundle.message("cleanup.in.file")); + myFixture.launchAction(intention); + myFixture.checkResultByFile(getTestName(false) + "_after.java"); + } + + @Override + protected String getBasePath() { + return JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/staticImportCanBeUsed/"; + } + + @SuppressWarnings("SameParameterValue") + private void addStaticAutoImport(@NotNull String name) { + JavaProjectCodeInsightSettings.getSettings(getProject()).includedAutoStaticNames.add(name); + } +} diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index 5d11ed908432..37fe1afda3d1 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -1429,6 +1429,8 @@ inspection.slow.abstract.set.remove.all.fix.family.name=Use 'Set.remove' instead inspection.slow.list.contains.all.description=Call to 'list.containsAll(collection)' may have poor performance inspection.slow.list.contains.all.fix.family.name=Wrap in 'HashSet' constructor inspection.slow.list.contains.all.fix.name=Wrap ''{0}'' in ''HashSet'' constructor +inspection.static.import.can.be.used.display.name=On-demand static import can be used +inspection.static.import.can.be.used.fix.name=Add on-demand static import slice.filter.parse.error.null.filter.not.applicable.for.primitive.type=''null'' filter is not applicable for primitive type {0} slice.filter.parse.error.not.null.filter.not.applicable.for.primitive.type=''!null'' filter is not applicable for primitive type {0} slice.filter.parse.error.enum.constant.not.found=Enum constant not found: {0}