mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[java-inspection] IDEA-363979 Conflicts with on-demand and module imports
(cherry picked from commit 0857df90e3f9cc450b48b56186573a9a89b5b041) GitOrigin-RevId: 8e1474a59c687561ba5f00a13fecd4d2a9057b7a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6113015d06
commit
3348e7291f
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user