[java-highlighting] move module-related warnings to JavaModuleDefinitionInspection

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

GitOrigin-RevId: 66179a41da5e02b759f07f9d6638b87711181348
This commit is contained in:
Tagir Valeev
2025-02-17 12:04:21 +00:00
committed by intellij-monorepo-bot
parent b58f63db9e
commit a0f777069b
15 changed files with 243 additions and 158 deletions
@@ -4,6 +4,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.module.ModuleUtilCore;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
@@ -20,10 +21,8 @@ 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.psi.util.PsiUtilCore;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.graph.DFSTBuilder;
@@ -522,6 +521,67 @@ public final class JavaPsiModuleUtil {
}
}
/**
* State of package reference in module-info file
*/
public enum PackageReferenceState {
/**
* Valid reference to a non-empty package
*/
VALID,
/**
* No package is found
*/
PACKAGE_NOT_FOUND,
/**
* Package exists but contains no classes (for exports) or no files (for opens)
*/
PACKAGE_EMPTY
}
/**
* @param statement statement to check ('opens' or 'exports' statement)
* @return state of the reference
*/
public static @NotNull PackageReferenceState checkPackageReference(@NotNull PsiPackageAccessibilityStatement statement) {
PsiJavaCodeReferenceElement refElement = statement.getPackageReference();
if (refElement != null) {
PsiFile file = statement.getContainingFile();
Module module = ModuleUtilCore.findModuleForFile(file);
if (module != null) {
PsiElement target = refElement.resolve();
PsiDirectory[] directories = PsiDirectory.EMPTY_ARRAY;
if (target instanceof PsiPackage psiPackage) {
boolean inTests = ModuleRootManager.getInstance(module).getFileIndex().isInTestSourceContent(file.getVirtualFile());
directories = psiPackage.getDirectories(module.getModuleScope(inTests));
Module mainMultiReleaseModule = JavaMultiReleaseUtil.getMainMultiReleaseModule(module);
if (mainMultiReleaseModule != null) {
directories = ArrayUtil.mergeArrays(directories, psiPackage.getDirectories(mainMultiReleaseModule.getModuleScope(inTests)));
}
}
String packageName = statement.getPackageName();
if (directories.length == 0) {
return PackageReferenceState.PACKAGE_NOT_FOUND;
}
boolean opens = statement.getRole() == PsiPackageAccessibilityStatement.Role.OPENS;
if (packageName != null && isPackageEmpty(directories, packageName, opens)) {
return PackageReferenceState.PACKAGE_EMPTY;
}
}
}
return PackageReferenceState.VALID;
}
private static boolean isPackageEmpty(PsiDirectory @NotNull [] directories, @NotNull String packageName, boolean anyFile) {
if (anyFile) {
return !ContainerUtil.exists(directories, dir -> dir.getFiles().length > 0);
}
else {
return PsiUtil.isPackageEmpty(directories, packageName);
}
}
/**
* Helper service to support resolve in java-psi-impl
*/
@@ -514,3 +514,5 @@ module.cyclic.dependence=Cyclic dependency: {0}
module.duplicate.exports.target=Duplicate ''exports'' target: {0}
module.duplicate.opens.target=Duplicate ''opens'' target: {0}
module.duplicate.implementation=Duplicate implementation: {0}
module.reference.package.not.found=Package not found: {0}
module.reference.package.empty=Package is empty: {0}
@@ -664,6 +664,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (isApplicable(JavaFeature.MODULES)) {
if (!hasErrorResults()) myModuleChecker.checkHostModuleStrength(statement);
if (!hasErrorResults()) myModuleChecker.checkDuplicateModuleReferences(statement);
if (!hasErrorResults()) myModuleChecker.checkPackageReference(statement);
}
}
@@ -282,4 +282,16 @@ final class ModuleChecker {
}
}
}
void checkPackageReference(@NotNull PsiPackageAccessibilityStatement statement) {
if (statement.getRole() == PsiPackageAccessibilityStatement.Role.OPENS) return;
PsiJavaCodeReferenceElement refElement = statement.getPackageReference();
if (refElement == null) return;
JavaPsiModuleUtil.PackageReferenceState state = JavaPsiModuleUtil.checkPackageReference(statement);
if (state == JavaPsiModuleUtil.PackageReferenceState.VALID) return;
var kind = state == JavaPsiModuleUtil.PackageReferenceState.PACKAGE_NOT_FOUND
? JavaErrorKinds.MODULE_REFERENCE_PACKAGE_NOT_FOUND
: JavaErrorKinds.MODULE_REFERENCE_PACKAGE_EMPTY;
myVisitor.report(kind.create(statement));
}
}
@@ -1457,6 +1457,14 @@ public final class JavaErrorKinds {
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()));
public static final Simple<PsiPackageAccessibilityStatement> MODULE_REFERENCE_PACKAGE_NOT_FOUND =
error(PsiPackageAccessibilityStatement.class, "module.reference.package.not.found")
.withAnchor(st -> st.getPackageReference())
.withRawDescription(st -> message("module.reference.package.not.found", st.getPackageName()));
public static final Simple<PsiPackageAccessibilityStatement> MODULE_REFERENCE_PACKAGE_EMPTY =
error(PsiPackageAccessibilityStatement.class, "module.reference.package.empty")
.withAnchor(st -> st.getPackageReference())
.withRawDescription(st -> message("module.reference.package.empty", st.getPackageName()));
private static @NotNull <Psi extends PsiElement> Simple<Psi> error(
@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) {
@@ -218,6 +218,7 @@ inspection.implicit.subclass.make.class.extendable=Make class ''{0}'' {1,choice,
inspection.infinite.loop.option=Ignore when placed in Thread.run
inspection.java.module.naming.terminal.digits=Module name component ''{0}'' should avoid terminal digits
inspection.java.module.naming=Java module name contradicts the convention
inspection.java.module.definition=Java module definition problems
inspection.local.can.be.final.display.name=Local variable or parameter can be 'final'
inspection.local.can.be.final.option1=Report method parameters
inspection.local.can.be.final.option2=Report catch parameters
@@ -611,3 +612,5 @@ safe.varargs.not.suppress.potentially.unsafe.operations=@SafeVarargs do not supp
safe.varargs.on.reifiable.type=@SafeVarargs is not applicable to reifiable types
inspection.unreachable.catch.name=Unreachable catch section
inspection.unreachable.catch.message=Unreachable section: {1, choice, 0#exception|2#exceptions} ''{0}'' {1, choice, 0#has|2#have} already been caught
module.service.unused=Service interface provided but not exported or used
module.ambiguous=Ambiguous module reference: {0}
@@ -89,6 +89,10 @@
groupPath="Java" groupBundle="messages.InspectionsBundle" groupKey="group.names.naming.conventions"
bundle="messages.JavaAnalysisBundle" key="inspection.java.module.naming"
implementationClass="com.intellij.codeInspection.java19modules.JavaModuleNamingInspection"/>
<localInspection language="JAVA" shortName="JavaModuleDefinition" enabledByDefault="true" level="WARNING"
groupPath="Java" groupBundle="messages.InspectionsBundle" groupKey="group.names.declaration.redundancy"
bundle="messages.JavaAnalysisBundle" key="inspection.java.module.definition"
implementationClass="com.intellij.codeInspection.java19modules.JavaModuleDefinitionInspection"/>
<localInspection language="JAVA" shortName="JavaRequiresAutoModule" enabledByDefault="true" level="WARNING"
groupPathKey="group.path.names.java.language.level.specific.issues.and.migration.aids"
groupBundle="messages.InspectionsBundle" groupKey="group.names.language.level.specific.issues.and.migration.aids9"
@@ -321,28 +321,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!hasErrorResults()) model.checkSwitchLabelValues(myErrorSink);
}
@Override
public void visitModule(@NotNull PsiJavaModule module) {
super.visitModule(module);
if (!hasErrorResults()) ModuleHighlightUtil.checkUnusedServices(module, myFile, myErrorSink);
}
@Override
public void visitRequiresStatement(@NotNull PsiRequiresStatement statement) {
super.visitRequiresStatement(statement);
if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) {
if (!hasErrorResults()) add(ModuleHighlightUtil.checkModuleReference(statement));
}
}
@Override
public void visitPackageAccessibilityStatement(@NotNull PsiPackageAccessibilityStatement statement) {
super.visitPackageAccessibilityStatement(statement);
if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) {
if (!hasErrorResults()) add(ModuleHighlightUtil.checkPackageReference(statement, myFile));
}
}
@Override
public void visitModuleReferenceElement(@NotNull PsiJavaModuleReferenceElement refElement) {
super.visitModuleReferenceElement(refElement);
@@ -23,6 +23,8 @@ import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
@@ -168,6 +170,13 @@ final class JavaErrorFixProvider {
PsiClassType type = JavaPsiFacade.getElementFactory(error.project()).createType(error.context().superClass());
return myFactory.createExtendsListFix(error.context().subClass(), type, true);
});
JavaFixProvider<PsiPackageAccessibilityStatement, Void> createClassInPackage = error -> {
String packageName = error.psi().getPackageName();
Module module = ModuleUtilCore.findModuleForFile(error.psi().getContainingFile());
return module == null ? null : myFactory.createCreateClassInPackageInModuleFix(module, packageName);
};
fix(MODULE_REFERENCE_PACKAGE_NOT_FOUND, createClassInPackage);
fix(MODULE_REFERENCE_PACKAGE_EMPTY, createClassInPackage);
}
private void createStatementFixes() {
@@ -5,74 +5,19 @@ import com.intellij.codeInsight.JavaModuleSystemEx;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.quickfix.AddExportsDirectiveFix;
import com.intellij.codeInsight.daemon.impl.quickfix.AddUsesDirectiveFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.PsiPackageAccessibilityStatement.Role;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.util.JavaMultiReleaseUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import static java.util.Objects.requireNonNullElse;
// generates HighlightInfoType.ERROR-like HighlightInfos for modularity-related (Jigsaw) problems
final class ModuleHighlightUtil {
static void checkUnusedServices(@NotNull PsiJavaModule module, @NotNull PsiFile file, @NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
Module host = ModuleUtilCore.findModuleForFile(file);
if (host == null) {
return;
}
List<PsiProvidesStatement> provides = JBIterable.from(module.getProvides()).toList();
if (!provides.isEmpty()) {
Set<String> exports = JBIterable.from(module.getExports()).map(PsiPackageAccessibilityStatement::getPackageName).filter(Objects::nonNull).toSet();
Set<String> uses = JBIterable.from(module.getUses()).map(st -> qName(st.getClassReference())).filter(Objects::nonNull).toSet();
for (PsiProvidesStatement statement : provides) {
PsiJavaCodeReferenceElement ref = statement.getInterfaceReference();
if (ref != null) {
PsiElement target = ref.resolve();
if (target instanceof PsiClass && ModuleUtilCore.findModuleForFile(target.getContainingFile()) == host) {
String className = qName(ref);
String packageName = StringUtil.getPackageName(className);
if (!exports.contains(packageName) && !uses.contains(className)) {
String message = JavaErrorBundle.message("module.service.unused");
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(
requireNonNullElse(ref.getReferenceNameElement(), ref)).descriptionAndTooltip(message);
ModCommandAction action1 = new AddExportsDirectiveFix(module, packageName, "");
info.registerFix(action1, null, null, null, null);
ModCommandAction action = new AddUsesDirectiveFix(module, className);
info.registerFix(action, null, null, null, null);
errorSink.accept(info);
}
}
}
}
}
}
private static String qName(PsiJavaCodeReferenceElement ref) {
return ref != null ? ref.getQualifiedName() : null;
}
static HighlightInfo.Builder checkModuleReference(@NotNull PsiImportModuleStatement statement) {
PsiJavaModuleReferenceElement refElement = statement.getModuleReference();
if (refElement == null) return null;
@@ -93,29 +38,6 @@ final class ModuleHighlightUtil {
return null;
}
static HighlightInfo.Builder 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) {
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()));
}
}
}
return null;
}
private static @NotNull HighlightInfo.Builder getUnresolvedJavaModuleReason(@NotNull PsiElement parent, @NotNull PsiJavaModuleReferenceElement refElement) {
PsiJavaModuleReference ref = refElement.getReference();
assert ref != null : refElement.getParent();
@@ -145,55 +67,4 @@ final class ModuleHighlightUtil {
.descriptionAndTooltip(JavaErrorBundle.message("module.ambiguous", refElement.getReferenceText()));
}
}
static HighlightInfo.Builder checkPackageReference(@NotNull PsiPackageAccessibilityStatement statement, @NotNull PsiFile file) {
PsiJavaCodeReferenceElement refElement = statement.getPackageReference();
if (refElement != null) {
Module module = ModuleUtilCore.findModuleForFile(file);
if (module != null) {
PsiElement target = refElement.resolve();
PsiDirectory[] directories = PsiDirectory.EMPTY_ARRAY;
if (target instanceof PsiPackage psiPackage) {
boolean inTests = ModuleRootManager.getInstance(module).getFileIndex().isInTestSourceContent(file.getVirtualFile());
directories = psiPackage.getDirectories(module.getModuleScope(inTests));
Module mainMultiReleaseModule = JavaMultiReleaseUtil.getMainMultiReleaseModule(module);
if (mainMultiReleaseModule != null) {
directories = ArrayUtil.mergeArrays(directories, psiPackage.getDirectories(mainMultiReleaseModule.getModuleScope(inTests)));
}
}
String packageName = statement.getPackageName();
boolean opens = statement.getRole() == Role.OPENS;
HighlightInfoType type = opens ? HighlightInfoType.WARNING : HighlightInfoType.ERROR;
if (directories.length == 0) {
String message = JavaErrorBundle.message("package.not.found", packageName);
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(type).range(refElement).descriptionAndTooltip(message);
IntentionAction action = QuickFixFactory.getInstance().createCreateClassInPackageInModuleFix(module, packageName);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
return info;
}
if (packageName != null && isPackageEmpty(directories, packageName, opens)) {
String message = JavaErrorBundle.message("package.is.empty", packageName);
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(type).range(refElement).descriptionAndTooltip(message);
IntentionAction action = QuickFixFactory.getInstance().createCreateClassInPackageInModuleFix(module, packageName);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
return info;
}
}
}
return null;
}
private static boolean isPackageEmpty(PsiDirectory @NotNull [] directories, @NotNull String packageName, boolean anyFile) {
if (anyFile) {
return !ContainerUtil.exists(directories, dir -> dir.getFiles().length > 0);
}
else {
return PsiUtil.isPackageEmpty(directories, packageName);
}
}
}
@@ -0,0 +1,113 @@
// 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.java19modules;
import com.intellij.codeInsight.daemon.impl.quickfix.AddExportsDirectiveFix;
import com.intellij.codeInsight.daemon.impl.quickfix.AddUsesDirectiveFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFixBackedByIntentionAction;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.java.codeserver.core.JavaPsiModuleUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.util.Objects.requireNonNullElse;
public final class JavaModuleDefinitionInspection extends AbstractBaseJavaLocalInspectionTool {
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return !PsiUtil.isModuleFile(holder.getFile()) ? PsiElementVisitor.EMPTY_VISITOR : new JavaElementVisitor() {
@Override
public void visitModule(@NotNull PsiJavaModule module) {
checkUnusedServices(module);
}
@Override
public void visitRequiresStatement(@NotNull PsiRequiresStatement statement) {
checkModuleReference(statement);
}
@Override
public void visitPackageAccessibilityStatement(@NotNull PsiPackageAccessibilityStatement statement) {
if (statement.getRole() != PsiPackageAccessibilityStatement.Role.OPENS) return;
PsiJavaCodeReferenceElement refElement = statement.getPackageReference();
if (refElement == null) return;
JavaPsiModuleUtil.PackageReferenceState state = JavaPsiModuleUtil.checkPackageReference(statement);
if (state == JavaPsiModuleUtil.PackageReferenceState.VALID) return;
var kind = state == JavaPsiModuleUtil.PackageReferenceState.PACKAGE_NOT_FOUND
? JavaErrorKinds.MODULE_REFERENCE_PACKAGE_NOT_FOUND
: JavaErrorKinds.MODULE_REFERENCE_PACKAGE_EMPTY;
String message = kind.description(statement, null).toString();
String packageName = statement.getPackageName();
Module module = ModuleUtilCore.findModuleForFile(holder.getFile());
IntentionAction action =
module == null ? null : QuickFixFactory.getInstance().createCreateClassInPackageInModuleFix(module, packageName);
holder.problem(refElement, message)
.maybeFix(action == null ? null : new LocalQuickFixBackedByIntentionAction(action))
.register();
}
void checkModuleReference(@NotNull PsiRequiresStatement statement) {
PsiJavaModuleReferenceElement refElement = statement.getReferenceElement();
if (refElement != null) {
PsiJavaModuleReference ref = refElement.getReference();
if (ref != null) {
ResolveResult[] results = ref.multiResolve(true);
if (results.length > 1 && ref.resolve() == null) {
holder.registerProblem(refElement, JavaAnalysisBundle.message("module.ambiguous", refElement.getReferenceText()));
}
}
}
}
private void checkUnusedServices(@NotNull PsiJavaModule module) {
Module host = ModuleUtilCore.findModuleForFile(holder.getFile());
if (host == null) {
return;
}
List<PsiProvidesStatement> provides = JBIterable.from(module.getProvides()).toList();
if (!provides.isEmpty()) {
Set<String>
exports =
JBIterable.from(module.getExports()).map(PsiPackageAccessibilityStatement::getPackageName).filter(Objects::nonNull).toSet();
Set<String> uses = JBIterable.from(module.getUses()).map(st -> qName(st.getClassReference())).filter(Objects::nonNull).toSet();
for (PsiProvidesStatement statement : provides) {
PsiJavaCodeReferenceElement ref = statement.getInterfaceReference();
if (ref != null) {
PsiElement target = ref.resolve();
if (target instanceof PsiClass && ModuleUtilCore.findModuleForFile(target.getContainingFile()) == host) {
String className = qName(ref);
String packageName = StringUtil.getPackageName(className);
if (!exports.contains(packageName) && !uses.contains(className)) {
holder.problem(requireNonNullElse(ref.getReferenceNameElement(), ref),
JavaAnalysisBundle.message("module.service.unused"))
.fix(new AddExportsDirectiveFix(module, packageName, ""))
.fix(new AddUsesDirectiveFix(module, className))
.register();
}
}
}
}
}
}
};
}
private static String qName(PsiJavaCodeReferenceElement ref) {
return ref != null ? ref.getQualifiedName() : null;
}
}
@@ -0,0 +1,13 @@
<html>
<body>
Reports miscellaneous problems with the module-info.java file. For example, it reports a service which is provided but not exported or used.
<p><b>Example:</b></p>
<pre><code>
module myModule {
// Service is provided but its containing package is not exported
provides com.example.MyService with com.example.MyServiceImpl;
}
</code></pre>
<!-- tooltip end -->
</body>
</html>
@@ -193,7 +193,6 @@ module.ambiguous=Ambiguous module reference: {0}
module.not.on.path=Module is not in dependencies: {0}
package.not.found=Package not found: {0}
package.is.empty=Package is empty: {0}
module.service.unused=Service interface provided but not exported or used
module.access.to.unnamed=Package ''{0}'' is declared in the unnamed module, but module ''{1}'' does not read it
module.access.from.named=Package ''{0}'' is declared in module ''{1}'', which does not export it to module ''{2}''
module.access.from.unnamed=Package ''{0}'' is declared in module ''{1}'', which does not export it to the unnamed module
@@ -3,11 +3,17 @@ package com.intellij.codeInsight.daemon.impl.quickfix
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil
import com.intellij.codeInsight.intention.impl.CreateClassInPackageInModuleFix
import com.intellij.codeInspection.java19modules.JavaModuleDefinitionInspection
import com.intellij.psi.PsiJavaFile
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
class CreateClassInPackageInModuleTest : LightJavaCodeInsightFixtureTestCase() {
override fun setUp() {
super.setUp()
myFixture.enableInspections(JavaModuleDefinitionInspection())
}
fun testExportsMissingDir(): Unit = doTestMissingDir("exports")
fun testOpensMissingDir(): Unit = doTestMissingDir("opens")
@@ -7,6 +7,7 @@ import com.intellij.codeInsight.intention.IntentionActionDelegate
import com.intellij.codeInspection.IllegalDependencyOnInternalPackageInspection
import com.intellij.codeInspection.deprecation.DeprecationInspection
import com.intellij.codeInspection.deprecation.MarkedForRemovalInspection
import com.intellij.codeInspection.java19modules.JavaModuleDefinitionInspection
import com.intellij.java.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
@@ -14,6 +15,7 @@ import com.intellij.java.workspace.entities.JavaModuleSettingsEntity
import com.intellij.java.workspace.entities.javaSettings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runWriteActionAndWait
import com.intellij.openapi.diagnostic.ReportingClassSubstitutor
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootManager
@@ -50,6 +52,8 @@ import java.util.jar.JarFile
class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
override fun setUp() {
super.setUp()
myFixture.enableInspections(JavaModuleDefinitionInspection())
addFile("module-info.java", "module M2 { }", M2)
addFile("module-info.java", "module M3 { }", M3)
@@ -289,7 +293,7 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
}
fun testWeakModule() {
highlight("""open module M { <error descr="'opens' is not allowed in an open module">opens pkg.missing;</error> }""")
highlight("""open module M { <error descr="'opens' is not allowed in an open module">opens <warning descr="Package not found: pkg.missing">pkg.missing</warning>;</error> }""")
}
fun testUses() {
@@ -327,6 +331,7 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
import pkg.main.Impl6;
module M {
requires M2;
exports pkg.main;
provides pkg.main.C with pkg.main.<error descr="Cannot resolve symbol 'NoImpl'">NoImpl</error>;
provides pkg.main.C with pkg.main.<error descr="'pkg.main.Impl1' is not public in 'pkg.main'. Cannot be accessed from outside package">Impl1</error>;
provides pkg.main.C with pkg.main.<error descr="The service implementation type must be a subtype of the service interface type, or have a public static no-args 'provider' method">Impl2</error>;
@@ -973,9 +978,10 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
myFixture.configureFromExistingVirtualFile(addFile(path, text))
val availableIntentions = myFixture.availableIntentions
val available = availableIntentions
.map { (it.asModCommandAction() ?: IntentionActionDelegate.unwrap(it))::class.java }
.map { ReportingClassSubstitutor.getClassToReport(it) }
.filter { it.name.startsWith("com.intellij.codeInsight.") &&
!(it.name.startsWith("com.intellij.codeInsight.intention.impl.") && it.name.endsWith("Action"))
&& !it.name.endsWith("DisableHighlightingIntentionAction")
&& !it.name.endsWith("DeclarativeHintsTogglingIntention")}
.map { it.simpleName }
assertThat(available).describedAs(availableIntentions.toString()).containsExactlyInAnyOrder(*fixes)