[java-highlighting] more module-related problems migrated

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: e0b7381612b50ee09c7a5ff676aec5f14da6636d
This commit is contained in:
Tagir Valeev
2025-02-11 18:46:42 +00:00
committed by intellij-monorepo-bot
parent 6fc7a8b6e5
commit 5d47c60541
9 changed files with 219 additions and 165 deletions
@@ -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<PsiJavaModule> findCycle(@NotNull PsiJavaModule module) {
Project project = module.getProject();
List<Set<PsiJavaModule>> 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<Set<PsiJavaModule>> findCycles(@NotNull Project project) {
Set<PsiJavaModule> projectModules = new HashSet<>();
for (Module module : ModuleManager.getInstance(project).getModules()) {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
List<PsiJavaModule> 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<PsiJavaModule, PsiJavaModule> 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<PsiJavaModule> graph = new ChameleonGraph<>(relations, false);
DFSTBuilder<PsiJavaModule> builder = new DFSTBuilder<>(graph);
Collection<Collection<PsiJavaModule>> 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
@@ -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}
@@ -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);
@@ -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<String> 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<PsiJavaModule> 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 -> {}
}
}}
@@ -1373,6 +1373,30 @@ public final class JavaErrorKinds {
.withRange((module, file) -> getRange(module));
public static final Parameterized<PsiPackageAccessibilityStatement, PsiJavaModule> MODULE_OPENS_IN_WEAK_MODULE =
parameterized(PsiPackageAccessibilityStatement.class, PsiJavaModule.class, "module.opens.in.weak.module");
public static final Parameterized<PsiJavaCodeReferenceElement, PsiClass> 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<PsiJavaModuleReferenceElement> 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<PsiJavaModuleReferenceElement> MODULE_NOT_ON_PATH =
error(PsiJavaModuleReferenceElement.class, "module.not.on.path")
.withRawDescription(ref -> message("module.not.on.path", ref.getReferenceText()));
public static final Parameterized<PsiJavaModuleReferenceElement, Collection<PsiJavaModule>> MODULE_CYCLIC_DEPENDENCE =
error(PsiJavaModuleReferenceElement.class, "module.cyclic.dependence")
.<Collection<PsiJavaModule>>parameterized()
.withRawDescription(
(ref, modules) -> message("module.cyclic.dependence", modules.stream().map(PsiJavaModule::getName)
.sorted().collect(Collectors.joining(", "))));
public static final Simple<PsiJavaModuleReferenceElement> MODULE_DUPLICATE_EXPORTS_TARGET =
error(PsiJavaModuleReferenceElement.class, "module.duplicate.exports.target")
.withRawDescription(ref -> message("module.duplicate.exports.target", ref.getReferenceText()));
public static final Simple<PsiJavaModuleReferenceElement> MODULE_DUPLICATE_OPENS_TARGET =
error(PsiJavaModuleReferenceElement.class, "module.duplicate.opens.target")
.withRawDescription(ref -> message("module.duplicate.opens.target", ref.getReferenceText()));
private static @NotNull <Psi extends PsiElement> Simple<Psi> error(
@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) {
@@ -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);
@@ -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<IntentionAction> registrar = new ArrayList<>();
myFactory.registerOrderEntryFixes(ref, registrar);
return registrar;
}
return List.of();
});
}
private void createStatementFixes() {
@@ -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<String> 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<PsiJavaModule> findCycle(@NotNull PsiJavaModule module) {
Project project = module.getProject();
List<Set<PsiJavaModule>> 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<String, Set<String>> 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<Set<PsiJavaModule>> findCycles(@NotNull Project project) {
Set<PsiJavaModule> projectModules = new HashSet<>();
for (Module module : ModuleManager.getInstance(project).getModules()) {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
List<PsiJavaModule> 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<PsiJavaModule, PsiJavaModule> 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<PsiJavaModule> graph = new ChameleonGraph<>(relations, false);
DFSTBuilder<PsiJavaModule> builder = new DFSTBuilder<>(graph);
Collection<Collection<PsiJavaModule>> components = builder.getComponents();
if (!components.isEmpty()) {
return ContainerUtil.map(components, elements -> new LinkedHashSet<>(elements));
}
}
}
return Collections.emptyList();
}
private static @NotNull Map<String, Set<String>> exportsMap(@NotNull PsiJavaModule source) {
Map<String, Set<String>> map = new HashMap<>();
for (PsiPackageAccessibilityStatement statement : source.getExports()) {
@@ -272,37 +203,6 @@ public final class JavaModuleGraphUtil {
return map;
}
private static final class ChameleonGraph<N> implements Graph<N> {
private final Set<N> myNodes;
private final MultiMap<N, N> myEdges;
private final boolean myInbound;
private ChameleonGraph(MultiMap<N, N> 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<N> getNodes() {
return myNodes;
}
@Override
public @NotNull Iterator<N> getIn(N n) {
return myInbound ? myEdges.get(n).iterator() : Collections.emptyIterator();
}
@Override
public @NotNull Iterator<N> getOut(N n) {
return myInbound ? Collections.emptyIterator() : myEdges.get(n).iterator();
}
}
public static class JavaModuleScope extends GlobalSearchScope {
private final @NotNull MultiMap<String, PsiJavaModule> myModules;
private final boolean myIncludeLibraries;
@@ -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<PsiJavaModule> cycle = JavaModuleGraphUtil.findCycle(target);
if (cycle.contains(container)) {
Stream<String> 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<? super HighlightInfo.Builder> errorSink) {
Set<String> 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<? super HighlightInfo.Builder> errorSink) {
PsiReferenceList implRefList = statement.getImplementationList();