diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/unusedImport/ImportStatementComparator.java b/java/java-analysis-impl/src/com/intellij/codeInspection/unusedImport/ImportStatementComparator.java index c768437b24aa..8b18052ced63 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/unusedImport/ImportStatementComparator.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/unusedImport/ImportStatementComparator.java @@ -1,6 +1,7 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInspection.unusedImport; +import com.intellij.psi.PsiImportModuleStatement; import com.intellij.psi.PsiImportStatementBase; import com.siyeh.ig.psiutils.PsiElementOrderComparator; @@ -20,11 +21,19 @@ final class ImportStatementComparator implements Comparator moduleStatement.findImportedPackage(packageName) != null)) - && !hasOnDemandImportConflict(qualifiedName, javaFile)) { - return; + OnDemandImportConflicts conflict = null; + ImplicitImportChecker implicitImportChecker = createImplicitImportChecker(javaFile); + if (implicitImportChecker.isImplicitlyImported(qualifiedName, false)) { + conflict = findOnDemandImportConflict(qualifiedName, javaFile); + if (implicitImportChecker.isImplicitlyImported(qualifiedName, false, conflict)) { + return; + } + } + if (importList.findOnDemandImportStatement(packageName) != null) { + if (conflict == null) { + conflict = findOnDemandImportConflict(qualifiedName, javaFile); + } + if (!conflict.conflictForOnDemand()) return; + } + if (ContainerUtil.exists(importList.getImportModuleStatements(), + moduleStatement -> moduleStatement.findImportedPackage(packageName) != null)) { + if (conflict == null) { + conflict = findOnDemandImportConflict(qualifiedName, javaFile); + } + if (!conflict.conflictForModules() && !conflict.conflictForOnDemand()) return; } if (hasExactImportConflict(qualifiedName, javaFile)) { return; @@ -131,7 +149,8 @@ public final class ImportUtils { if (hasExactImportConflict(fqName, file)) { return false; } - if (hasOnDemandImportConflict(fqName, file, true) && !isAlreadyImported(file, fqName) + if (findOnDemandImportConflict(fqName, file, true, true, true) && + !isAlreadyImported(file, fqName) ) { return false; } @@ -222,17 +241,59 @@ public final class ImportUtils { return false; } - public static boolean hasOnDemandImportConflict(@NotNull String fqName, @NotNull PsiElement context) { - return hasOnDemandImportConflict(fqName, context, false); + /** + * Represents the conflicts that can occur with on-demand imports in a Java file. + * This record captures whether there is a conflict with on-demand imports involving + * class names and module imports. + * The `conflictForOnDemand` indicates if there is a conflict when importing classes + * using on-demand imports, such as `import package.*;`. + * The `conflictForModules` indicates if there is a conflict when considering module-related + * imports. + * + * @param conflictForOnDemand true if there is a conflict with on-demand imports, false otherwise + * @param conflictForModules true if there is a conflict related to modules, false otherwise + */ + public record OnDemandImportConflicts(boolean conflictForOnDemand, boolean conflictForModules) {} + + /** + * Finds conflicts related to on-demand imports in the context of a given fully qualified name. + * This method checks for potential conflicts with existing imports in the specified context, + * considering both class-level on-demand imports and module-related imports. + * + * @param fqName the fully qualified name of the class or package to check for import conflicts + * @param context the PSI element representing the context within which to check for import conflicts + * @return an OnDemandImportConflicts object that contains information about whether there are + * conflicts for on-demand imports and module imports + */ + public static OnDemandImportConflicts findOnDemandImportConflict(@NotNull String fqName, @NotNull PsiElement context) { + if(PsiUtil.isAvailable(JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS, context)) { + boolean onDemandConflict = findOnDemandImportConflict(fqName, context, false, true, false); + boolean moduleConflict = findOnDemandImportConflict(fqName, context, false, false, true); + return new OnDemandImportConflicts(onDemandConflict, moduleConflict); + } + boolean demandImportConflict = findOnDemandImportConflict(fqName, context, false, true, true); + return new OnDemandImportConflicts(demandImportConflict, demandImportConflict) ; } + /** + * @deprecated use {@link ImportUtils#findOnDemandImportConflict(String, PsiElement)} + */ + @Deprecated(forRemoval = true) + public static boolean hasOnDemandImportConflict(@NotNull String fqName, @NotNull PsiElement context) { + OnDemandImportConflicts conflict = findOnDemandImportConflict(fqName, context); + return conflict.conflictForOnDemand() || conflict.conflictForModules(); + } /** * @param strict if strict is true this method checks if the conflicting * class which is imported is actually used in the file. If it isn't the * on demand import can be overridden with an exact import for the fqName * without breaking stuff. */ - private static boolean hasOnDemandImportConflict(@NotNull String fqName, @NotNull PsiElement context, boolean strict) { + private static boolean findOnDemandImportConflict(@NotNull String fqName, + @NotNull PsiElement context, + boolean strict, + boolean considerOnDemand, + boolean considerModules) { final PsiFile containingFile = context.getContainingFile(); if (!(containingFile instanceof PsiJavaFile javaFile)) { return false; @@ -243,9 +304,9 @@ public final class ImportUtils { } final List importStatements = ContainerUtil.append(getAllImplicitImports(javaFile), imports.getAllImportStatements()); - ThreeState state = hasOnDemandImportConflictWithImports(javaFile, importStatements, fqName, strict); + ThreeState state = hasOnDemandImportConflictWithImports(javaFile, importStatements, fqName, strict, considerOnDemand, considerModules); if (state != ThreeState.UNSURE) return state.toBoolean(); - return hasDefaultImportConflict(fqName, javaFile); + return considerOnDemand && hasDefaultImportConflict(fqName, javaFile); } /** @@ -255,25 +316,31 @@ public final class ImportUtils { * @param javaFile the Java file to check for import conflicts. * @param importStatements the list of import statements to check against. * @param fqName the fully qualified name to check for conflicts. + * @param checkOnDemand check conflicts for all on-demand statements, excluding module import statements. + * @param checkModules check conflicts for module imports. * @return true if there is an on-demand import conflict, false otherwise. */ public static boolean hasOnDemandImportConflictWithImports(@NotNull PsiJavaFile javaFile, @NotNull List importStatements, - @NotNull String fqName) { - return hasOnDemandImportConflictWithImports(javaFile, importStatements, fqName, false) == ThreeState.YES; + @NotNull String fqName, + boolean checkOnDemand, + boolean checkModules) { + return hasOnDemandImportConflictWithImports(javaFile, importStatements, fqName, false, checkOnDemand, checkModules) == ThreeState.YES; } private static ThreeState hasOnDemandImportConflictWithImports(@NotNull PsiJavaFile javaFile, @NotNull List importStatements, @NotNull String fqName, - boolean strict) { + boolean strict, + boolean checkOnDemand, + boolean checkModules) { final String shortName = ClassUtil.extractClassName(fqName); final String packageName = ClassUtil.extractPackageName(fqName); for (final PsiImportStatementBase importStatement : importStatements) { if (!importStatement.isOnDemand()) { continue; } - if (importStatement instanceof PsiImportModuleStatement moduleStatement) { + if (checkModules && importStatement instanceof PsiImportModuleStatement moduleStatement) { //can't process, let's assume that we have conflict because it is safe if (DumbService.isDumb(javaFile.getProject())) return ThreeState.YES; Ref result = new Ref<>(null); @@ -303,6 +370,7 @@ public final class ImportUtils { }, scope, null); if (result.get() != null) return ThreeState.fromBoolean(result.get()); } + if (!checkOnDemand) continue; final PsiJavaCodeReferenceElement importReference = importStatement.getImportReference(); if (importReference == null) { continue; @@ -434,7 +502,9 @@ public final class ImportUtils { return false; } final PsiImportStaticStatement onDemandImportStatement = findOnDemandImportStaticStatement(importList, qualifierClass); - if (onDemandImportStatement != null && !hasOnDemandImportConflict(qualifierClass + '.' + memberName, javaFile)) { + if (onDemandImportStatement != null && + //check only on demands + !findOnDemandImportConflict(qualifierClass + '.' + memberName, javaFile, false, true, false)) { return true; } final Project project = context.getProject(); @@ -542,7 +612,7 @@ public final class ImportUtils { } final PsiImportStaticStatement onDemandImportStatement = findOnDemandImportStaticStatement(importList, memberClassName); if (onDemandImportStatement != null) { - if (!hasOnDemandImportConflict(memberClassName + '.' + memberName, javaFile)) { + if (!findOnDemandImportConflict(memberClassName + '.' + memberName, javaFile, false, true, false)) { return true; } } @@ -593,29 +663,38 @@ public final class ImportUtils { } public boolean isImplicitlyImported(String qName, boolean isStatic) { + return isImplicitlyImported(qName, isStatic, null); + } + + public boolean isImplicitlyImported(String qName, boolean isStatic, + @Nullable ImportUtils.OnDemandImportConflicts conflicts) { String packageOrClassName = StringUtil.getPackageName(qName); String className = ClassUtil.extractClassName(qName); if (!isStatic) { - for (PsiImportModuleStatement psiImportModuleStatement : myModulesStatements) { - PsiPackageAccessibilityStatement importedPackage = psiImportModuleStatement.findImportedPackage(packageOrClassName); - if (importedPackage == null) continue; - PsiJavaCodeReferenceElement reference = importedPackage.getPackageReference(); - if (reference == null) continue; - PsiElement resolved = reference.resolve(); - if (resolved instanceof PsiPackage psiPackage) { - if (psiPackage.containsClassNamed(className)) return true; + if(conflicts==null || (!conflicts.conflictForModules() && !conflicts.conflictForOnDemand())){ + for (PsiImportModuleStatement psiImportModuleStatement : myModulesStatements) { + PsiPackageAccessibilityStatement importedPackage = psiImportModuleStatement.findImportedPackage(packageOrClassName); + if (importedPackage == null) continue; + PsiJavaCodeReferenceElement reference = importedPackage.getPackageReference(); + if (reference == null) continue; + PsiElement resolved = reference.resolve(); + if (resolved instanceof PsiPackage psiPackage) { + if (psiPackage.containsClassNamed(className)) return true; + } } } - if (myPackageStatements.containsKey(packageOrClassName)) return true; + if ((conflicts == null || !conflicts.conflictForOnDemand()) && myPackageStatements.containsKey(packageOrClassName)) return true; } else { - PsiImportStaticStatement psiImportStaticStatement = myStaticImportStatements.get(packageOrClassName); - if (psiImportStaticStatement != null) { - if (psiImportStaticStatement.isOnDemand()) return true; - PsiJavaCodeReferenceElement reference = psiImportStaticStatement.getImportReference(); - if (reference == null) return false; - String qualifiedName = reference.getQualifiedName(); - return qName.equals(qualifiedName); + if (conflicts == null || !conflicts.conflictForOnDemand()) { + PsiImportStaticStatement psiImportStaticStatement = myStaticImportStatements.get(packageOrClassName); + if (psiImportStaticStatement != null) { + if (psiImportStaticStatement.isOnDemand()) return true; + PsiJavaCodeReferenceElement reference = psiImportStaticStatement.getImportReference(); + if (reference == null) return false; + String qualifiedName = reference.getQualifiedName(); + return qName.equals(qualifiedName); + } } } return false; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java index b481ec5d0d06..e94048a08b72 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java @@ -206,7 +206,7 @@ public abstract class ImportClassFixBase(filtered); } - filerByPackageName(classList, psiFile); + filterByPackageName(classList, psiFile); filterAlreadyImportedButUnresolved(classList, psiFile); @@ -235,7 +235,7 @@ public abstract class ImportClassFixBase classList, @NotNull PsiFile file) { + private void filterByPackageName(@NotNull Collection classList, @NotNull PsiFile file) { String qualifiedName = getQualifiedName(myReferenceElement); String packageName = StringUtil.getPackageName(qualifiedName); if (!packageName.isEmpty() && 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 6cddec6d8a65..fb0802868be4 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 @@ -13,6 +13,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NlsSafe; import com.intellij.openapi.util.Predicates; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; @@ -341,7 +342,7 @@ public final class ImportHelper { if (implicitModuleImports.isEmpty() && conflicts.isEmpty()) return Collections.emptySet(); Set result = new HashSet<>(); - String packageName = file.getPackageName(); + String filePackageName = file.getPackageName(); ImportUtils.ImplicitImportChecker checker = ImportUtils.createImplicitImportChecker(file); for (PsiClass aClass : file.getClasses()) { // do not visit imports @@ -354,20 +355,30 @@ public final class ImportHelper { if (!(resolveResult.getElement() instanceof PsiClass psiClass)) return; String qualifiedName = psiClass.getQualifiedName(); if (qualifiedName == null) return; + String referencePackageName = StringUtil.getPackageName(qualifiedName); + String referenceShortName = StringUtil.getShortName(qualifiedName); //conflict with packages boolean hasConflict = conflicts.contains(psiClass.getName()); if (!hasConflict) { - //conflict with implicit module imports - hasConflict = !implicitModuleImports.isEmpty() && - ImportUtils.hasOnDemandImportConflictWithImports(file, implicitModuleImports, qualifiedName); + + //check conflict only with implicit module imports + //explicit module imports should be checked separately, right now it is not supported + //if it is already imported by on demands, there is no conflict with modules + if (!(PsiUtil.isAvailable(JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS, file) && + classNames.getOrDefault(referencePackageName, Collections.emptySet()).contains(referenceShortName))) { + hasConflict = !implicitModuleImports.isEmpty() && + ImportUtils.hasOnDemandImportConflictWithImports(file, implicitModuleImports, qualifiedName, false, true); + } } if (!hasConflict) return; + //can be visible by inheritance if (!(resolveResult.getCurrentFileResolveScope() instanceof PsiImportStatementBase) && !isImplicitlyImported(psiClass, checker)) { return; } + //in the same package or in the same class if (PsiTreeUtil.isAncestor(file, psiClass, true) || - packageName.equals(StringUtil.getPackageName(qualifiedName))) { + filePackageName.equals(referencePackageName)) { return; } result.add(qualifiedName); @@ -455,6 +466,8 @@ public final class ImportHelper { boolean useOnDemand = !packageName.isEmpty(); + //right now supports import on demand only for packages + //it doesn't support add imports on module if (hasImportOnDemand(file, packageName)) { useOnDemand = false; } @@ -606,12 +619,12 @@ public final class ImportHelper { if (importedPackages.contains(packageNameToReimport)) { hasConflict = true; } - else { - for (PsiImportModuleStatement module : importedModules) { - if (module.findImportedPackage(packageNameToReimport) != null) { - hasConflict = true; - break; - } + //shouldn't filter for demand over module, because it is necessary to check, + //that class which is imported by module will not be shadowed by new on-demand package import + for (PsiImportModuleStatement module : importedModules) { + if (module.findImportedPackage(packageNameToReimport) != null) { + hasConflict = true; + break; } } if (hasConflict) { @@ -906,7 +919,7 @@ public final class ImportHelper { @NotNull String thisPackageName, @Nullable PsiFile context) { if (scope instanceof PsiImportList) return; - ImportUtils.ImplicitImportChecker checker = + ImportUtils.ImplicitImportChecker checker = scope.getContainingFile() instanceof PsiJavaFile javaFile ? ImportUtils.createImplicitImportChecker(javaFile) : null; Queue queue = new ArrayDeque<>(); @@ -952,6 +965,7 @@ public final class ImportHelper { PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope(); if (!(currentFileResolveScope instanceof PsiImportStatementBase) && refElement != null) { + //imported not with imports (implicit or explicit) if (!(refElement instanceof PsiClass psiClass && checker != null && isImplicitlyImported(psiClass, checker))) { continue; } @@ -1075,7 +1089,7 @@ public final class ImportHelper { } /** - * An imported element, e.g. a fully qualified class name. + * An imported element, e.g. a fully qualified class name. * This is an implementation detail, unfortunately public because of JavaFX, don't expose it in public API. * * @param name the fully qualified name of the element that should be imported. diff --git a/java/java-impl/src/com/siyeh/ipp/fqnames/FullyQualifiedNamePredicate.java b/java/java-impl/src/com/siyeh/ipp/fqnames/FullyQualifiedNamePredicate.java deleted file mode 100644 index 637b81adf8ac..000000000000 --- a/java/java-impl/src/com/siyeh/ipp/fqnames/FullyQualifiedNamePredicate.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2003-2017 Dave Griffith, Bas Leijdekkers - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ipp.fqnames; - -import com.intellij.application.options.CodeStyle; -import com.intellij.codeInsight.javadoc.JavaDocUtil; -import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.JavaCodeStyleSettings; -import com.intellij.psi.javadoc.PsiDocComment; -import com.intellij.psi.util.PsiTreeUtil; -import com.siyeh.ig.psiutils.ImportUtils; -import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.Nullable; - -class FullyQualifiedNamePredicate implements PsiElementPredicate { - - @Override - public boolean satisfiedBy(PsiElement element) { - if (!(element instanceof PsiJavaCodeReferenceElement referenceElement)) { - return false; - } - if (!referenceElement.isQualified()) { - return false; - } - final PsiElement parent = referenceElement.getParent(); - if (parent instanceof PsiMethodCallExpression || parent instanceof PsiAssignmentExpression || parent instanceof PsiVariable) { - return false; - } - if (PsiTreeUtil.getParentOfType(element, PsiImportStatementBase.class, PsiPackageStatement.class, JavaCodeFragment.class) != null) { - return false; - } - final CodeStyleSettings codeStyleSettings = CodeStyle.getSettings(element.getContainingFile()); - if (isInsideCommentInPackageInfo(referenceElement)) { - return false; - } - final PsiFile file = element.getContainingFile(); - if ("module-info.java".equals(file.getName())) { - return false; - } - final PsiElement qualifier = referenceElement.getQualifier(); - if (!(qualifier instanceof PsiJavaCodeReferenceElement qualifierReferenceElement)) { - return false; - } - final PsiElement resolved = qualifierReferenceElement.resolve(); - if (!(resolved instanceof PsiPackage)) { - if (!(resolved instanceof PsiClass)) { - return false; - } - if (!codeStyleSettings.getCustomSettings(JavaCodeStyleSettings.class).INSERT_INNER_CLASS_IMPORTS) { - return false; - } - } - final PsiElement target = referenceElement.resolve(); - if (!(target instanceof PsiClass aClass)) { - return false; - } - final String fqName = aClass.getQualifiedName(); - return fqName != null && ImportUtils.nameCanBeImported(fqName, element); - } - - private static boolean isInsideCommentInPackageInfo(@Nullable PsiJavaCodeReferenceElement referenceElement) { - final PsiDocComment containingComment = PsiTreeUtil.getParentOfType(referenceElement, PsiDocComment.class); - return JavaDocUtil.isInsidePackageInfo(containingComment); - } -} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaImportStatementElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaImportStatementElementType.java index 76ca9ed12233..aabab65f925a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaImportStatementElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaImportStatementElementType.java @@ -59,11 +59,10 @@ public abstract class JavaImportStatementElementType extends JavaStubElementType type == JavaElementType.MODULE_REFERENCE) { refText = JavaSourceUtil.getReferenceText(tree, child); } - else if (type == JavaTokenType.DOT) { + else if (type == JavaTokenType.DOT || type == JavaTokenType.MODULE_KEYWORD) { isOnDemand = true; } } - byte flags = PsiImportStatementStubImpl.packFlags(isOnDemand, node.getTokenType() == JavaElementType.IMPORT_STATIC_STATEMENT, node.getTokenType() == JavaElementType.IMPORT_MODULE_STATEMENT); return new PsiImportStatementStubImpl(parentStub, refText, flags); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java index a6f8e66b0459..eac75aebe935 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java @@ -24,7 +24,7 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; public class JavaFileElementType extends ILightStubFileElementType { - public static final int STUB_VERSION = 60; + public static final int STUB_VERSION = 61; private static volatile int TEST_STUB_VERSION_MODIFIER = 0; diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/ImportImplicitModuleWithRedundantPackage/expected.xml b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/ImportImplicitModuleWithRedundantPackage/expected.xml new file mode 100644 index 000000000000..d098c743a876 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/ImportImplicitModuleWithRedundantPackage/expected.xml @@ -0,0 +1,9 @@ + + + + a.java + 1 + Unused import + Unused import <code>import java.util.*;</code> #loc + + \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/ImportModuleWithRedundantPackage/expected.xml b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/ImportModuleWithRedundantPackage/expected.xml new file mode 100644 index 000000000000..6325be52a180 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/ImportModuleWithRedundantPackage/expected.xml @@ -0,0 +1,9 @@ + + + + Main.java + 2 + Unused import + Unused import <code>import java.util.*;/**/</code> #loc + + \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/RedundantModuleImport/expected.xml b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/RedundantImportModuleWithNotRedundantPackage/expected.xml similarity index 88% rename from java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/RedundantModuleImport/expected.xml rename to java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/RedundantImportModuleWithNotRedundantPackage/expected.xml index 23ac85ee646b..95f7ead4ae48 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/RedundantModuleImport/expected.xml +++ b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/RedundantImportModuleWithNotRedundantPackage/expected.xml @@ -4,6 +4,6 @@ Main.java 1 Unused import - Unused import <code>import module java.base;</code> #loc + Unused import <code>import module java.base;/**/</code> #loc \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/SingleImportWithModuleImport/expected.xml b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/SingleImportWithModuleImport/expected.xml new file mode 100644 index 000000000000..9bd0e9c3a7d6 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/imports/globalInspection/SingleImportWithModuleImport/expected.xml @@ -0,0 +1,15 @@ + + + + Main.java + 2 + Unused import + Unused import <code>import java.util.List;</code> #loc + + + Main.java + 3 + Unused import + Unused import <code>import java.util.ArrayList;</code> #loc + + \ No newline at end of file diff --git a/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule.java b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule.java new file mode 100644 index 000000000000..73f7192ba90b --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule.java @@ -0,0 +1,20 @@ +package p2; + +import p1.A1; +import p1.A2; +import p1.A3; +import p1.A4; +import p1.A5; + + +A1 a1; +A2 a2; +A3 a3; +A4 a4; +A5 a5; + +List myName; + +void main() { + +} diff --git a/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule2.java b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule2.java new file mode 100644 index 000000000000..cda24fad7cc2 --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule2.java @@ -0,0 +1,21 @@ +package p2; + +import p1.A1; +import p1.A2; +import p1.A3; +import p1.A4; +import p1.A5; +import p1.List; + + +A1 a1; +A2 a2; +A3 a3; +A4 a4; +A5 a5; + +List myName; + +void main() { + +} diff --git a/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule2_after.java b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule2_after.java new file mode 100644 index 000000000000..4b850aa8fb14 --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule2_after.java @@ -0,0 +1,16 @@ +package p2; + +import p1.*; + + +A1 a1; +A2 a2; +A3 a3; +A4 a4; +A5 a5; + +List myName; + +void main() { + +} diff --git a/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule_after.java b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule_after.java new file mode 100644 index 000000000000..d0727797b012 --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/ConflictModuleImportImplicitClassDemandOverModule_after.java @@ -0,0 +1,18 @@ +package p2; + +import p1.*; + +import java.util.List; + + +A1 a1; +A2 a2; +A3 a3; +A4 a4; +A5 a5; + +List myName; + +void main() { + +} diff --git a/java/java-tests/testData/psi/optimizeImports/ConflictStaticImportWithImplicitClassDemandOverModule.java b/java/java-tests/testData/psi/optimizeImports/ConflictStaticImportWithImplicitClassDemandOverModule.java new file mode 100644 index 000000000000..b71adf0760e3 --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/ConflictStaticImportWithImplicitClassDemandOverModule.java @@ -0,0 +1,18 @@ +package p2; + +import static p1.A1.foo1; +import static p1.A1.foo2; +import static p1.A1.foo3; +import static p1.A1.foo4; +import static p1.A1.foo5; + + +public static void main(String[] args) { + foo1(); + foo2(); + foo3(); + foo4(); + foo5(); + print(""); +} + diff --git a/java/java-tests/testData/psi/optimizeImports/ConflictStaticImportWithImplicitClassDemandOverModule_after.java b/java/java-tests/testData/psi/optimizeImports/ConflictStaticImportWithImplicitClassDemandOverModule_after.java new file mode 100644 index 000000000000..6f804c6be347 --- /dev/null +++ b/java/java-tests/testData/psi/optimizeImports/ConflictStaticImportWithImplicitClassDemandOverModule_after.java @@ -0,0 +1,15 @@ +package p2; + +import static java.io.IO.print; +import static p1.A1.*; + + +public static void main(String[] args) { + foo1(); + foo2(); + foo3(); + foo4(); + foo5(); + print(""); +} + diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddImportActionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddImportActionTest.java index 5f5fa6a4b7da..0144a88c39d3 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddImportActionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddImportActionTest.java @@ -841,9 +841,7 @@ public class AddImportActionTest extends LightJavaCodeInsightFixtureTestCase { } } """); - myFixture.enableInspections(new UnnecessaryFullyQualifiedNameInspection()); - myFixture.launchAction(myFixture.findSingleIntention("Remove unnecessary qualification")); - NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); + removeUnnecessaryQualification(); myFixture.checkResult(""" class Test { @@ -857,6 +855,12 @@ public class AddImportActionTest extends LightJavaCodeInsightFixtureTestCase { """); } + private void removeUnnecessaryQualification() { + myFixture.enableInspections(new UnnecessaryFullyQualifiedNameInspection()); + myFixture.launchAction(myFixture.findSingleIntention("Remove unnecessary qualification")); + NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); + } + public void testDoNotAllowToAddImportInPackageInfoFile() { myFixture.configureByText("package-info.java", """ @@ -1097,4 +1101,264 @@ public class AddImportActionTest extends LightJavaCodeInsightFixtureTestCase { assertTrue(myFixture.filterAvailableIntentions("Import class").isEmpty()); }); } + + public void testModuleImportClassUnnecessaryQualifier() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass("package a; public class List {}"); + + myFixture.configureByText("Test.java", """ + import a.*; + import module java.base; + class Test{ + void main(){ + java.util.List x; + } + } + """); + + reimportClass(); + myFixture.checkResult(""" + import a.*; + import module java.base; + + import java.util.List; + + class Test{ + void main(){ + List x; + } + } + """); + }); + } + + public void testModuleImportClassUnnecessaryQualifierAlreadyUsed() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass("package a; public class List {}"); + + myFixture.configureByText("Test.java", """ + import a.*; + import module java.base; + class Test{ + void main(){ + java.util.List x; + List y; + } + } + """); + + myFixture.enableInspections(new UnnecessaryFullyQualifiedNameInspection()); + assertTrue(myFixture.filterAvailableIntentions("Replace qualified name with import").isEmpty()); + }); + } + + public void testModuleImportClassUnnecessaryQualifierImportFromDemand() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass("package a; public class List {}"); + + myFixture.configureByText("Test.java", """ + import a.*; + import module java.base; + class Test{ + void main(){ + a.List x; + } + } + """); + + removeUnnecessaryQualification(); + myFixture.checkResult(""" + import a.*; + import module java.base; + class Test{ + void main(){ + List x; + } + } + """); + }); + } + + public void testModuleImportUnnecessaryQualifierAlreadyImported() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.configureByText("Test.java", """ + import module java.base; + class Test{ + void main(){ + java.util.List x; + } + } + """); + + removeUnnecessaryQualification(); + myFixture.checkResult(""" + import module java.base; + class Test{ + void main(){ + List x; + } + } + """); + }); + } + + public void testImplicitClassUnnecessaryQualifier() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass("package a; public class List {}"); + + myFixture.configureByText("Test.java", """ + import a.*; + + void main(){ + java.util.List x; + } + """); + + reimportClass(); + myFixture.checkResult(""" + import a.*; + + import java.util.List; + + void main(){ + List x; + } + """); + }); + } + + public void testImplicitClassUnnecessaryQualifierAlreadyImported() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + + myFixture.configureByText("Test.java", """ + void main(){ + java.util.List x; + } + """); + + removeUnnecessaryQualification(); + myFixture.checkResult(""" + void main(){ + List x; + } + """); + }); + } + + public void testImportFoldingWithConflictsToJavaBaseModuleImplicitClassDemandsOverModule() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass("package p1; public class List {}"); + myFixture.addClass("package p1; public class A1 {}"); + myFixture.addClass("package p1; public class A2 {}"); + myFixture.addClass("package p1; public class A3 {}"); + myFixture.addClass("package p1; public class A4 {}"); + myFixture.addClass("package p1; public class A5 {}"); + + myFixture.configureByText("C.java", + """ + + import module java.base; + import p1.A1; + import p1.A2; + import p1.A3; + import p1.A4; + + + A1 a1; + A2 a2; + A3 a3; + A4 a4; + A5 a5; + + List myName; + + void main(){ + } + """); + importClass(); + + myFixture.checkResult( + """ + + import module java.base; + import p1.*; + + import java.util.List; + + + A1 a1; + A2 a2; + A3 a3; + A4 a4; + A5 a5; + + List myName; + + void main(){ + } + """); + }); + } + + public void testImportFoldingWithConflictsToJavaBaseModuleImplicitClassFromPackageDemandsOverModule() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass("package p1; public class List {}"); + myFixture.addClass("package p1; public class A1 {}"); + myFixture.addClass("package p1; public class A2 {}"); + myFixture.addClass("package p1; public class A3 {}"); + myFixture.addClass("package p1; public class A4 {}"); + myFixture.addClass("package p1; public class A5 {}"); + + myFixture.configureByText("C.java", + """ + + import module java.base; + import p1.A1; + import p1.A2; + import p1.A3; + import p1.A4; + import p1.List; + + + A1 a1; + A2 a2; + A3 a3; + A4 a4; + A5 a5; + + List myName; + + void main(){ + } + """); + importClass(); + + myFixture.checkResult( + """ + + import module java.base; + import p1.*; + + + A1 a1; + A2 a2; + A3 a3; + A4 a4; + A5 a5; + + List myName; + + void main(){ + } + """); + }); + } } 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 81e2ccab837b..1d1ead439595 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/OptimizeImportsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/OptimizeImportsTest.java @@ -182,6 +182,30 @@ public class OptimizeImportsTest extends OptimizeImportsTestCase { doTest(); } + public void testConflictModuleImportImplicitClassDemandOverModule() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + myFixture.addClass("package p1; public class List {}"); + myFixture.addClass("package p1; public class A1 {}"); + myFixture.addClass("package p1; public class A2 {}"); + myFixture.addClass("package p1; public class A3 {}"); + myFixture.addClass("package p1; public class A4 {}"); + myFixture.addClass("package p1; public class A5 {}"); + doTest(); + }); + } + + public void testConflictModuleImportImplicitClassDemandOverModule2() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + myFixture.addClass("package p1; public class List {}"); + myFixture.addClass("package p1; public class A1 {}"); + myFixture.addClass("package p1; public class A2 {}"); + myFixture.addClass("package p1; public class A3 {}"); + myFixture.addClass("package p1; public class A4 {}"); + myFixture.addClass("package p1; public class A5 {}"); + doTest(); + }); + } + public void testConflictStaticImport(){ myFixture.addClass( """ @@ -206,6 +230,34 @@ public class OptimizeImportsTest extends OptimizeImportsTestCase { doTest(); } + public void testConflictStaticImportWithImplicitClassDemandOverModule() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), () -> { + + myFixture.addClass( + """ + package p1; + public class A1 { + public static void print(Object obj) {} + public static void foo() {} + public static void foo1() {} + public static void foo2() {} + public static void foo3() {} + public static void foo4() {} + public static void foo5() {} + } + """); + myFixture.addClass(""" + package java.io; + + public final class IO { + public static void print(Object obj) {} + } + """); + doTest(); + + }); + } + public void testConflictStaticImportWithImplicitClass(){ myFixture.addClass( """ diff --git a/java/java-tests/testSrc/com/siyeh/ig/imports/JavaLangImportInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/imports/JavaLangImportInspectionTest.java index 3d823536be4f..8973b03c2752 100644 --- a/java/java-tests/testSrc/com/siyeh/ig/imports/JavaLangImportInspectionTest.java +++ b/java/java-tests/testSrc/com/siyeh/ig/imports/JavaLangImportInspectionTest.java @@ -29,9 +29,9 @@ public class JavaLangImportInspectionTest extends LightJavaInspectionTestCase { "class String {}"); doTest("package a;" + "import java.lang.String;" + - "class X {{" + + "class X {" + " String s;" + - "}}"); + "}"); } public void testSimple() { @@ -42,6 +42,20 @@ public class JavaLangImportInspectionTest extends LightJavaInspectionTestCase { "}}"); } + public void testPackageOnDemandConflict() { + myFixture.addClass(""" + package b; + class String { + }"""); + doTest(""" + package a; + import java.lang.String; + import b.*; + class X { + String s; + }"""); + } + @Nullable @Override protected InspectionProfileEntry getInspection() { diff --git a/java/java-tests/testSrc/com/siyeh/ig/imports/UnusedImportGlobalInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/imports/UnusedImportGlobalInspectionTest.java index c04b36d08304..b8c10ef45900 100644 --- a/java/java-tests/testSrc/com/siyeh/ig/imports/UnusedImportGlobalInspectionTest.java +++ b/java/java-tests/testSrc/com/siyeh/ig/imports/UnusedImportGlobalInspectionTest.java @@ -300,11 +300,11 @@ public class UnusedImportGlobalInspectionTest extends LightJavaCodeInsightFixtur doTest(); } - public void testRedundantModuleImport() { + public void testSingleImportWithModuleImport() { doTest(""" - /*Unused import 'import module java.base;'*/import module java.base;/**/ - import java.util.List; - import java.util.ArrayList; + import module java.base; + /*Unused import 'import java.util.List;'*/import java.util.List;/**/ + /*Unused import 'import java.util.ArrayList;'*/import java.util.ArrayList;/**/ class Main { public static void main(String[] args) { @@ -339,6 +339,58 @@ public class UnusedImportGlobalInspectionTest extends LightJavaCodeInsightFixtur }); } + public void testImportModuleWithRedundantPackage() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), + () -> { + doTest(""" + import module java.base; + /*Unused import 'import java.util.*;'*/import java.util.*;/**/ + + class Main { + public static void main(String[] args) { + List a = new ArrayList<>(); + } + } + """); + }); + } + + public void testImportImplicitModuleWithRedundantPackage() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), + () -> { + doTest(""" + /*Unused import 'import java.util.*;'*/import java.util.*;/**/ + + public static void main(String[] args) { + List a = new ArrayList<>(); + } + """); + }); + } + + + public void testRedundantImportModuleWithNotRedundantPackage() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.PACKAGE_IMPORTS_SHADOW_MODULE_IMPORTS.getMinimumLevel(), + () -> { + myFixture.addClass(""" + package a.b; + + public final class List { + } + """); + doTest(""" + /*Unused import 'import module java.base;'*/import module java.base;/**/ + import a.b.*; + + class Main { + public static void main(String[] args) { + List a; + } + } + """); + }); + } + private void doTest(String classText) { myFixture.addClass(classText);