mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 06:50:54 +07:00
[java, analysis, jigsaw] IDEA-381119 Remove module resolving from graph building to avoid recursive cycles
(cherry picked from commit fc672374ce0c379eb16d0c027132f262b6aed55f) (cherry picked from commit 577c84278c86e0147c62d6ebc2e2f26295f37c37) IJ-MR-182987 GitOrigin-RevId: 4bd27e20281444c10f0090c90f07e9bd20ed82e4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c82c8cb7d8
commit
87fbf8e408
@@ -23,6 +23,7 @@ import com.intellij.psi.search.ProjectScope;
|
||||
import com.intellij.psi.search.searches.JavaModuleSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.graph.DFSTBuilder;
|
||||
@@ -259,7 +260,8 @@ public final class JavaPsiModuleUtil {
|
||||
return requireNonNullElse(ContainerUtil.find(cycles, set -> set.contains(module)), Collections.emptyList());
|
||||
}
|
||||
|
||||
private static @Nullable VirtualFile getVirtualFile(@NotNull PsiJavaModule module) {
|
||||
private static @Nullable VirtualFile getVirtualFile(@Nullable PsiJavaModule module) {
|
||||
if (module == null) return null;
|
||||
if (module instanceof LightJavaModule light) {
|
||||
return light.getRootVirtualFile();
|
||||
}
|
||||
@@ -385,23 +387,19 @@ public final class JavaPsiModuleUtil {
|
||||
* The resulting graph is used for tracing readability and checking package conflicts.
|
||||
*/
|
||||
private static @NotNull RequiresGraph buildRequiresGraph(@NotNull Project project) {
|
||||
MultiMap<String, PsiJavaModule> allModules = MultiMap.create();
|
||||
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
|
||||
Set<String> transitiveEdges = new HashSet<>();
|
||||
|
||||
Queue<PsiJavaModule> queue = new ArrayDeque<>();
|
||||
GlobalSearchScope scope = ProjectScope.getAllScope(project);
|
||||
JavaModuleSearch.allModules(project, scope).forEach(module -> {
|
||||
queue.add(module);
|
||||
allModules.putValue(module.getName(), module);
|
||||
return true;
|
||||
});
|
||||
|
||||
Set<PsiJavaModule> visited = new HashSet<>();
|
||||
while (!queue.isEmpty()) {
|
||||
PsiJavaModule module = queue.poll();
|
||||
if (!(module instanceof LightJavaModule) && visited.add(module)) {
|
||||
Set<PsiJavaModule> shouldBeVisited = visit(module, relations, transitiveEdges);
|
||||
shouldBeVisited.removeAll(visited);
|
||||
queue.addAll(shouldBeVisited);
|
||||
for (PsiJavaModule module : allModules.values()) {
|
||||
if (!(module instanceof LightJavaModule)) {
|
||||
visit(module, relations, transitiveEdges, allModules);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,40 +412,50 @@ public final class JavaPsiModuleUtil {
|
||||
* the relations between modules, and the set of transitive edges based on the requires statements within
|
||||
* the module.
|
||||
*
|
||||
* @param module the module to be visited
|
||||
* @param relations a mapping that represents module dependencies
|
||||
* @param module the module to be visited
|
||||
* @param relations a mapping that represents module dependencies
|
||||
* @param transitiveEdges a set of transitive edges representing transitive dependencies
|
||||
* @return a set of modules that should be visited next
|
||||
* @param allModules a map of module names to PsiJavaModule instances
|
||||
*/
|
||||
private static @NotNull Set<PsiJavaModule> visit(@NotNull PsiJavaModule module,
|
||||
@NotNull MultiMap<PsiJavaModule, PsiJavaModule> relations,
|
||||
@NotNull Set<String> transitiveEdges) {
|
||||
Set<PsiJavaModule> shouldBeVisited = new HashSet<>();
|
||||
|
||||
private static void visit(@NotNull PsiJavaModule module,
|
||||
@NotNull MultiMap<PsiJavaModule, PsiJavaModule> relations,
|
||||
@NotNull Set<String> transitiveEdges,
|
||||
@NotNull MultiMap<String, PsiJavaModule> allModules) {
|
||||
relations.putValues(module, Collections.emptyList());
|
||||
boolean explicitJavaBase = false;
|
||||
|
||||
GlobalSearchScope scope = GlobalSearchScope.allScope(module.getProject());
|
||||
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(true)) {
|
||||
PsiJavaModule dependency = (PsiJavaModule)result.getElement();
|
||||
assert dependency != null : result;
|
||||
String moduleName = ref.getCanonicalText();
|
||||
if (JAVA_BASE.equals(moduleName)) explicitJavaBase = true;
|
||||
for (PsiJavaModule dependency : filterModules(allModules.get(moduleName), scope)) {
|
||||
relations.putValue(module, dependency);
|
||||
if (statement.hasModifierProperty(PsiModifier.TRANSITIVE)) {
|
||||
transitiveEdges.add(RequiresGraph.key(dependency, module));
|
||||
}
|
||||
shouldBeVisited.add(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!explicitJavaBase) {
|
||||
PsiJavaModule javaBase = JavaPsiFacade.getInstance(module.getProject()).findModule(JAVA_BASE, module.getResolveScope());
|
||||
if (javaBase != null) relations.putValue(module, javaBase);
|
||||
Collection<PsiJavaModule> modules = filterModules(allModules.get(JAVA_BASE), module.getResolveScope());
|
||||
if (modules.size() == 1) {
|
||||
relations.putValue(module, modules.iterator().next());
|
||||
}
|
||||
}
|
||||
return shouldBeVisited;
|
||||
}
|
||||
|
||||
private static @NotNull List<PsiJavaModule> filterModules(@NotNull Collection<PsiJavaModule> modules, @NotNull GlobalSearchScope scope) {
|
||||
SmartList<PsiJavaModule> filtered = new SmartList<>();
|
||||
for (PsiJavaModule candidate : modules) {
|
||||
VirtualFile candidateFile = getVirtualFile(candidate);
|
||||
if (candidateFile != null && scope.contains(candidateFile)) {
|
||||
filtered.add(candidate);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private static final class ChameleonGraph<N> implements Graph<N> {
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.impl.file.impl;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.ManifestUtil;
|
||||
import com.intellij.ide.highlighter.JavaClassFileType;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.PackageIndex;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Predicates;
|
||||
@@ -20,15 +21,10 @@ import com.intellij.psi.impl.PackagePrefixElementFinder;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.file.PsiPackageImpl;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaAutoModuleNameIndex;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaModuleNameIndex;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaSourceModuleNameIndex;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.search.searches.JavaModuleSearch;
|
||||
import com.intellij.psi.util.JavaMultiReleaseUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -36,7 +32,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@@ -75,7 +70,7 @@ public final class JavaFileManagerImpl implements JavaFileManager, Disposable {
|
||||
|
||||
int count = result.size();
|
||||
if (count == 0) return PsiClass.EMPTY_ARRAY;
|
||||
if (count == 1) return new PsiClass[] {result.get(0).getFirst()};
|
||||
if (count == 1) return new PsiClass[] {result.getFirst().getFirst()};
|
||||
|
||||
ContainerUtil.quickSort(result, (o1, o2) -> scope.compare(o2.getSecond(), o1.getSecond()));
|
||||
|
||||
@@ -159,51 +154,7 @@ public final class JavaFileManagerImpl implements JavaFileManager, Disposable {
|
||||
@Override
|
||||
public @NotNull Collection<PsiJavaModule> findModules(@NotNull String moduleName, @NotNull GlobalSearchScope scope) {
|
||||
GlobalSearchScope excludingScope = new LibSrcExcludingScope(scope);
|
||||
|
||||
Project project = myManager.getProject();
|
||||
List<PsiJavaModule> results = new ArrayList<>(JavaModuleNameIndex.getInstance().getModules(moduleName, project, excludingScope));
|
||||
|
||||
Set<VirtualFile> shadowedRoots = new HashSet<>();
|
||||
for (VirtualFile manifest : JavaSourceModuleNameIndex.getFilesByKey(moduleName, excludingScope)) {
|
||||
VirtualFile root = manifest.getParent().getParent();
|
||||
shadowedRoots.add(root);
|
||||
results.add(LightJavaModule.create(myManager, root, moduleName));
|
||||
}
|
||||
|
||||
for (VirtualFile root : JavaAutoModuleNameIndex.getFilesByKey(moduleName, excludingScope)) {
|
||||
if (shadowedRoots.contains(root)) { //already found by MANIFEST attribute
|
||||
continue;
|
||||
}
|
||||
VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME);
|
||||
if (manifest != null && LightJavaModule.claimedModuleName(manifest) != null) {
|
||||
continue;
|
||||
}
|
||||
results.add(LightJavaModule.create(myManager, root, moduleName));
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
CachedValuesManager valuesManager = CachedValuesManager.getManager(project);
|
||||
ProjectRootModificationTracker rootModificationTracker = ProjectRootModificationTracker.getInstance(project);
|
||||
for (Module module : ModuleManager.getInstance(project).getModules()) {
|
||||
VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(false);
|
||||
if (sourceRoots.length > 0) {
|
||||
String virtualAutoModuleName = ManifestUtil.lightManifestAttributeValue(module, PsiJavaModule.AUTO_MODULE_NAME);
|
||||
if (moduleName.equals(virtualAutoModuleName)) {
|
||||
results.add(LightJavaModule.create(myManager, sourceRoots[0], moduleName));
|
||||
break;
|
||||
}
|
||||
|
||||
String defaultModuleName = valuesManager.getCachedValue(module, () ->
|
||||
CachedValueProvider.Result.create(LightJavaModule.moduleName(module.getName()), rootModificationTracker)
|
||||
);
|
||||
if (moduleName.equals(defaultModuleName)) {
|
||||
results.add(LightJavaModule.create(myManager, sourceRoots[0], moduleName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<PsiJavaModule> results = JavaModuleSearch.search(moduleName, myManager.getProject(), excludingScope).findAll();
|
||||
return upgradeModules(sortModules(results, scope), moduleName, scope);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +1,178 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.impl.search;
|
||||
|
||||
import com.intellij.java.codeserver.core.JavaManifestUtil;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiJavaModule;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaAutoModuleNameIndex;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaModuleNameIndex;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaSourceModuleNameIndex;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.JavaModuleSearch;
|
||||
import com.intellij.psi.stubs.StubIndex;
|
||||
import com.intellij.util.CommonProcessors.CollectProcessor;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.QueryExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public final class JavaModuleSearcher implements QueryExecutor<PsiJavaModule, JavaModuleSearch.Parameters> {
|
||||
@Override
|
||||
public boolean execute(JavaModuleSearch.@NotNull Parameters queryParameters,
|
||||
@NotNull Processor<? super PsiJavaModule> consumer) {
|
||||
String name = queryParameters.getName();
|
||||
StubIndex index = StubIndex.getInstance();
|
||||
if (name == null) {
|
||||
//It is important to collect moduleNames first, then process the name -- don't do it recursively! -- it risks
|
||||
// a deadlock: processAllKeys() acquires readLock, but processElements() _could_ acquire writeLock (see
|
||||
// StubIndexEx.tryFixIndexesForProblemFiles())
|
||||
//In general: it is a bad idea to do recursive index lookups, i.e., another lookup from the lambda passed to something
|
||||
// like processAllKeys()/processElements(). Such lambdas should be a short & simple code, not complex deep-stack processing.
|
||||
// In a second case -- 'unfold' the recursive processing, as it is done here.
|
||||
CollectProcessor<String> moduleNamesCollector = new CollectProcessor<>();
|
||||
index.processAllKeys(JavaStubIndexKeys.MODULE_NAMES, moduleNamesCollector, queryParameters.getScope());
|
||||
for (String moduleName : moduleNamesCollector.getResults()) {
|
||||
boolean shouldContinue = index.processElements(
|
||||
JavaStubIndexKeys.MODULE_NAMES,
|
||||
moduleName,
|
||||
queryParameters.getProject(),
|
||||
queryParameters.getScope(),
|
||||
null,
|
||||
PsiJavaModule.class,
|
||||
consumer
|
||||
);
|
||||
if (!shouldContinue) {
|
||||
return false;
|
||||
}
|
||||
String moduleName = queryParameters.getName();
|
||||
Project project = queryParameters.getProject();
|
||||
GlobalSearchScope scope = queryParameters.getScope();
|
||||
|
||||
if (moduleName == null) {
|
||||
return processAllModules(project, consumer);
|
||||
}
|
||||
|
||||
return processModuleByName(moduleName, project, scope, consumer);
|
||||
}
|
||||
|
||||
private static boolean processAllModules(@NotNull Project project,
|
||||
@NotNull Processor<? super PsiJavaModule> consumer) {
|
||||
GlobalSearchScope indexScope = GlobalSearchScope.allScope(project);
|
||||
|
||||
// collect all module-name keys
|
||||
Set<String> allNames = new LinkedHashSet<>();
|
||||
allNames.addAll(JavaModuleNameIndex.getInstance().getAllKeys(project));
|
||||
allNames.addAll(JavaSourceModuleNameIndex.getAllKeys(project));
|
||||
allNames.addAll(JavaAutoModuleNameIndex.getAllKeys(project));
|
||||
|
||||
Set<String> namesWithResults = new HashSet<>();
|
||||
// process real and indexed light modules only.
|
||||
for (String name : allNames) {
|
||||
if (!processModulesFromIndices(name, project, indexScope, consumer, namesWithResults)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return processJpsModules(project, consumer, namesWithResults, null);
|
||||
}
|
||||
|
||||
private static boolean processModuleByName(@NotNull String moduleName,
|
||||
@NotNull Project project,
|
||||
@NotNull GlobalSearchScope scope,
|
||||
@NotNull Processor<? super PsiJavaModule> consumer) {
|
||||
Set<String> namesWithResults = new HashSet<>();
|
||||
|
||||
if (!processModulesFromIndices(moduleName, project, scope, consumer, namesWithResults)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we already found the module, no need to fallback.
|
||||
if (namesWithResults.contains(moduleName)) {
|
||||
return true;
|
||||
}
|
||||
return index.processElements(JavaStubIndexKeys.MODULE_NAMES,
|
||||
name,
|
||||
queryParameters.getProject(),
|
||||
queryParameters.getScope(),
|
||||
null,
|
||||
PsiJavaModule.class,
|
||||
consumer);
|
||||
|
||||
return processJpsModules(project, consumer, namesWithResults, moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean processJpsModules(@NotNull Project project,
|
||||
@NotNull Processor<? super PsiJavaModule> consumer,
|
||||
@NotNull Set<? super String> namesWithResults,
|
||||
@Nullable String moduleName) {
|
||||
PsiManager psiManager = PsiManager.getInstance(project);
|
||||
CachedValuesManager valuesManager = CachedValuesManager.getManager(project);
|
||||
ProjectRootModificationTracker tracker = ProjectRootModificationTracker.getInstance(project);
|
||||
Module[] modules = ModuleManager.getInstance(project).getModules();
|
||||
|
||||
for (Module module : modules) {
|
||||
VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(false);
|
||||
if (sourceRoots.length == 0) continue;
|
||||
|
||||
VirtualFile root = sourceRoots[0];
|
||||
|
||||
// auto module name from manifest (including virtual manifests)
|
||||
String autoModuleName = JavaManifestUtil.getManifestAttributeValue(module, PsiJavaModule.AUTO_MODULE_NAME);
|
||||
if (autoModuleName != null && !namesWithResults.contains(autoModuleName)) {
|
||||
if (moduleName != null && moduleName.equals(autoModuleName)) {
|
||||
namesWithResults.add(autoModuleName);
|
||||
if (!consumer.process(LightJavaModule.create(psiManager, root, autoModuleName))) return false;
|
||||
return true;
|
||||
}
|
||||
else if (moduleName == null) {
|
||||
namesWithResults.add(autoModuleName);
|
||||
if (!consumer.process(LightJavaModule.create(psiManager, root, autoModuleName))) return false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default module name derived from module name
|
||||
String defaultModuleName = valuesManager.getCachedValue(module, () ->
|
||||
CachedValueProvider.Result.create(LightJavaModule.moduleName(module.getName()), tracker));
|
||||
if (!namesWithResults.contains(defaultModuleName)) {
|
||||
if (moduleName != null && moduleName.equals(defaultModuleName)) {
|
||||
namesWithResults.add(defaultModuleName);
|
||||
if (!consumer.process(LightJavaModule.create(psiManager, root, defaultModuleName))) return false;
|
||||
return true;
|
||||
}
|
||||
else if (moduleName == null) {
|
||||
namesWithResults.add(defaultModuleName);
|
||||
if (!consumer.process(LightJavaModule.create(psiManager, root, defaultModuleName))) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean processModulesFromIndices(@NotNull String moduleName,
|
||||
@NotNull Project project,
|
||||
@NotNull GlobalSearchScope scope,
|
||||
@NotNull Processor<? super PsiJavaModule> consumer,
|
||||
@NotNull Set<? super String> namesWithResults) {
|
||||
PsiManager psiManager = PsiManager.getInstance(project);
|
||||
// Real modules from module-info.java
|
||||
for (PsiJavaModule module : JavaModuleNameIndex.getInstance().getModules(moduleName, project, scope)) {
|
||||
namesWithResults.add(moduleName);
|
||||
if (!consumer.process(module)) return false;
|
||||
}
|
||||
|
||||
// Light modules created from source manifests
|
||||
Set<VirtualFile> shadowedRoots = new HashSet<>();
|
||||
for (VirtualFile manifest : JavaSourceModuleNameIndex.getFilesByKey(moduleName, scope)) {
|
||||
VirtualFile root = getSourceRootFromManifest(manifest);
|
||||
if (root == null) continue;
|
||||
|
||||
namesWithResults.add(moduleName);
|
||||
shadowedRoots.add(root);
|
||||
|
||||
if (!consumer.process(LightJavaModule.create(psiManager, root, moduleName))) return false;
|
||||
}
|
||||
|
||||
// Light modules created from auto-module-name (jar roots)
|
||||
for (VirtualFile root : JavaAutoModuleNameIndex.getFilesByKey(moduleName, scope)) {
|
||||
if (shadowedRoots.contains(root)) continue;
|
||||
|
||||
VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME);
|
||||
// If the manifest claims a module name (possibly different), skip this root.
|
||||
if (manifest != null && LightJavaModule.claimedModuleName(manifest) != null) continue;
|
||||
|
||||
namesWithResults.add(moduleName);
|
||||
if (!consumer.process(LightJavaModule.create(psiManager, root, moduleName))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static @Nullable VirtualFile getSourceRootFromManifest(@NotNull VirtualFile manifest) {
|
||||
VirtualFile parent = manifest.getParent();
|
||||
if (parent == null) return null;
|
||||
VirtualFile root = parent.getParent();
|
||||
if (root == null) return null;
|
||||
return root;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user