diff --git a/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaModule.java b/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaModule.java index 6129f2577fcf..2139ad04cc30 100644 --- a/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaModule.java +++ b/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaModule.java @@ -33,8 +33,15 @@ public interface RefJavaModule extends RefElement { Map> getExportedPackageNames(); @NotNull - Map getRequiredModuleNames(); + Map getRequiredModules(); - @NotNull - Map getRequiresStatements(); + class Dependency { + @NotNull public final Map> packageNames; + public final boolean isPublic; + + public Dependency(@NotNull Map> packageNames, boolean isPublic) { + this.packageNames = packageNames; + this.isPublic = isPublic; + } + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java19modules/Java9RedundantRequiresStatementInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java19modules/Java9RedundantRequiresStatementInspection.java index 537bc9f853d3..a4de49ed87bd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java19modules/Java9RedundantRequiresStatementInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java19modules/Java9RedundantRequiresStatementInspection.java @@ -29,7 +29,6 @@ import com.intellij.psi.*; import com.intellij.psi.impl.source.PsiJavaModuleReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; -import gnu.trove.THashMap; import gnu.trove.THashSet; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; @@ -44,15 +43,8 @@ import java.util.*; public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchInspectionTool { private static final Logger LOG = Logger.getInstance(Java9RedundantRequiresStatementInspection.class); - // applicable to RefFile and RefModule private static final Key> IMPORTED_JAVA_PACKAGES = Key.create("imported_java_packages"); - // applicable to RefProject - // mappings: module name -> exported package name -> export to modules - private static final Key>>> EXPORTED_MODULE_PACKAGES = - Key.create("exported_module_packages"); - - @Nls @NotNull @Override @@ -60,29 +52,43 @@ public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchIn return InspectionsBundle.message("inspection.redundant.requires.statement.name"); } - @Override - public void runInspection(@NotNull AnalysisScope scope, - @NotNull InspectionManager manager, - @NotNull GlobalInspectionContext globalContext, - @NotNull ProblemDescriptionsProcessor problemDescriptionsProcessor) { - super.runInspection(scope, manager, globalContext, problemDescriptionsProcessor); - globalContext.getRefManager().iterate(new RedundantRequiresStatementVisitor(manager, problemDescriptionsProcessor)); - } - @Nullable @Override public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, @NotNull AnalysisScope scope, @NotNull InspectionManager manager, @NotNull GlobalInspectionContext globalContext) { - if (refEntity instanceof RefFile) { - RefModule refModule = ((RefFile)refEntity).getModule(); - if (refModule != null) { - Set importedPackages = refEntity.getUserData(IMPORTED_JAVA_PACKAGES); - if (!ContainerUtil.isEmpty(importedPackages)) { - Set moduleImportedPackages = refModule.getUserData(IMPORTED_JAVA_PACKAGES); - if (moduleImportedPackages != null) { - moduleImportedPackages.addAll(importedPackages); + if (refEntity instanceof RefJavaModule) { + RefJavaModule refJavaModule = (RefJavaModule)refEntity; + + RefModule refModule = refJavaModule.getModule(); + PsiJavaModule psiJavaModule = refJavaModule.getElement(); + if (refModule != null && psiJavaModule != null) { + Set moduleImportedPackages = refModule.getUserData(IMPORTED_JAVA_PACKAGES); + if (moduleImportedPackages != null) { + Map requiredModules = refJavaModule.getRequiredModules(); + if (!requiredModules.isEmpty()) { + List descriptors = new ArrayList<>(); + for (Map.Entry entry : requiredModules.entrySet()) { + String requiredModuleName = entry.getKey(); + RefJavaModule.Dependency dependency = entry.getValue(); + + if (isDependencyUnused(dependency.packageNames, moduleImportedPackages, refJavaModule.getName())) { + PsiRequiresStatement requiresStatement = ContainerUtil.find( + psiJavaModule.getRequires(), statement -> requiredModuleName.equals(statement.getModuleName())); + if (requiresStatement != null) { + CommonProblemDescriptor descriptor = manager.createProblemDescriptor( + requiresStatement, + InspectionsBundle.message("inspection.redundant.requires.statement.description", requiredModuleName), + new DeleteRedundantRequiresStatementFix(requiredModuleName, moduleImportedPackages), + ProblemHighlightType.LIKE_UNUSED_SYMBOL, false); + descriptors.add(descriptor); + } + } + } + if (!ContainerUtil.isEmpty(descriptors)) { + return descriptors.toArray(CommonProblemDescriptor.EMPTY_ARRAY); + } } } } @@ -112,25 +118,16 @@ public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchIn return new RedundantRequiresStatementAnnotator(); } - @NotNull - private static Map>> getExportedModulePackages(RefManager refManager) { - RefProject refProject = refManager.getRefProject(); - Map>> exportedModulePackages = refProject.getUserData(EXPORTED_MODULE_PACKAGES); - if (exportedModulePackages == null) { - exportedModulePackages = new THashMap<>(); - refProject.putUserData(EXPORTED_MODULE_PACKAGES, exportedModulePackages); - } - return exportedModulePackages; - } - private static PsiJavaModule resolveRequiredModule(PsiRequiresStatement requiresStatement) { return PsiJavaModuleReference.resolve(requiresStatement, requiresStatement.getModuleName(), false); } private static class DeleteRedundantRequiresStatementFix implements LocalQuickFix { + private String myRequiredModuleName; private Set myImportedPackages; - public DeleteRedundantRequiresStatementFix(Set importedPackages) { + public DeleteRedundantRequiresStatementFix(String requiredModuleName, Set importedPackages) { + myRequiredModuleName = requiredModuleName; myImportedPackages = importedPackages; } @@ -141,6 +138,13 @@ public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchIn return InspectionsBundle.message("inspection.redundant.requires.statement.fix.family"); } + @Nls + @NotNull + @Override + public String getName() { + return InspectionsBundle.message("inspection.redundant.requires.statement.fix.name", myRequiredModuleName); + } + @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement element = descriptor.getPsiElement(); @@ -209,55 +213,8 @@ public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchIn } } - private static class RedundantRequiresStatementVisitor extends RefJavaVisitor { - private final InspectionManager myManager; - private final ProblemDescriptionsProcessor myProblemDescriptionsProcessor; - - public RedundantRequiresStatementVisitor(InspectionManager manager, ProblemDescriptionsProcessor problemDescriptionsProcessor) { - myManager = manager; - myProblemDescriptionsProcessor = problemDescriptionsProcessor; - } - - @Override - public void visitJavaModule(@NotNull RefJavaModule refJavaModule) { - super.visitJavaModule(refJavaModule); - - RefModule refModule = refJavaModule.getModule(); - if (refModule != null) { - Set moduleImportedPackages = refModule.getUserData(IMPORTED_JAVA_PACKAGES); - if (moduleImportedPackages != null) { - Map requiredModuleNames = refJavaModule.getRequiredModuleNames(); - if (!requiredModuleNames.isEmpty()) { - Map>> exportedModulePackages = getExportedModulePackages(refJavaModule.getRefManager()); - for (String dependencyModuleName : requiredModuleNames.keySet()) { - Map> exportedPackages = exportedModulePackages.get(dependencyModuleName); - if (exportedPackages != null && isDependencyUnused(exportedPackages, moduleImportedPackages, refJavaModule.getName())) { - PsiRequiresStatement statement = refJavaModule.getRequiresStatements().get(dependencyModuleName); - if (statement != null) { - registerProblem(refJavaModule, statement, moduleImportedPackages, dependencyModuleName); - } - } - } - } - } - } - } - - private void registerProblem(@NotNull RefJavaModule refJavaModule, - @NotNull PsiRequiresStatement requiresStatement, - @NotNull Set importedPackages, - @NotNull String dependencyModuleName) { - ProblemDescriptor descriptor = - myManager.createProblemDescriptor(requiresStatement, - InspectionsBundle - .message("inspection.redundant.requires.statement.description", dependencyModuleName), - new DeleteRedundantRequiresStatementFix(importedPackages), - ProblemHighlightType.LIKE_UNUSED_SYMBOL, false); - myProblemDescriptionsProcessor.addProblemElement(refJavaModule, descriptor); - } - } - private static class RedundantRequiresStatementAnnotator extends RefGraphAnnotator { + private static final Set DONT_COLLECT_PACKAGES = Collections.emptySet(); @Override public void onReferencesBuild(RefElement refElement) { @@ -269,12 +226,9 @@ public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchIn } } else if (refElement instanceof RefJavaModule) { - PsiJavaModule javaModule = ((RefJavaModule)refElement).getElement(); - if (javaModule != null) { - RefModule refModule = refElement.getModule(); - if (refModule != null) { - onJavaModuleReferencesBuilt(javaModule, refModule); - } + RefModule refModule = refElement.getModule(); + if (refModule != null) { + setImportedPackages(refModule, refElement.getElement() != null); } } } @@ -283,64 +237,54 @@ public class Java9RedundantRequiresStatementInspection extends GlobalJavaBatchIn if (file.getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_9)) { PsiImportList importList = file.getImportList(); if (importList != null) { - Set packageNames = new THashSet<>(); - PsiImportStatementBase[] statements = importList.getAllImportStatements(); - if (statements.length != 0) { - for (PsiImportStatementBase statement : statements) { - PsiElement resolved = statement.resolve(); - String packageName = null; - if (resolved instanceof PsiPackage) { - packageName = ((PsiPackage)resolved).getQualifiedName(); - } - else if (resolved instanceof PsiMember) { - PsiJavaFile parentFile = PsiTreeUtil.getParentOfType(resolved, PsiJavaFile.class); - if (parentFile != null) { - packageName = parentFile.getPackageName(); + RefModule refModule = refFile.getModule(); + if (refModule != null) { + Set packageNames = getImportedPackages(refModule); + if (packageNames != DONT_COLLECT_PACKAGES) { + PsiImportStatementBase[] statements = importList.getAllImportStatements(); + for (PsiImportStatementBase statement : statements) { + String packageName = getPackageName(statement); + if (!StringUtil.isEmpty(packageName)) { + packageNames.add(packageName); } } - if (!StringUtil.isEmpty(packageName)) { - packageNames.add(packageName); - } - if (!packageNames.isEmpty()) { - refFile.putUserData(IMPORTED_JAVA_PACKAGES, packageNames); - } } } } } } - private static void onJavaModuleReferencesBuilt(@NotNull PsiJavaModule javaModule, @NotNull RefModule refModule) { - LOG.assertTrue(refModule.getUserData(IMPORTED_JAVA_PACKAGES) == null, "Duplicate Java module declaration"); - refModule.putUserData(IMPORTED_JAVA_PACKAGES, new THashSet<>()); - - Map>> exportedModulePackages = getExportedModulePackages(refModule.getRefManager()); - for (PsiRequiresStatement statement : javaModule.getRequires()) { - String dependencyModuleName = statement.getModuleName(); - if (dependencyModuleName != null) { - Map> exportedPackages = exportedModulePackages.get(dependencyModuleName); - if (exportedPackages == null) { - PsiJavaModule dependency = resolveRequiredModule(statement); - exportedPackages = getExportedPackages(dependency); - exportedModulePackages.put(dependencyModuleName, exportedPackages); - } + @Nullable + private static String getPackageName(@NotNull PsiImportStatementBase statement) { + PsiElement resolved = statement.resolve(); + if (resolved instanceof PsiPackage) { + return ((PsiPackage)resolved).getQualifiedName(); + } + else if (resolved instanceof PsiMember) { + PsiJavaFile parentFile = PsiTreeUtil.getParentOfType(resolved, PsiJavaFile.class); + if (parentFile != null) { + return parentFile.getPackageName(); } } + return null; } @NotNull - private static Map> getExportedPackages(@Nullable PsiJavaModule javaModule) { - if (javaModule == null) { - return Collections.emptyMap(); + private static Set getImportedPackages(@NotNull RefModule refModule) { + Set importedPackages = refModule.getUserData(IMPORTED_JAVA_PACKAGES); + if (importedPackages == null) { + PsiJavaModule javaModule = JavaModuleGraphUtil.findDescriptorByModule(refModule.getModule()); + importedPackages = javaModule != null ? new THashSet<>() : DONT_COLLECT_PACKAGES; + refModule.putUserData(IMPORTED_JAVA_PACKAGES, importedPackages); } - Map> exportedPackages = new THashMap<>(); - for (PsiExportsStatement statement : javaModule.getExports()) { - String packageName = statement.getPackageName(); - if (packageName != null) { - exportedPackages.put(packageName, statement.getModuleNames()); - } + return importedPackages; + } + + private static void setImportedPackages(RefModule refModule, boolean collectPackages) { + Set importedPackages = refModule.getUserData(IMPORTED_JAVA_PACKAGES); + if (importedPackages == null) { + refModule.putUserData(IMPORTED_JAVA_PACKAGES, collectPackages ? new THashSet<>() : DONT_COLLECT_PACKAGES); } - return !exportedPackages.isEmpty() ? exportedPackages : Collections.emptyMap(); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaModuleImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaModuleImpl.java index 0b2e658e3ad5..f88fb3cc08e7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaModuleImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaModuleImpl.java @@ -34,9 +34,7 @@ public class RefJavaModuleImpl extends RefElementImpl implements RefJavaModule { private final RefModule myRefModule; private Map> myExportedPackageNames; - private Map myRequiredModuleNames; - - private Map myRequiresStatements; + private Map myRequiredModules; public RefJavaModuleImpl(@NotNull PsiJavaModule javaModule, @NotNull RefManagerImpl manager) { super(javaModule.getModuleName(), javaModule, manager); @@ -77,14 +75,8 @@ public class RefJavaModuleImpl extends RefElementImpl implements RefJavaModule { } @NotNull - @Override - public Map getRequiredModuleNames() { - return myRequiredModuleNames != null ? myRequiredModuleNames : Collections.emptyMap(); - } - - @NotNull - public Map getRequiresStatements() { - return myRequiresStatements != null ? myRequiresStatements : Collections.emptyMap(); + public Map getRequiredModules() { + return myRequiredModules != null ? myRequiredModules : Collections.emptyMap(); } @Override @@ -94,31 +86,27 @@ public class RefJavaModuleImpl extends RefElementImpl implements RefJavaModule { for (PsiRequiresStatement statement : javaModule.getRequires()) { PsiJavaModuleReferenceElement referenceElement = statement.getReferenceElement(); if (referenceElement != null) { - if (myRequiresStatements == null) myRequiresStatements = new THashMap<>(1); - myRequiresStatements.put(referenceElement.getReferenceText(), statement); - - PsiPolyVariantReference moduleReference = referenceElement.getReference(); - PsiElement element = addReference(moduleReference); + PsiElement element = addReference(referenceElement.getReference()); if (element instanceof PsiJavaModule) { - if (myRequiredModuleNames == null) myRequiredModuleNames = new THashMap<>(1); - myRequiredModuleNames.put(((PsiJavaModule)element).getModuleName(), statement.isPublic()); + PsiJavaModule requiredModule = (PsiJavaModule)element; + Map> packageNames = getExportedPackagesSnapshot(requiredModule); + if (myRequiredModules == null) myRequiredModules = new THashMap<>(1); + myRequiredModules.put(requiredModule.getModuleName(), new Dependency(packageNames, statement.isPublic())); } } } List emptyList = Collections.emptyList(); for (PsiExportsStatement statement : javaModule.getExports()) { - PsiJavaCodeReferenceElement packageReference = statement.getPackageReference(); - PsiElement element = addReference(packageReference); + PsiElement element = addReference(statement.getPackageReference()); String packageName = null; if (element instanceof PsiPackage) { - if (myExportedPackageNames == null) myExportedPackageNames = new THashMap<>(1); packageName = ((PsiPackage)element).getQualifiedName(); + if (myExportedPackageNames == null) myExportedPackageNames = new THashMap<>(1); myExportedPackageNames.put(packageName, emptyList); } for (PsiJavaModuleReferenceElement referenceElement : statement.getModuleReferences()) { if (referenceElement != null) { - PsiPolyVariantReference moduleReference = referenceElement.getReference(); - PsiElement moduleElement = addReference(moduleReference); + PsiElement moduleElement = addReference(referenceElement.getReference()); if (packageName != null && moduleElement instanceof PsiJavaModule) { List toModuleNames = myExportedPackageNames.get(packageName); if (toModuleNames == emptyList) myExportedPackageNames.put(packageName, toModuleNames = new ArrayList<>(1)); @@ -150,4 +138,16 @@ public class RefJavaModuleImpl extends RefElementImpl implements RefJavaModule { } return resolvedElements.size() == 1 ? resolvedElements.get(0) : null; } + + @NotNull + private static Map> getExportedPackagesSnapshot(@NotNull PsiJavaModule javaModule) { + Map> exportedPackages = new THashMap<>(); + for (PsiExportsStatement statement : javaModule.getExports()) { + String packageName = statement.getPackageName(); + if (packageName != null) { + exportedPackages.put(packageName, statement.getModuleNames()); + } + } + return !exportedPackages.isEmpty() ? exportedPackages : Collections.emptyMap(); + } } diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index aa4bfc4fc830..e42ad9b55fac 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -471,6 +471,7 @@ inspection.non.accessible.type.exposed.name=The class is not exported from the m inspection.redundant.requires.statement.name=Redundant 'requires' statement in module-info inspection.redundant.requires.statement.description=Redundant statement ''requires {0}'' inspection.redundant.requires.statement.fix.family=Delete redundant 'requires' statement +inspection.redundant.requires.statement.fix.name=Delete statement ''requires {0}'' inspection.root.node.title=Inspections