diff --git a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/ImportUtils.java b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/ImportUtils.java index 5204f3456402..931e9eabb62f 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/ImportUtils.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/ImportUtils.java @@ -683,6 +683,7 @@ public final class ImportUtils { private final @NotNull Set myModulesStatements = new HashSet<>(); private final @NotNull Map myPackageStatements = new HashMap<>(); + private final @NotNull Map myCache = new HashMap<>(); private ImplicitImportChecker(@NotNull PsiJavaFile file) { for (PsiImportStatementBase anImport : getAllImplicitImports(file)) { if(anImport instanceof PsiImportStaticStatement staticStatement) { @@ -703,12 +704,18 @@ public final class ImportUtils { } } + record ImplicitCacheItem(String qName, boolean isStatic, + @NotNull ImportUtils.OnDemandImportConflict conflicts) { } + public boolean isImplicitlyImported(String qName, boolean isStatic) { return isImplicitlyImported(qName, isStatic, OnDemandImportConflict.NO_CONFLICTS); } public boolean isImplicitlyImported(String qName, boolean isStatic, @NotNull ImportUtils.OnDemandImportConflict conflicts) { + ImplicitCacheItem cacheItem = new ImplicitCacheItem(qName, isStatic, conflicts); + Boolean result = myCache.get(cacheItem); + if (result != null) return result; String packageOrClassName = StringUtil.getPackageName(qName); String className = ClassUtil.extractClassName(qName); if (!isStatic) { @@ -720,24 +727,39 @@ public final class ImportUtils { if (reference == null) continue; PsiElement resolved = reference.resolve(); if (resolved instanceof PsiPackage psiPackage) { - if (psiPackage.containsClassNamed(className)) return true; + if (psiPackage.containsClassNamed(className)) { + myCache.put(cacheItem, true); + return true; + } } } } - if (!conflicts.hasConflictForOnDemand() && myPackageStatements.containsKey(packageOrClassName)) return true; + if (!conflicts.hasConflictForOnDemand() && myPackageStatements.containsKey(packageOrClassName)) { + myCache.put(cacheItem, true); + return true; + } } else { if (!conflicts.hasConflictForOnDemand()) { PsiImportStaticStatement psiImportStaticStatement = myStaticImportStatements.get(packageOrClassName); if (psiImportStaticStatement != null) { - if (psiImportStaticStatement.isOnDemand()) return true; + if (psiImportStaticStatement.isOnDemand()) { + myCache.put(cacheItem, true); + return true; + } PsiJavaCodeReferenceElement reference = psiImportStaticStatement.getImportReference(); - if (reference == null) return false; + if (reference == null) { + myCache.put(cacheItem, false); + return false; + } String qualifiedName = reference.getQualifiedName(); - return qName.equals(qualifiedName); + boolean equals = qName.equals(qualifiedName); + myCache.put(cacheItem, equals); + return equals; } } } + myCache.put(cacheItem, false); return false; } } 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 f271ab73ed3c..157b79076574 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 @@ -128,6 +128,8 @@ public final class ImportHelper extends ImportHelperBase { moduleStatementMap.keySet()); classesToUseSingle.addAll(toReimport); + deleteOnDemandIfAllIsSingle(classesOrPackagesToImportOnDemand, resultList, classesToUseSingle, checker); + try { boolean onDemandFirst = mySettings.isLayoutOnDemandImportFromSamePackageFirst(); StringBuilder text = @@ -159,6 +161,33 @@ public final class ImportHelper extends ImportHelperBase { } } + /** + * Removes on-demand imports from the given map if all corresponding elements are present in the singles set + * (individually imported) or implicitly imported. + * + * @param onDemand a map of package or class names (keys) and boolean values associating them with on-demand imports. + * @param allList a list of Import objects representing all current imports in the file. + * @param singles a set of individual imports to retain. + * @param checker an ImplicitImportChecker used to determine if a name is implicitly imported. + */ + private static void deleteOnDemandIfAllIsSingle(@NotNull Map onDemand, + @NotNull List allList, + @NotNull Set singles, + @NotNull ImportUtils.ImplicitImportChecker checker) { + Set save = new HashSet<>(); + if (singles.isEmpty()) return; + for (Import anImport : allList) { + String name = anImport.name(); + boolean isStatic = anImport.isStatic(); + String packageOrClassName = StringUtil.getPackageName(name); + boolean implicitlyImported = checker.isImplicitlyImported(name, isStatic); + if (!singles.contains(name) && !implicitlyImported && onDemand.containsKey(packageOrClassName)) { + save.add(packageOrClassName); + } + } + onDemand.keySet().retainAll(save); + } + /** * Collects the names of classes that are imported by modules specified implicitly in the given Java file and in the import list. * @@ -168,8 +197,8 @@ public final class ImportHelper extends ImportHelperBase { * @return a map of class names and used module imports. */ private static @NotNull Map collectNamesImportedByModules(@NotNull PsiJavaFile file, - @NotNull List statements, - @NotNull List list) { + @NotNull List statements, + @NotNull List list) { List implicitImports = ImportsUtil.getAllImplicitImports(file); List moduleImports = new ArrayList<>(ContainerUtil.filterIsInstance(implicitImports, PsiImportModuleStatement.class)); @@ -986,11 +1015,11 @@ public final class ImportHelper extends ImportHelperBase { } private static boolean isOnDemandStaticImported(@NotNull PsiJavaFile file, @NotNull Import anImport) { - if(!anImport.isStatic()) return false; + if (!anImport.isStatic()) return false; PsiImportList importList = file.getImportList(); - if(importList==null)return false; + if (importList == null) return false; for (PsiImportStaticStatement statement : importList.getImportStaticStatements()) { - if(!statement.isOnDemand()) return false; + if (!statement.isOnDemand()) return false; String packageName = StringUtil.getPackageName(anImport.name); if (statement.getImportReference() != null && packageName.equals(statement.getImportReference().getQualifiedName())) { diff --git a/java/java-tests/testData/psi/optimizeImports/DontShowOnDemandIfAllSingles.java b/java/java-tests/testData/psi/optimizeImports/DontShowOnDemandIfAllSingles.java new file mode 100644 index 000000000000..14f8e54cebde --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/DontShowOnDemandIfAllSingles.java @@ -0,0 +1,10 @@ +package p3; + +import p2.*; +import p1.List; + +public class A { + public static void main(String[] args) { + List frameList = List.of(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/optimizeImports/DontShowOnDemandIfAllSingles_after.java b/java/java-tests/testData/psi/optimizeImports/DontShowOnDemandIfAllSingles_after.java new file mode 100644 index 000000000000..5b919ae01c3c --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/DontShowOnDemandIfAllSingles_after.java @@ -0,0 +1,10 @@ +package p3; + +import p1.List; +import p2.*; + +public class A { + public static void main(String[] args) { + List frameList = List.of(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightOptimizeImportsTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightOptimizeImportsTest.java index b041c01ad1f3..1de9057be54a 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightOptimizeImportsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightOptimizeImportsTest.java @@ -15,6 +15,7 @@ public class LightOptimizeImportsTest extends LightJavaCodeInsightFixtureTestCas public void testLayoutOnDemandImportsFromTheSamePackageFirst() { myFixture.addClass("package a.a; public class A {}"); + myFixture.addClass("package a.a; public class C {}"); myFixture.addClass("package a.b; public class A {}"); myFixture.addClass("package a.b; public class B {}"); myFixture.addClass("package a.b; public class Boolean {}"); @@ -22,12 +23,14 @@ public class LightOptimizeImportsTest extends LightJavaCodeInsightFixtureTestCas package a; import a.a.A; + import a.a.C; import a.b.B; import a.b.Boolean; class Main { A a; B b; + C c; Boolean bool; }"""; myFixture.configureByText(JavaFileType.INSTANCE, code); @@ -48,6 +51,7 @@ public class LightOptimizeImportsTest extends LightJavaCodeInsightFixtureTestCas class Main { A a; B b; + C c; Boolean bool; }"""; myFixture.checkResult(result); diff --git a/java/java-tests/testSrc/com/intellij/java/psi/OptimizeImportsTest.java b/java/java-tests/testSrc/com/intellij/java/psi/OptimizeImportsTest.java index 33f69ca353e3..f4f33cf253f3 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/OptimizeImportsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/OptimizeImportsTest.java @@ -63,11 +63,15 @@ public class OptimizeImportsTest extends OptimizeImportsTestCase { JavaCodeStyleSettings javaSettings = JavaCodeStyleSettings.getInstance(getProject()); boolean preserveModuleImports = javaSettings.isPreserveModuleImports(); PackageEntryTable table = javaSettings.IMPORT_LAYOUT_TABLE; + int classOnDemand = javaSettings.CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND; + int NamesOnDemand = javaSettings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND; Disposer.register(getTestRootDisposable(), new Disposable() { @Override public void dispose() { javaSettings.setPreserveModuleImports(preserveModuleImports); javaSettings.IMPORT_LAYOUT_TABLE = table; + javaSettings.CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND = classOnDemand; + javaSettings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND = NamesOnDemand; } }); myFixture.enableInspections(new UnusedDeclarationInspection()); @@ -220,6 +224,17 @@ public class OptimizeImportsTest extends OptimizeImportsTestCase { }); } + + public void testDontShowOnDemandIfAllSingles() { + myFixture.addClass("package p1; public class List {}"); + myFixture.addClass("package p2; public class List {}"); + myFixture.addClass("package p2; public class Something {}"); + JavaCodeStyleSettings javaSettings = JavaCodeStyleSettings.getInstance(getProject()); + javaSettings.CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND = 1; + javaSettings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND = 1; + doTest(); + } + public void testConflictStaticImport(){ myFixture.addClass( """