From 291c3cfbd97260cbe57418edc717ecb98002d6ab Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 10 Feb 2025 15:25:02 +0100 Subject: [PATCH] [java-modules] More code moved to JavaPsiModuleUtil from JavaModuleGraphUtil GitOrigin-RevId: 0a0e7d4bc713deb3d1d55857873c1f708da7a2af --- .../core/intellij.java.codeserver.core.iml | 2 + .../resources/META-INF/JavaCodeServerCore.xml | 6 + .../codeserver/core/JavaPsiModuleUtil.java | 254 +++++++++++++++++- .../impl/intellij.java.execution.impl.iml | 1 + .../execution/util/JavaParametersUtil.java | 6 +- .../analysis/JavaModuleGraphHelperImpl.java | 25 -- .../impl/analysis/JavaModuleGraphUtil.java | 199 +------------- .../impl/analysis/ModuleHighlightUtil.java | 8 +- ...a9ReflectionClassVisibilityInspection.java | 3 +- .../resources/META-INF/JavaPlugin.xml | 3 +- .../completion/JavaCompletionUtil.java | 5 +- .../psi/impl/JavaPlatformModuleSystem.kt | 7 +- .../proximity/ExplicitlyImportedWeigher.java | 3 +- plugins/kotlin/core/kotlin.core.iml | 1 + .../idea/modules/IdeJavaModuleResolver.kt | 3 +- 15 files changed, 279 insertions(+), 247 deletions(-) create mode 100644 java/codeserver/core/resources/META-INF/JavaCodeServerCore.xml delete mode 100644 java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphHelperImpl.java diff --git a/java/codeserver/core/intellij.java.codeserver.core.iml b/java/codeserver/core/intellij.java.codeserver.core.iml index ddd48c7f6b9f..d0873c34122e 100644 --- a/java/codeserver/core/intellij.java.codeserver.core.iml +++ b/java/codeserver/core/intellij.java.codeserver.core.iml @@ -3,6 +3,7 @@ + @@ -17,5 +18,6 @@ + \ No newline at end of file diff --git a/java/codeserver/core/resources/META-INF/JavaCodeServerCore.xml b/java/codeserver/core/resources/META-INF/JavaCodeServerCore.xml new file mode 100644 index 000000000000..ec47d70f0457 --- /dev/null +++ b/java/codeserver/core/resources/META-INF/JavaCodeServerCore.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiModuleUtil.java b/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiModuleUtil.java index bd6a4d00dea4..b1852a3ce926 100644 --- a/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiModuleUtil.java +++ b/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiModuleUtil.java @@ -18,22 +18,27 @@ import com.intellij.psi.impl.light.LightJavaModule; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.search.searches.JavaModuleSearch; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.JavaMultiReleaseUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.graph.Graph; +import com.intellij.util.graph.GraphGenerator; +import com.intellij.util.indexing.DumbModeAccessType; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaResourceRootType; import org.jetbrains.jps.model.java.JavaSourceRootType; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.function.BiFunction; import java.util.jar.JarFile; +import static com.intellij.psi.PsiJavaModule.JAVA_BASE; + /** * Utilities related to JPMS modules */ @@ -212,4 +217,245 @@ public final class JavaPsiModuleUtil { return null; } + + /** + * @param source source module + * @param destination target module + * @return true if the source module reads the target module + */ + public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) { + return getRequiresGraph(source).reads(source, destination); + } + + /** + * @param source source module + * @return set of all direct (non-transitive) dependencies of the source module + */ + public static @NotNull Set getAllDependencies(@NotNull PsiJavaModule source) { + return getRequiresGraph(source).getAllDependencies(source, false); + } + + /** + * @param source source module + * @return set of all direct and indirect (transitive) dependencies of the source module + */ + public static @NotNull Set getAllTransitiveDependencies(@NotNull PsiJavaModule source) { + return getRequiresGraph(source).getAllDependencies(source, true); + } + + /** + * Represents a dependency conflict when a single package is imported from two modules + * @param packageName package name + * @param module1 first module from which this package is being read + * @param module2 second module from which this package is being read + */ + public record ModulePackageConflict(@NotNull String packageName, @NotNull PsiJavaModule module1, @NotNull PsiJavaModule module2) { } + + /** + * @param module module to check + * @return a {@link ModulePackageConflict} representing a package name conflict among module dependencies; null if no conflict found. + * If there are several conflicts, only the first one is returned. + */ + public static @Nullable ModulePackageConflict findConflict(@NotNull PsiJavaModule module) { + return getRequiresGraph(module).findConflict(module); + } + + /** + * @param module module that accesses a package + * @param packageName the name of the accessed package + * @return the module where the package is declared; null if not found + */ + public static @Nullable PsiJavaModule findOrigin(@NotNull PsiJavaModule module, @NotNull String packageName) { + return getRequiresGraph(module).findOrigin(module, packageName); + } + + private static RequiresGraph getRequiresGraph(@NotNull PsiJavaModule module) { + final Project project = module.getProject(); + if (DumbService.getInstance(project).isAlternativeResolveEnabled()) { + return DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode(() -> buildRequiresGraph(project)); + } + return CachedValuesManager.getManager(project).getCachedValue(project, () -> + CachedValueProvider.Result.create(buildRequiresGraph(project), + PsiJavaModuleModificationTracker.getInstance(project), + ProjectRootModificationTracker.getInstance(project))); + } + + /* + * Collects all module dependencies in the project. + * The resulting graph is used for tracing readability and checking package conflicts. + */ + private static @NotNull RequiresGraph buildRequiresGraph(@NotNull Project project) { + MultiMap relations = MultiMap.create(); + Set transitiveEdges = new HashSet<>(); + + GlobalSearchScope scope = ProjectScope.getAllScope(project); + JavaModuleSearch.allModules(project, scope).forEach(module -> { + visit(module, relations, transitiveEdges); + return true; + }); + + Graph graph = GraphGenerator.generate(new ChameleonGraph<>(relations, true)); + return new RequiresGraph(graph, transitiveEdges); + } + + private static void visit(@NotNull PsiJavaModule module, @NotNull MultiMap relations, @NotNull Set transitiveEdges) { + if (!(module instanceof LightJavaModule) && !relations.containsKey(module)) { + relations.putValues(module, Collections.emptyList()); + boolean explicitJavaBase = false; + for (PsiRequiresStatement statement : module.getRequires()) { + PsiJavaModuleReference ref = statement.getModuleReference(); + if (ref != null) { + if (JAVA_BASE.equals(ref.getCanonicalText())) explicitJavaBase = true; + for (ResolveResult result : ref.multiResolve(false)) { + PsiJavaModule dependency = (PsiJavaModule)result.getElement(); + assert dependency != null : result; + relations.putValue(module, dependency); + if (statement.hasModifierProperty(PsiModifier.TRANSITIVE)) transitiveEdges.add(RequiresGraph.key(dependency, module)); + visit(dependency, relations, transitiveEdges); + } + } + } + if (!explicitJavaBase) { + PsiJavaModule javaBase = JavaPsiFacade.getInstance(module.getProject()).findModule(JAVA_BASE, module.getResolveScope()); + if (javaBase != null) relations.putValue(module, javaBase); + } + } + } + + private static final class ChameleonGraph implements Graph { + private final Set myNodes; + private final MultiMap myEdges; + private final boolean myInbound; + + private ChameleonGraph(MultiMap edges, boolean inbound) { + myNodes = new HashSet<>(); + edges.entrySet().forEach(e -> { + myNodes.add(e.getKey()); + myNodes.addAll(e.getValue()); + }); + myEdges = edges; + myInbound = inbound; + } + + @Override + public @NotNull Collection getNodes() { + return myNodes; + } + + @Override + public @NotNull Iterator getIn(N n) { + return myInbound ? myEdges.get(n).iterator() : Collections.emptyIterator(); + } + + @Override + public @NotNull Iterator getOut(N n) { + return myInbound ? Collections.emptyIterator() : myEdges.get(n).iterator(); + } + } + + private static final class RequiresGraph { + private final @NotNull Graph myGraph; + private final @NotNull Set myTransitiveEdges; + + private RequiresGraph(@NotNull Graph graph, @NotNull Set transitiveEdges) { + myGraph = graph; + myTransitiveEdges = transitiveEdges; + } + + public boolean reads(PsiJavaModule source, PsiJavaModule destination) { + Collection nodes = myGraph.getNodes(); + if (nodes.contains(destination) && nodes.contains(source)) { + Iterator directReaders = myGraph.getOut(destination); + while (directReaders.hasNext()) { + PsiJavaModule next = directReaders.next(); + if (source.equals(next) || myTransitiveEdges.contains(key(destination, next)) && reads(source, next)) { + return true; + } + } + } + return false; + } + + private @Nullable ModulePackageConflict findConflict(@NotNull PsiJavaModule source) { + Map exports = new HashMap<>(); + return processExports(source, (pkg, m) -> { + PsiJavaModule found = exports.put(pkg, m); + return found == null || + found instanceof LightJavaModule && m instanceof LightJavaModule || + found.getName().equals(m.getName()) + ? null : new ModulePackageConflict(pkg, found, m); + }); + } + + private @Nullable PsiJavaModule findOrigin(@NotNull PsiJavaModule module, @NotNull String packageName) { + return processExports(module, (pkg, m) -> packageName.equals(pkg) ? m : null); + } + + private @Nullable T processExports(@NotNull PsiJavaModule start, @NotNull BiFunction processor) { + return myGraph.getNodes().contains(start) ? processExports(start.getName(), start, true, new HashSet<>(), processor) : null; + } + + private @Nullable T processExports(@Nullable String name, + @NotNull PsiJavaModule module, + boolean direct, + @NotNull Set visited, + @NotNull BiFunction processor) { + if (visited.add(module)) { + if (!direct) { + for (PsiPackageAccessibilityStatement statement : module.getExports()) { + List exportTargets = statement.getModuleNames(); + if (exportTargets.isEmpty() || exportTargets.contains(name)) { + T result = processor.apply(statement.getPackageName(), module); + if (result != null) return result; + } + } + } + for (Iterator iterator = myGraph.getIn(module); iterator.hasNext();) { + PsiJavaModule dependency = iterator.next(); + if (direct || myTransitiveEdges.contains(key(dependency, module))) { + T result = processExports(name, dependency, false, visited, processor); + if (result != null) return result; + } + } + } + + return null; + } + + public static @NotNull String key(@NotNull PsiJavaModule module, @NotNull PsiJavaModule exporter) { + return module.getName() + '/' + exporter.getName(); + } + + public @NotNull Set getAllDependencies(@NotNull PsiJavaModule module, boolean transitive) { + Set requires = new HashSet<>(); + collectDependencies(module, requires, transitive); + return requires; + } + + private void collectDependencies(@NotNull PsiJavaModule module, @NotNull Set dependencies, boolean transitive) { + for (Iterator iterator = myGraph.getIn(module); iterator.hasNext();) { + PsiJavaModule dependency = iterator.next(); + if (!dependencies.contains(dependency) && (!transitive || myTransitiveEdges.contains(key(dependency, module)))) { + dependencies.add(dependency); + collectDependencies(dependency, dependencies, transitive); + } + } + } + } + + /** + * Helper service to support resolve in java-psi-impl + */ + public static class Helper extends JavaModuleGraphHelper { + @Contract("null->null") + @Override + public @Nullable PsiJavaModule findDescriptorByElement(@Nullable PsiElement element) { + return JavaPsiModuleUtil.findDescriptorByElement(element); + } + + @Override + public @NotNull Set getAllTransitiveDependencies(@NotNull PsiJavaModule psiJavaModule) { + return JavaPsiModuleUtil.getAllTransitiveDependencies(psiJavaModule); + } + } } diff --git a/java/execution/impl/intellij.java.execution.impl.iml b/java/execution/impl/intellij.java.execution.impl.iml index 8b54aa8c3482..7ae1cc54ce79 100644 --- a/java/execution/impl/intellij.java.execution.impl.iml +++ b/java/execution/impl/intellij.java.execution.impl.iml @@ -53,6 +53,7 @@ + diff --git a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java index 9885ff8cee12..a00fadca640b 100644 --- a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java +++ b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java @@ -1,12 +1,12 @@ // 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.execution.util; -import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil; import com.intellij.execution.CantRunException; import com.intellij.execution.CommonJavaRunConfigurationParameters; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.JavaExecutionUtil; import com.intellij.execution.configurations.*; +import com.intellij.java.codeserver.core.JavaPsiModuleUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; @@ -225,7 +225,7 @@ public final class JavaParametersUtil { Set forModulePath = new HashSet<>(explicitModules); for (PsiJavaModule explicitModule : explicitModules) { - forModulePath.addAll(JavaModuleGraphUtil.getAllDependencies(explicitModule)); + forModulePath.addAll(JavaPsiModuleUtil.getAllDependencies(explicitModule)); } if (!includeTests) { @@ -296,7 +296,7 @@ public final class JavaParametersUtil { PsiClassType provideInterfaceType = provide.getInterfaceType(); if (provideInterfaceType != null && interfaces.contains(provideInterfaceType.getCanonicalText())) { registerProviders.accept(aModule); - JavaModuleGraphUtil.getAllDependencies(aModule).forEach(registerProviders); + JavaPsiModuleUtil.getAllDependencies(aModule).forEach(registerProviders); continue nextModule; } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphHelperImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphHelperImpl.java deleted file mode 100644 index 10001182bf70..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphHelperImpl.java +++ /dev/null @@ -1,25 +0,0 @@ -// 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.codeInsight.daemon.impl.analysis; - -import com.intellij.java.codeserver.core.JavaPsiModuleUtil; -import com.intellij.psi.JavaModuleGraphHelper; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiJavaModule; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Set; - -public class JavaModuleGraphHelperImpl extends JavaModuleGraphHelper { - @Contract("null->null") - @Override - public @Nullable PsiJavaModule findDescriptorByElement(@Nullable PsiElement element) { - return JavaPsiModuleUtil.findDescriptorByElement(element); - } - - @Override - public @NotNull Set getAllTransitiveDependencies(@NotNull PsiJavaModule psiJavaModule) { - return JavaModuleGraphUtil.getAllTransitiveDependencies(psiJavaModule); - } -} \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java index 2819199f8da6..38f4fd48242e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java @@ -13,11 +13,9 @@ import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.util.Trinity; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; @@ -25,8 +23,6 @@ import com.intellij.psi.impl.PsiJavaModuleModificationTracker; import com.intellij.psi.impl.light.LightJavaModule; import com.intellij.psi.impl.source.resolve.JavaResolveUtil; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.ProjectScope; -import com.intellij.psi.search.searches.JavaModuleSearch; import com.intellij.psi.util.CachedValueProvider.Result; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiUtil; @@ -36,15 +32,12 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.graph.DFSTBuilder; import com.intellij.util.graph.Graph; -import com.intellij.util.graph.GraphGenerator; -import com.intellij.util.indexing.DumbModeAccessType; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import java.util.*; -import java.util.function.BiFunction; import static com.intellij.openapi.roots.DependencyScope.PROVIDED; import static com.intellij.psi.PsiJavaModule.JAVA_BASE; @@ -95,26 +88,6 @@ public final class JavaModuleGraphUtil { return targets != null && (targets.isEmpty() || target != null && targets.contains(target.getName())); } - public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) { - return getRequiresGraph(source).reads(source, destination); - } - - public static @NotNull Set getAllDependencies(PsiJavaModule source) { - return getRequiresGraph(source).getAllDependencies(source, false); - } - - public static @NotNull Set getAllTransitiveDependencies(PsiJavaModule source) { - return getRequiresGraph(source).getAllDependencies(source, true); - } - - public static @Nullable Trinity findConflict(@NotNull PsiJavaModule module) { - return getRequiresGraph(module).findConflict(module); - } - - public static @Nullable PsiJavaModule findOrigin(@NotNull PsiJavaModule module, @NotNull String packageName) { - return getRequiresGraph(module).findOrigin(module, packageName); - } - /** * Determines if a specified module is readable from a given context * @@ -179,7 +152,7 @@ public final class JavaModuleGraphUtil { if (from == to) return false; if (!PsiNameHelper.isValidModuleName(to.getName(), to)) return false; if (contains(from.getRequires(), to.getName())) return false; - if (reads(from, to)) return false; + if (JavaPsiModuleUtil.reads(from, to)) return false; PsiUtil.addModuleStatement(from, PsiKeyword.REQUIRES + " " + (isStaticModule(to.getName(), scope) ? PsiKeyword.STATIC + " " : "") + (isExported(from, to) ? PsiKeyword.TRANSITIVE + " " : "") + @@ -194,33 +167,6 @@ public final class JavaModuleGraphUtil { return false; } - /** - * Optimizes the dependencies of a current module file by removing redundant 'requires' statements - * that contain (transitive) in the selected dependency. - * - * @param currentModule The Java module for which to optimize the dependencies. - * @param selectedDependency The Java module that is the selected dependency. - */ - public static void optimizeDependencies(@NotNull PsiJavaModule currentModule, @NotNull PsiJavaModule selectedDependency) { - Map requires = new HashMap<>(); - for (PsiRequiresStatement require : currentModule.getRequires()) { - PsiJavaModule resolvedModule = require.resolve(); - if (resolvedModule != null) { - requires.put(resolvedModule, require); - } - } - - Set redundant = new HashSet<>(); - for (PsiJavaModule module : requires.keySet()) { - if (module.getName().equals(selectedDependency.getName())) continue; - if (reads(selectedDependency, module)) redundant.add(module); - } - - for (PsiJavaModule module : redundant) { - requires.get(module).delete(); - } - } - private static boolean isExported(@NotNull PsiJavaModule from, @NotNull PsiJavaModule to) { VirtualFile toFile = getVirtualFile(to); if (toFile == null) return false; @@ -326,149 +272,6 @@ public final class JavaModuleGraphUtil { return map; } - private static RequiresGraph getRequiresGraph(@NotNull PsiJavaModule module) { - final Project project = module.getProject(); - if (DumbService.getInstance(project).isAlternativeResolveEnabled()) { - return DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode(() -> buildRequiresGraph(project)); - } - return CachedValuesManager.getManager(project).getCachedValue(project, () -> - Result.create(buildRequiresGraph(project), - PsiJavaModuleModificationTracker.getInstance(project), - ProjectRootModificationTracker.getInstance(project))); - } - - /* - * Collects all module dependencies in the project. - * The resulting graph is used for tracing readability and checking package conflicts. - */ - private static @NotNull RequiresGraph buildRequiresGraph(@NotNull Project project) { - MultiMap relations = MultiMap.create(); - Set transitiveEdges = new HashSet<>(); - - GlobalSearchScope scope = ProjectScope.getAllScope(project); - JavaModuleSearch.allModules(project, scope).forEach(module -> { - visit(module, relations, transitiveEdges); - return true; - }); - - Graph graph = GraphGenerator.generate(new ChameleonGraph<>(relations, true)); - return new RequiresGraph(graph, transitiveEdges); - } - - private static void visit(@NotNull PsiJavaModule module, @NotNull MultiMap relations, @NotNull Set transitiveEdges) { - if (!(module instanceof LightJavaModule) && !relations.containsKey(module)) { - relations.putValues(module, Collections.emptyList()); - boolean explicitJavaBase = false; - for (PsiRequiresStatement statement : module.getRequires()) { - PsiJavaModuleReference ref = statement.getModuleReference(); - if (ref != null) { - if (JAVA_BASE.equals(ref.getCanonicalText())) explicitJavaBase = true; - for (ResolveResult result : ref.multiResolve(false)) { - PsiJavaModule dependency = (PsiJavaModule)result.getElement(); - assert dependency != null : result; - relations.putValue(module, dependency); - if (statement.hasModifierProperty(PsiModifier.TRANSITIVE)) transitiveEdges.add(RequiresGraph.key(dependency, module)); - visit(dependency, relations, transitiveEdges); - } - } - } - if (!explicitJavaBase) { - PsiJavaModule javaBase = JavaPsiFacade.getInstance(module.getProject()).findModule(JAVA_BASE, module.getResolveScope()); - if (javaBase != null) relations.putValue(module, javaBase); - } - } - } - - private static final class RequiresGraph { - private final @NotNull Graph myGraph; - private final @NotNull Set myTransitiveEdges; - - private RequiresGraph(@NotNull Graph graph, @NotNull Set transitiveEdges) { - myGraph = graph; - myTransitiveEdges = transitiveEdges; - } - - public boolean reads(PsiJavaModule source, PsiJavaModule destination) { - Collection nodes = myGraph.getNodes(); - if (nodes.contains(destination) && nodes.contains(source)) { - Iterator directReaders = myGraph.getOut(destination); - while (directReaders.hasNext()) { - PsiJavaModule next = directReaders.next(); - if (source.equals(next) || myTransitiveEdges.contains(key(destination, next)) && reads(source, next)) { - return true; - } - } - } - return false; - } - - public @Nullable Trinity findConflict(@NotNull PsiJavaModule source) { - Map exports = new HashMap<>(); - return processExports(source, (pkg, m) -> { - PsiJavaModule found = exports.put(pkg, m); - return found == null || - found instanceof LightJavaModule && m instanceof LightJavaModule || - found.getName().equals(m.getName()) - ? null : new Trinity<>(pkg, found, m); - }); - } - - public @Nullable PsiJavaModule findOrigin(@NotNull PsiJavaModule module, @NotNull String packageName) { - return processExports(module, (pkg, m) -> packageName.equals(pkg) ? m : null); - } - - private @Nullable T processExports(@NotNull PsiJavaModule start, @NotNull BiFunction processor) { - return myGraph.getNodes().contains(start) ? processExports(start.getName(), start, true, new HashSet<>(), processor) : null; - } - - private @Nullable T processExports(@Nullable String name, - @NotNull PsiJavaModule module, - boolean direct, - @NotNull Set visited, - @NotNull BiFunction processor) { - if (visited.add(module)) { - if (!direct) { - for (PsiPackageAccessibilityStatement statement : module.getExports()) { - List exportTargets = statement.getModuleNames(); - if (exportTargets.isEmpty() || exportTargets.contains(name)) { - T result = processor.apply(statement.getPackageName(), module); - if (result != null) return result; - } - } - } - for (Iterator iterator = myGraph.getIn(module); iterator.hasNext();) { - PsiJavaModule dependency = iterator.next(); - if (direct || myTransitiveEdges.contains(key(dependency, module))) { - T result = processExports(name, dependency, false, visited, processor); - if (result != null) return result; - } - } - } - - return null; - } - - public static @NotNull String key(@NotNull PsiJavaModule module, @NotNull PsiJavaModule exporter) { - return module.getName() + '/' + exporter.getName(); - } - - public @NotNull Set getAllDependencies(@NotNull PsiJavaModule module, boolean transitive) { - Set requires = new HashSet<>(); - collectDependencies(module, requires, transitive); - return requires; - } - - private void collectDependencies(@NotNull PsiJavaModule module, @NotNull Set dependencies, boolean transitive) { - for (Iterator iterator = myGraph.getIn(module); iterator.hasNext();) { - PsiJavaModule dependency = iterator.next(); - if (!dependencies.contains(dependency) && (!transitive || myTransitiveEdges.contains(key(dependency, module)))) { - dependencies.add(dependency); - collectDependencies(dependency, dependencies, transitive); - } - } - } - } - private static final class ChameleonGraph implements Graph { private final Set myNodes; private final MultiMap myEdges; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java index 8fefeef8fbc2..7476a0d5c281 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java @@ -11,6 +11,7 @@ import com.intellij.codeInsight.daemon.impl.quickfix.*; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.java.codeserver.core.JavaPsiModuleUtil; +import com.intellij.java.codeserver.core.JavaPsiModuleUtil.ModulePackageConflict; import com.intellij.modcommand.ModCommandAction; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; @@ -19,7 +20,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.Trinity; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.JavaFeature; @@ -60,7 +60,7 @@ final class ModuleHighlightUtil { if (javaModule != null) { String packageName = statement.getPackageName(); if (packageName != null) { - PsiJavaModule origin = JavaModuleGraphUtil.findOrigin(javaModule, packageName); + PsiJavaModule origin = JavaPsiModuleUtil.findOrigin(javaModule, packageName); if (origin != null) { PsiJavaCodeReferenceElement reference = statement.getPackageReference(); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) @@ -485,10 +485,10 @@ final class ModuleHighlightUtil { } static HighlightInfo.Builder checkClashingReads(@NotNull PsiJavaModule module) { - Trinity conflict = JavaModuleGraphUtil.findConflict(module); + ModulePackageConflict conflict = JavaPsiModuleUtil.findConflict(module); if (conflict != null) { String message = JavaErrorBundle.message( - "module.conflicting.reads", module.getName(), conflict.first, conflict.second.getName(), conflict.third.getName()); + "module.conflicting.reads", module.getName(), conflict.packageName(), conflict.module1(), conflict.module2()); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(module)).descriptionAndTooltip(message); } diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/Java9ReflectionClassVisibilityInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/Java9ReflectionClassVisibilityInspection.java index fa21b74090b3..d3f11306c17f 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/Java9ReflectionClassVisibilityInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/Java9ReflectionClassVisibilityInspection.java @@ -1,7 +1,6 @@ // 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.reflectiveAccess; -import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil; import com.intellij.codeInsight.daemon.impl.quickfix.AddExportsDirectiveFix; import com.intellij.codeInsight.daemon.impl.quickfix.AddOpensDirectiveFix; import com.intellij.codeInsight.daemon.impl.quickfix.AddRequiresDirectiveFix; @@ -62,7 +61,7 @@ public final class Java9ReflectionClassVisibilityInspection extends AbstractBase if (psiClass != null) { final PsiJavaModule otherModule = JavaPsiModuleUtil.findDescriptorByElement(psiClass); if (otherModule != null && otherModule != javaModule) { - if (!JavaModuleGraphUtil.reads(javaModule, otherModule)) { + if (!JavaPsiModuleUtil.reads(javaModule, otherModule)) { String message = JavaBundle.message("module.not.in.requirements", javaModule.getName(), otherModule.getName()); holder.problem(classNameArgument, message).fix(new AddRequiresDirectiveFix(javaModule, otherModule.getName())).register(); return; diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml index e2a2914b1a4e..97bc7de232fb 100644 --- a/java/java-impl/resources/META-INF/JavaPlugin.xml +++ b/java/java-impl/resources/META-INF/JavaPlugin.xml @@ -11,6 +11,7 @@ + @@ -395,8 +396,6 @@ serviceImplementation="com.intellij.psi.impl.file.PsiPackageImplementationHelperImpl"/> - diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index e05263a702a9..2a85f890f2e4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -7,7 +7,6 @@ import com.intellij.codeInsight.completion.scope.CompletionElement; import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor; import com.intellij.codeInsight.completion.util.CompletionStyleUtil; import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; -import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil; import com.intellij.codeInsight.editorActions.TabOutScopesTracker; import com.intellij.codeInsight.guess.GuessManager; import com.intellij.codeInsight.lookup.*; @@ -520,11 +519,11 @@ public final class JavaCompletionUtil { } if (PsiUtil.isAvailable(JavaFeature.MODULES, myPlace)) { final PsiJavaModule currentModule = - ReadAction.compute(() -> JavaModuleGraphUtil.findDescriptorByFile(myOriginalFile, myPlace.getProject())); + ReadAction.compute(() -> JavaPsiModuleUtil.findDescriptorByFile(myOriginalFile, myPlace.getProject())); if (currentModule != null) { final PsiJavaModule targetModule = ReadAction.compute(() -> JavaPsiModuleUtil.findDescriptorByElement(psiClass)); if (targetModule != null && targetModule != currentModule && - !JavaModuleGraphUtil.reads(currentModule, targetModule)) { + !JavaPsiModuleUtil.reads(currentModule, targetModule)) { LookupElementDecorator element = generator.apply(presentation -> presentation.setItemTextForeground(JBColor.RED)); return PrioritizedLookupElement.withExplicitProximity(element, -1); } diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt b/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt index 836f5f82840e..a48eb4033da4 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt +++ b/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt @@ -12,6 +12,7 @@ import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.QuickFixFactory import com.intellij.codeInspection.util.IntentionName import com.intellij.java.JavaBundle +import com.intellij.java.codeserver.core.JavaPsiModuleUtil import com.intellij.java.codeserver.core.JavaPsiModuleUtil.findDescriptorByElement import com.intellij.modcommand.ActionContext import com.intellij.modcommand.ModCommand @@ -50,21 +51,21 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx { return getProblem(targetPackageName, targetFile, place, false) { (current, target) -> val currentModule = current.module ?: return@getProblem false val targetModule = target.module ?: return@getProblem false - return@getProblem JavaModuleGraphUtil.reads(currentModule, targetModule) + return@getProblem JavaPsiModuleUtil.reads(currentModule, targetModule) } } override fun isAccessible(targetModule: PsiJavaModule, place: PsiElement): Boolean { return getProblem(targetModule, place, true) { (current, target) -> if (current.module == null || target.module == null) return@getProblem false - return@getProblem JavaModuleGraphUtil.reads(current.module, target.module!!) + return@getProblem JavaPsiModuleUtil.reads(current.module, target.module!!) } == null } override fun checkAccess(targetModule: PsiJavaModule, place: PsiElement): ErrorWithFixes? { return getProblem(targetModule, place, false) { (current, target) -> if (current.module == null || target.module == null) return@getProblem false - return@getProblem JavaModuleGraphUtil.reads(current.module, target.module!!) + return@getProblem JavaPsiModuleUtil.reads(current.module, target.module!!) } } diff --git a/java/java-impl/src/com/intellij/psi/util/proximity/ExplicitlyImportedWeigher.java b/java/java-impl/src/com/intellij/psi/util/proximity/ExplicitlyImportedWeigher.java index 12a8c1002015..6c115d470f22 100644 --- a/java/java-impl/src/com/intellij/psi/util/proximity/ExplicitlyImportedWeigher.java +++ b/java/java-impl/src/com/intellij/psi/util/proximity/ExplicitlyImportedWeigher.java @@ -1,7 +1,6 @@ // 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.psi.util.proximity; -import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil; import com.intellij.java.codeserver.core.JavaPsiModuleUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; @@ -33,7 +32,7 @@ public final class ExplicitlyImportedWeigher extends ProximityWeigher { BiConsumer, PsiJavaModule> append = (list, module) -> { if (module != null) { list.add(module); - list.addAll(JavaModuleGraphUtil.getAllTransitiveDependencies(module)); + list.addAll(JavaPsiModuleUtil.getAllTransitiveDependencies(module)); } }; diff --git a/plugins/kotlin/core/kotlin.core.iml b/plugins/kotlin/core/kotlin.core.iml index f9055a0dd223..71cdffeb4151 100644 --- a/plugins/kotlin/core/kotlin.core.iml +++ b/plugins/kotlin/core/kotlin.core.iml @@ -61,5 +61,6 @@ + \ No newline at end of file diff --git a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt index 0c801fbc5a37..ea58ebf00ea7 100644 --- a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt +++ b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.idea.modules import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil +import com.intellij.java.codeserver.core.JavaPsiModuleUtil import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.vfs.VirtualFile @@ -64,7 +65,7 @@ class IdeJavaModuleResolver(private val project: Project) : JavaModuleResolver { return JavaModuleResolver.AccessError.ModuleDoesNotReadUnnamedModule } - if (ourModule != null && !JavaModuleGraphUtil.reads(ourModule, theirModule)) { + if (ourModule != null && !JavaPsiModuleUtil.reads(ourModule, theirModule)) { return JavaModuleResolver.AccessError.ModuleDoesNotReadModule(theirModule.name) }