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 b1852a3ce926..cb055cea3325 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 @@ -3,6 +3,7 @@ package com.intellij.java.codeserver.core; import com.intellij.ide.highlighter.ArchiveFileType; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; @@ -22,14 +23,17 @@ 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.psi.util.PsiUtilCore; 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 org.jetbrains.jps.model.java.JavaResourceRootType; import org.jetbrains.jps.model.java.JavaSourceRootType; @@ -38,6 +42,7 @@ import java.util.function.BiFunction; import java.util.jar.JarFile; import static com.intellij.psi.PsiJavaModule.JAVA_BASE; +import static java.util.Objects.requireNonNullElse; /** * Utilities related to JPMS modules @@ -243,6 +248,80 @@ public final class JavaPsiModuleUtil { return getRequiresGraph(source).getAllDependencies(source, true); } + /** + * @param module module to check for dependency cycles + * @return collection of modules that form a dependency cycle + */ + public static @NotNull Collection findCycle(@NotNull PsiJavaModule module) { + Project project = module.getProject(); + List> cycles = CachedValuesManager.getManager(project).getCachedValue(project, () -> + CachedValueProvider.Result.create(findCycles(project), + PsiJavaModuleModificationTracker.getInstance(project), + ProjectRootModificationTracker.getInstance(project))); + return requireNonNullElse(ContainerUtil.find(cycles, set -> set.contains(module)), Collections.emptyList()); + } + + private static @Nullable VirtualFile getVirtualFile(@NotNull PsiJavaModule module) { + if (module instanceof LightJavaModule light) { + return light.getRootVirtualFile(); + } + return PsiUtilCore.getVirtualFile(module); + } + + /* + * Looks for cycles between Java modules in the project sources. + * Library/JDK modules are excluded in an assumption there can't be any lib -> src dependencies. + * Module references are resolved "globally" (i.e., without taking project dependencies into account). + */ + private static @Unmodifiable @NotNull List> findCycles(@NotNull Project project) { + Set projectModules = new HashSet<>(); + for (Module module : ModuleManager.getInstance(project).getModules()) { + ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); + List descriptors = ContainerUtil.mapNotNull(moduleRootManager.getSourceRoots(true), + root -> findDescriptorByFile(root, project)); + if (descriptors.size() > 2) return Collections.emptyList(); // aborts the process when there are incorrect modules in the project + + if (descriptors.size() == 2) { + if (descriptors.stream() + .map(d -> getVirtualFile(d)) + .filter(Objects::nonNull).count() < 2) { + return Collections.emptyList(); + } + projectModules.addAll(descriptors); + } + if (descriptors.size() == 1) projectModules.add(descriptors.get(0)); + } + + if (!projectModules.isEmpty()) { + MultiMap relations = MultiMap.create(); + for (PsiJavaModule module : projectModules) { + for (PsiRequiresStatement statement : module.getRequires()) { + PsiJavaModuleReference ref = statement.getModuleReference(); + if (ref != null) { + ResolveResult[] results = ref.multiResolve(true); + if (results.length == 1) { + PsiJavaModule dependency = (PsiJavaModule)results[0].getElement(); + if (dependency != null && projectModules.contains(dependency)) { + relations.putValue(module, dependency); + } + } + } + } + } + + if (!relations.isEmpty()) { + Graph graph = new ChameleonGraph<>(relations, false); + DFSTBuilder builder = new DFSTBuilder<>(graph); + Collection> components = builder.getComponents(); + if (!components.isEmpty()) { + return ContainerUtil.map(components, elements -> new LinkedHashSet<>(elements)); + } + } + } + + return Collections.emptyList(); + } + /** * Represents a dependency conflict when a single package is imported from two modules * @param packageName package name diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index 6fde0e6d9c2d..a0a4be830277 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -492,3 +492,9 @@ module.duplicate.uses=Duplicate ''uses'': {0} module.duplicate.provides=Duplicate ''provides'': {0} module.file.wrong.location=Module declaration should be located in a module's source root module.opens.in.weak.module='opens' is not allowed in an open module +module.service.enum=The service definition is an enum: {0} +module.not.found=Module not found: {0} +module.not.on.path=Module is not in dependencies: {0} +module.cyclic.dependence=Cyclic dependence: {0} +module.duplicate.exports.target=Duplicate ''exports'' target: {0} +module.duplicate.opens.target=Duplicate ''opens'' target: {0} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java index a50e89baf87b..cc24c1255755 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java @@ -642,6 +642,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { super.visitRequiresStatement(statement); if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) { if (!hasErrorResults() && myLanguageLevel.isAtLeast(LanguageLevel.JDK_10)) myModuleChecker.checkModifiers(statement); + if (!hasErrorResults()) myModuleChecker.checkModuleReference(statement); } } @@ -650,6 +651,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { super.visitPackageAccessibilityStatement(statement); if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) { if (!hasErrorResults()) myModuleChecker.checkHostModuleStrength(statement); + if (!hasErrorResults()) myModuleChecker.checkDuplicateModuleReferences(statement); } } @@ -659,6 +661,14 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) checkPreviewFeature(statement); } + @Override + public void visitUsesStatement(@NotNull PsiUsesStatement statement) { + super.visitUsesStatement(statement); + if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) { + if (!hasErrorResults()) myModuleChecker.checkServiceReference(statement.getClassReference()); + } + } + @Override public void visitImportModuleStatement(@NotNull PsiImportModuleStatement statement) { super.visitImportModuleStatement(statement); diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ModuleChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ModuleChecker.java index 39f054c78a33..07f7765d69fe 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ModuleChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ModuleChecker.java @@ -17,6 +17,7 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; @@ -127,6 +128,20 @@ final class ModuleChecker { } } + void checkDuplicateModuleReferences(@NotNull PsiPackageAccessibilityStatement statement) { + Set targets = new HashSet<>(); + for (PsiJavaModuleReferenceElement refElement : statement.getModuleReferences()) { + String refText = refElement.getReferenceText(); + PsiJavaModuleReference ref = refElement.getReference(); + assert ref != null : statement; + if (!targets.add(refText)) { + boolean exports = statement.getRole() == PsiPackageAccessibilityStatement.Role.EXPORTS; + var kind = exports ? JavaErrorKinds.MODULE_DUPLICATE_EXPORTS_TARGET : JavaErrorKinds.MODULE_DUPLICATE_OPENS_TARGET; + myVisitor.report(kind.create(refElement)); + } + } + } + private static String qName(PsiJavaCodeReferenceElement ref) { return ref != null ? ref.getQualifiedName() : null; } @@ -167,4 +182,52 @@ final class ModuleChecker { myVisitor.report(JavaErrorKinds.MODULE_OPENS_IN_WEAK_MODULE.create(statement, module)); } } -} + + void checkServiceReference(@Nullable PsiJavaCodeReferenceElement refElement) { + if (refElement != null) { + PsiElement target = refElement.resolve(); + if (!(target instanceof PsiClass psiClass)) { + myVisitor.report(JavaErrorKinds.REFERENCE_UNRESOLVED.create(refElement)); + } + else if (psiClass.isEnum()) { + myVisitor.report(JavaErrorKinds.MODULE_SERVICE_ENUM.create(refElement, psiClass)); + } + } + } + + void checkModuleReference(@NotNull PsiRequiresStatement statement) { + PsiJavaModuleReferenceElement refElement = statement.getReferenceElement(); + if (refElement != null) { + PsiJavaModuleReference ref = refElement.getReference(); + assert ref != null : refElement.getParent(); + PsiJavaModule target = ref.resolve(); + if (target == null) { + reportUnresolvedJavaModule(refElement); + return; + } + PsiJavaModule container = (PsiJavaModule)statement.getParent(); + if (target == container) { + myVisitor.report(JavaErrorKinds.MODULE_CYCLIC_DEPENDENCE.create(refElement, Set.of(container))); + } + else { + Collection cycle = JavaPsiModuleUtil.findCycle(target); + if (cycle.contains(container)) { + myVisitor.report(JavaErrorKinds.MODULE_CYCLIC_DEPENDENCE.create(refElement, cycle)); + } + } + } + } + + private void reportUnresolvedJavaModule(@NotNull PsiJavaModuleReferenceElement refElement) { + PsiJavaModuleReference ref = refElement.getReference(); + assert ref != null : refElement.getParent(); + + ResolveResult[] results = ref.multiResolve(true); + switch (results.length) { + case 0 -> myVisitor.report(myVisitor.isIncompleteModel() + ? JavaErrorKinds.REFERENCE_PENDING.create(refElement) + : JavaErrorKinds.MODULE_NOT_FOUND.create(refElement)); + case 1 -> myVisitor.report(JavaErrorKinds.MODULE_NOT_ON_PATH.create(refElement)); + default -> {} + } + }} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java index 99ff5487b2ff..6371c4a6376a 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java @@ -1373,6 +1373,30 @@ public final class JavaErrorKinds { .withRange((module, file) -> getRange(module)); public static final Parameterized MODULE_OPENS_IN_WEAK_MODULE = parameterized(PsiPackageAccessibilityStatement.class, PsiJavaModule.class, "module.opens.in.weak.module"); + public static final Parameterized MODULE_SERVICE_ENUM = + parameterized(PsiJavaCodeReferenceElement.class, PsiClass.class, "module.service.enum") + .withAnchor(ref -> requireNonNullElse(ref.getReferenceNameElement(), ref)) + .withRawDescription((ref, cls) -> message("module.service.enum", cls.getName())); + public static final Simple MODULE_NOT_FOUND = + error(PsiJavaModuleReferenceElement.class, "module.not.found") + .withHighlightType(JavaErrorHighlightType.WRONG_REF) + .withRawDescription(ref -> message("module.not.found", ref.getReferenceText())); + public static final Simple MODULE_NOT_ON_PATH = + error(PsiJavaModuleReferenceElement.class, "module.not.on.path") + .withRawDescription(ref -> message("module.not.on.path", ref.getReferenceText())); + public static final Parameterized> MODULE_CYCLIC_DEPENDENCE = + error(PsiJavaModuleReferenceElement.class, "module.cyclic.dependence") + .>parameterized() + .withRawDescription( + (ref, modules) -> message("module.cyclic.dependence", modules.stream().map(PsiJavaModule::getName) + .sorted().collect(Collectors.joining(", ")))); + + public static final Simple MODULE_DUPLICATE_EXPORTS_TARGET = + error(PsiJavaModuleReferenceElement.class, "module.duplicate.exports.target") + .withRawDescription(ref -> message("module.duplicate.exports.target", ref.getReferenceText())); + public static final Simple MODULE_DUPLICATE_OPENS_TARGET = + error(PsiJavaModuleReferenceElement.class, "module.duplicate.opens.target") + .withRawDescription(ref -> message("module.duplicate.opens.target", ref.getReferenceText())); private static @NotNull Simple error( @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index da8697a08f2d..0f715d557f16 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -426,7 +426,19 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh super.visitPackageAccessibilityStatement(statement); if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) { if (!hasErrorResults()) add(ModuleHighlightUtil.checkPackageReference(statement, myFile)); - if (!hasErrorResults()) ModuleHighlightUtil.checkPackageAccessTargets(statement, myErrorSink); + } + } + + @Override + public void visitModuleReferenceElement(@NotNull PsiJavaModuleReferenceElement refElement) { + super.visitModuleReferenceElement(refElement); + PsiJavaModuleReference ref = refElement.getReference(); + if (refElement.getParent() instanceof PsiPackageAccessibilityStatement && + ref != null && ref.multiResolve(true).length == 0) { + String message = JavaErrorBundle.message("module.not.found", refElement.getReferenceText()); + HighlightInfo.Builder info = + HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).descriptionAndTooltip(message); + add(info); } } @@ -438,14 +450,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } } - @Override - public void visitUsesStatement(@NotNull PsiUsesStatement statement) { - super.visitUsesStatement(statement); - if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) { - if (!hasErrorResults()) add(ModuleHighlightUtil.checkServiceReference(statement.getClassReference())); - } - } - @Override public void visitProvidesStatement(@NotNull PsiProvidesStatement statement) { super.visitProvidesStatement(statement); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java index 3c3b2ab40349..f8e4ffcc9385 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java @@ -148,6 +148,19 @@ final class JavaErrorFixProvider { fix(MODULE_FILE_WRONG_LOCATION, error -> new MoveFileFix(error.psi().getContainingFile().getVirtualFile(), error.context(), QuickFixBundle.message("move.file.to.source.root.text"))); fix(MODULE_OPENS_IN_WEAK_MODULE, error -> removeModifierFix(error.context(), PsiModifier.OPEN)); + fix(MODULE_DUPLICATE_EXPORTS_TARGET, + error -> myFactory.createDeleteFix(error.psi(), QuickFixBundle.message("delete.reference.fix.text"))); + fix(MODULE_DUPLICATE_OPENS_TARGET, + error -> myFactory.createDeleteFix(error.psi(), QuickFixBundle.message("delete.reference.fix.text"))); + multi(MODULE_NOT_ON_PATH, error -> { + PsiJavaModuleReference ref = error.psi().getReference(); + if (ref != null) { + List registrar = new ArrayList<>(); + myFactory.registerOrderEntryFixes(ref, registrar); + return registrar; + } + return List.of(); + }); } private void createStatementFixes() { 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 38f4fd48242e..2866433b5e94 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 @@ -11,7 +11,6 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeRegistry; 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.Project; import com.intellij.openapi.roots.*; @@ -19,7 +18,6 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; -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; @@ -30,18 +28,14 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.ObjectUtils; 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 org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; import java.util.*; import static com.intellij.openapi.roots.DependencyScope.PROVIDED; import static com.intellij.psi.PsiJavaModule.JAVA_BASE; -import static java.util.Objects.requireNonNullElse; public final class JavaModuleGraphUtil { private static final Set STATIC_REQUIRES_MODULE_NAMES = Set.of("lombok"); @@ -72,15 +66,6 @@ public final class JavaModuleGraphUtil { return javaModule instanceof LightJavaModule ? null : javaModule; } - public static @NotNull Collection findCycle(@NotNull PsiJavaModule module) { - Project project = module.getProject(); - List> cycles = CachedValuesManager.getManager(project).getCachedValue(project, () -> - Result.create(findCycles(project), - PsiJavaModuleModificationTracker.getInstance(project), - ProjectRootModificationTracker.getInstance(project))); - return requireNonNullElse(ContainerUtil.find(cycles, set -> set.contains(module)), Collections.emptyList()); - } - public static boolean exports(@NotNull PsiJavaModule source, @NotNull String packageName, @Nullable PsiJavaModule target) { Map> exports = CachedValuesManager.getCachedValue(source, () -> Result.create(exportsMap(source), source.getContainingFile())); @@ -208,60 +193,6 @@ public final class JavaModuleGraphUtil { return scope == PROVIDED; } - /* - * Looks for cycles between Java modules in the project sources. - * Library/JDK modules are excluded in an assumption there can't be any lib -> src dependencies. - * Module references are resolved "globally" (i.e., without taking project dependencies into account). - */ - private static @Unmodifiable @NotNull List> findCycles(@NotNull Project project) { - Set projectModules = new HashSet<>(); - for (Module module : ModuleManager.getInstance(project).getModules()) { - ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); - List descriptors = ContainerUtil.mapNotNull(moduleRootManager.getSourceRoots(true), - root -> findDescriptorByFile(root, project)); - if (descriptors.size() > 2) return Collections.emptyList(); // aborts the process when there are incorrect modules in the project - - if (descriptors.size() == 2) { - if (descriptors.stream() - .map(d -> getVirtualFile(d)) - .filter(Objects::nonNull).count() < 2) { - return Collections.emptyList(); - } - projectModules.addAll(descriptors); - } - if (descriptors.size() == 1) projectModules.add(descriptors.get(0)); - } - - if (!projectModules.isEmpty()) { - MultiMap relations = MultiMap.create(); - for (PsiJavaModule module : projectModules) { - for (PsiRequiresStatement statement : module.getRequires()) { - PsiJavaModuleReference ref = statement.getModuleReference(); - if (ref != null) { - ResolveResult[] results = ref.multiResolve(true); - if (results.length == 1) { - PsiJavaModule dependency = (PsiJavaModule)results[0].getElement(); - if (dependency != null && projectModules.contains(dependency)) { - relations.putValue(module, dependency); - } - } - } - } - } - - if (!relations.isEmpty()) { - Graph graph = new ChameleonGraph<>(relations, false); - DFSTBuilder builder = new DFSTBuilder<>(graph); - Collection> components = builder.getComponents(); - if (!components.isEmpty()) { - return ContainerUtil.map(components, elements -> new LinkedHashSet<>(elements)); - } - } - } - - return Collections.emptyList(); - } - private static @NotNull Map> exportsMap(@NotNull PsiJavaModule source) { Map> map = new HashMap<>(); for (PsiPackageAccessibilityStatement statement : source.getExports()) { @@ -272,37 +203,6 @@ public final class JavaModuleGraphUtil { return map; } - 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(); - } - } - public static class JavaModuleScope extends GlobalSearchScope { private final @NotNull MultiMap myModules; private final boolean myIncludeLibraries; 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 5850bc121cdd..41be5b9d3fe2 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 @@ -13,7 +13,6 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.java.codeserver.core.JavaServiceProviderUtil; import com.intellij.modcommand.ModCommandAction; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.roots.ModuleRootManager; @@ -30,12 +29,9 @@ import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.JBIterable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; // generates HighlightInfoType.ERROR-like HighlightInfos for modularity-related (Jigsaw) problems final class ModuleHighlightUtil { @@ -102,19 +98,16 @@ final class ModuleHighlightUtil { PsiJavaModuleReference ref = refElement.getReference(); assert ref != null : refElement.getParent(); PsiJavaModule target = ref.resolve(); - if (target == null) return getUnresolvedJavaModuleReason(statement, refElement); - PsiJavaModule container = (PsiJavaModule)statement.getParent(); - if (target == container) { - String message = JavaErrorBundle.message("module.cyclic.dependence", container.getName()); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).descriptionAndTooltip(message); - } - else { - Collection cycle = JavaModuleGraphUtil.findCycle(target); - if (cycle.contains(container)) { - Stream stream = cycle.stream().map(PsiJavaModule::getName); - if (ApplicationManager.getApplication().isUnitTestMode()) stream = stream.sorted(); - String message = JavaErrorBundle.message("module.cyclic.dependence", stream.collect(Collectors.joining(", "))); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).descriptionAndTooltip(message); + if (target == null) { + PsiJavaModuleReference ref1 = refElement.getReference(); + assert ref1 != null : refElement.getParent(); + + ResolveResult[] results = ref1.multiResolve(true); + if (results.length > 1) { + // TODO: make as error or extract to inspection + return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING) + .range(refElement) + .descriptionAndTooltip(JavaErrorBundle.message("module.ambiguous", refElement.getReferenceText())); } } } @@ -203,44 +196,6 @@ final class ModuleHighlightUtil { } } - static void checkPackageAccessTargets(@NotNull PsiPackageAccessibilityStatement statement, - @NotNull Consumer errorSink) { - Set targets = new HashSet<>(); - for (PsiJavaModuleReferenceElement refElement : statement.getModuleReferences()) { - String refText = refElement.getReferenceText(); - PsiJavaModuleReference ref = refElement.getReference(); - assert ref != null : statement; - if (!targets.add(refText)) { - boolean exports = statement.getRole() == Role.EXPORTS; - String message = JavaErrorBundle.message(exports ? "module.duplicate.exports.target" : "module.duplicate.opens.target", refText); - HighlightInfo.Builder info = createDuplicateReference(refElement, message); - errorSink.accept(info); - } - else if (ref.multiResolve(true).length == 0) { - String message = JavaErrorBundle.message("module.not.found", refElement.getReferenceText()); - HighlightInfo.Builder info = - HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).descriptionAndTooltip(message); - errorSink.accept(info); - } - } - } - - static HighlightInfo.Builder checkServiceReference(@Nullable PsiJavaCodeReferenceElement refElement) { - if (refElement != null) { - PsiElement target = refElement.resolve(); - if (!(target instanceof PsiClass psiClass)) { - String message = JavaErrorBundle.message("cannot.resolve.symbol", refElement.getReferenceName()); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(refElement)).descriptionAndTooltip(message); - } - else if (psiClass.isEnum()) { - String message = JavaErrorBundle.message("module.service.enum", psiClass.getName()); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(refElement)).descriptionAndTooltip(message); - } - } - - return null; - } - static void checkServiceImplementations(@NotNull PsiProvidesStatement statement, @NotNull PsiFile file, @NotNull Consumer errorSink) { PsiReferenceList implRefList = statement.getImplementationList();