[java-inspection] IDEA-363979 Conflicts with on-demand and module imports

(cherry picked from commit 0857df90e3f9cc450b48b56186573a9a89b5b041)

GitOrigin-RevId: 8e1474a59c687561ba5f00a13fecd4d2a9057b7a
This commit is contained in:
Mikhail Pyltsin
2024-12-05 17:59:11 +01:00
committed by intellij-monorepo-bot
parent 6113015d06
commit 3348e7291f
23 changed files with 700 additions and 151 deletions

View File

@@ -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<PsiImportStatementBa
@Override
public int compare(PsiImportStatementBase importStatementBase1, PsiImportStatementBase importStatementBase2) {
final boolean onDemand = importStatementBase1.isOnDemand();
if (onDemand != importStatementBase2.isOnDemand()) {
return onDemand ? -1 : 1;
final boolean onDemand1 = importStatementBase1.isOnDemand();
final boolean onDemand2 = importStatementBase2.isOnDemand();
if (onDemand1 != onDemand2) {
return onDemand1 ? -1 : 1;
}
// just sort on demand imports first, and sort the rest in reverse file order.
if (onDemand1) {
boolean isModule1 = importStatementBase1 instanceof PsiImportModuleStatement;
boolean isModule2 = importStatementBase2 instanceof PsiImportModuleStatement;
if (isModule1 != isModule2) {
return isModule1 ? -1 : 1;
}
}
// just sort on module import first, then on demand imports, and sort the rest in reverse file order.
return -PsiElementOrderComparator.getInstance().compare(importStatementBase1, importStatementBase2);
}
}

View File

@@ -118,7 +118,7 @@ class ImportsAreUsedVisitor extends JavaRecursiveElementWalkingVisitor {
if (memberPackageName == null) {
return null;
}
final boolean hasOnDemandImportConflict = ImportUtils.hasOnDemandImportConflict(memberQualifiedName, myFile);
ImportUtils.OnDemandImportConflicts conflicts = ImportUtils.findOnDemandImportConflict(memberQualifiedName, myFile);
for (PsiImportStatementBase importStatement : importStatements) {
if (!importStatement.isOnDemand()) {
final PsiJavaCodeReferenceElement reference = importStatement.getImportReference();
@@ -133,7 +133,12 @@ class ImportsAreUsedVisitor extends JavaRecursiveElementWalkingVisitor {
}
}
else {
if (hasOnDemandImportConflict) {
if (importStatement instanceof PsiImportModuleStatement && conflicts.conflictForModules()) {
continue;
}
if (!(importStatement instanceof PsiImportModuleStatement) &&
importStatement.isOnDemand() &&
conflicts.conflictForOnDemand()) {
continue;
}
if (importStatement instanceof PsiImportModuleStatement psiImportModuleStatement &&

View File

@@ -25,7 +25,6 @@ import com.siyeh.HardcodedMethodConstants;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.fixes.DeleteImportFix;
import com.siyeh.ig.psiutils.ImportUtils;
import org.jetbrains.annotations.NotNull;
@@ -81,7 +80,7 @@ public final class JavaLangImportInspection extends BaseInspection implements Cl
if (!HardcodedMethodConstants.JAVA_LANG.equals(parentName)) {
return;
}
if (ImportUtils.hasOnDemandImportConflict(text, statement)) {
if (ImportUtils.findOnDemandImportConflict(text, statement).conflictForOnDemand()) {
return;
}
registerError(statement);

View File

@@ -20,16 +20,20 @@ import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaFileCodeStyleFacade;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.*;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -67,12 +71,26 @@ public final class ImportUtils {
if (containingPackageName.equals(packageName) || importList.findSingleClassImportStatement(qualifiedName) != null) {
return;
}
if ((createImplicitImportChecker(javaFile).isImplicitlyImported(qualifiedName, false) ||
importList.findOnDemandImportStatement(packageName) != null ||
ContainerUtil.exists(importList.getImportModuleStatements(),
moduleStatement -> 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<PsiImportStatementBase> 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<? extends PsiImportStatementBase> 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<? extends PsiImportStatementBase> 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<Boolean> 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;

View File

@@ -203,7 +203,7 @@ public abstract class ImportClassFixBase<T extends PsiElement, R extends PsiRefe
classList = new ArrayList<>(filtered);
}
filerByPackageName(classList, psiFile);
filterByPackageName(classList, psiFile);
filterAlreadyImportedButUnresolved(classList, psiFile);
@@ -232,7 +232,7 @@ public abstract class ImportClassFixBase<T extends PsiElement, R extends PsiRefe
return false;
}
private void filerByPackageName(@NotNull Collection<PsiClass> classList, @NotNull PsiFile file) {
private void filterByPackageName(@NotNull Collection<PsiClass> classList, @NotNull PsiFile file) {
String qualifiedName = getQualifiedName(myReferenceElement);
String packageName = StringUtil.getPackageName(qualifiedName);
if (!packageName.isEmpty() &&

View File

@@ -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<String> 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<PsiElement> 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.

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -24,7 +24,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class JavaFileElementType extends ILightStubFileElementType<PsiJavaFileStub> {
public static final int STUB_VERSION = 60;
public static final int STUB_VERSION = 61;
private static volatile int TEST_STUB_VERSION_MODIFIER = 0;

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>a.java</file>
<line>1</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unused import</problem_class>
<description>Unused import &lt;code&gt;import java.util.*;&lt;/code&gt; #loc</description>
</problem>
</problems>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Main.java</file>
<line>2</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unused import</problem_class>
<description>Unused import &lt;code&gt;import java.util.*;/**/&lt;/code&gt; #loc</description>
</problem>
</problems>

View File

@@ -4,6 +4,6 @@
<file>Main.java</file>
<line>1</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unused import</problem_class>
<description>Unused import &lt;code&gt;import module java.base;&lt;/code&gt; #loc</description>
<description>Unused import &lt;code&gt;import module java.base;/**/&lt;/code&gt; #loc</description>
</problem>
</problems>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Main.java</file>
<line>2</line>
<problem_class id="UNUSED_IMPORT" severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unused import</problem_class>
<description>Unused import &lt;code&gt;import java.util.List;&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>Main.java</file>
<line>3</line>
<problem_class id="UNUSED_IMPORT" severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unused import</problem_class>
<description>Unused import &lt;code&gt;import java.util.ArrayList;&lt;/code&gt; #loc</description>
</problem>
</problems>

View File

@@ -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() {
}

View File

@@ -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() {
}

View File

@@ -0,0 +1,16 @@
package p2;
import p1.*;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
List myName;
void main() {
}

View File

@@ -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() {
}

View File

@@ -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("");
}

View File

@@ -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("");
}

View File

@@ -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.L<caret>ist 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.L<caret>ist 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.L<caret>ist 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.L<caret>ist 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.L<caret>ist 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.L<caret>ist 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;
A<caret>5 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;
A<caret>5 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(){
}
""");
});
}
}

View File

@@ -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(
"""

View File

@@ -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() {

View File

@@ -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<String> 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<String> 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);