[java] allows one module-info file in either source or test root - access highlighting (IDEA-171221)

This commit is contained in:
Roman Shevchenko
2017-04-12 11:52:56 +02:00
parent 5c07c848e8
commit 7bc5c83779
3 changed files with 92 additions and 12 deletions
@@ -92,7 +92,7 @@ public class JavaModuleGraphUtil {
private static List<Set<PsiJavaModule>> findCycles(Project project) {
Set<PsiJavaModule> projectModules = ContainerUtil.newHashSet();
for (Module module : ModuleManager.getInstance(project).getModules()) {
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope());
if (files.size() > 1) return Collections.emptyList(); // aborts the process when there are incorrect modules in the project
Optional.ofNullable(ContainerUtil.getFirstItem(files))
.map(PsiManager.getInstance(project)::findFile)
@@ -146,7 +146,7 @@ public class JavaModuleGraphUtil {
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
Set<String> transitiveEdges = ContainerUtil.newTroveSet();
for (Module module : ModuleManager.getInstance(project).getModules()) {
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope());
Optional.ofNullable(ContainerUtil.getFirstItem(files))
.map(PsiManager.getInstance(project)::findFile)
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
@@ -79,18 +79,24 @@ public class ModuleHighlightUtil {
return LightJavaModule.getModule(PsiManager.getInstance(project), classRoot);
}
}
return null;
}
else {
Module module = index.getModuleForFile(file);
return Optional.ofNullable(module)
.map(m -> FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, m.getModuleScope(false)))
.map(c -> c.size() == 1 ? c.iterator().next() : null)
.map(f -> PsiManager.getInstance(project).findFile(f))
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
.orElse(null);
if (module != null) {
boolean isTest = index.isInTestSourceContent(file);
List<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope()).stream()
.filter(f -> index.isInTestSourceContent(f) == isTest)
.collect(Collectors.toList());
if (files.size() == 1) {
PsiFile psiFile = PsiManager.getInstance(project).findFile(files.get(0));
if (psiFile instanceof PsiJavaFile) {
return ((PsiJavaFile)psiFile).getModuleDeclaration();
}
}
}
}
return null;
}
static HighlightInfo checkPackageStatement(@NotNull PsiPackageStatement statement, @NotNull PsiFile file, @Nullable PsiJavaModule module) {
@@ -246,6 +246,80 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
""".trimIndent())
}
fun testPackageAccessibilityInNonModularTest() {
addFile("module-info.java", "module M { }")
addFile("module-info.java", "module M2 { exports pkg.m2; exports pkg.m2.impl to close.friends.only; }", M2)
addFile("pkg/m2/C2.java", "package pkg.m2;\npublic class C2 { }", M2)
addFile("pkg/m2/impl/C2Impl.java", "package pkg.m2.impl;\nimport pkg.m2.C2;\npublic class C2Impl { public static C2 make() {} }", M2)
addFile("pkg/m4/C4.java", "package pkg.m4;\npublic class C4 { }", M4)
addFile("module-info.java", "module M5 { exports pkg.m5; }", M5)
addFile("pkg/m5/C5.java", "package pkg.m5;\npublic class C5 { }", M5)
addFile("module-info.java", "module M6 { requires transitive M7; }", M6)
addFile("module-info.java", "module M7 { exports pkg.m7; }", M7)
addFile("pkg/m7/C7.java", "package pkg.m7;\npublic class C7 { }", M7)
highlight("test.java", """
import pkg.m2.C2;
import pkg.m2.*;
import pkg.m2.impl.C2Impl;
import pkg.m2.impl.*;
import pkg.m4.C4;
import pkg.m5.C5;
import pkg.m7.C7;
import pkg.lib1.LC1;
import pkg.lib1.impl.LC1Impl;
import pkg.lib1.impl.*;
import pkg.lib2.LC2;
import pkg.lib2.impl.LC2Impl;
import static pkg.m2.impl.C2Impl.make;
/** See also {@link C2Impl#make} */
class C {{
C2Impl.make();
pkg.m2.impl.C2Impl.make();
}}
""".trimIndent(), true)
}
fun testPackageAccessibilityInModularTest() {
addTestFile("module-info.java", "module M { requires M2; requires M6; requires lib.named; requires lib.auto; }")
addFile("module-info.java", "module M2 { exports pkg.m2; exports pkg.m2.impl to close.friends.only; }", M2)
addFile("pkg/m2/C2.java", "package pkg.m2;\npublic class C2 { }", M2)
addFile("pkg/m2/impl/C2Impl.java", "package pkg.m2.impl;\nimport pkg.m2.C2;\npublic class C2Impl { public static C2 make() {} }", M2)
addFile("pkg/m4/C4.java", "package pkg.m4;\npublic class C4 { }", M4)
addFile("module-info.java", "module M5 { exports pkg.m5; }", M5)
addFile("pkg/m5/C5.java", "package pkg.m5;\npublic class C5 { }", M5)
addFile("module-info.java", "module M6 { requires transitive M7; }", M6)
addFile("module-info.java", "module M7 { exports pkg.m7; }", M7)
addFile("pkg/m7/C7.java", "package pkg.m7;\npublic class C7 { }", M7)
highlight("test.java", """
import pkg.m2.C2;
import pkg.m2.*;
import <error descr="The module 'M2' does not export the package 'pkg.m2.impl' to the module 'M'">pkg.m2.impl.C2Impl</error>;
import <error descr="The module 'M2' does not export the package 'pkg.m2.impl' to the module 'M'">pkg.m2.impl</error>.*;
import <error descr="A named module cannot access packages of an unnamed one">pkg.m4.C4</error>;
import <error descr="The module 'M' does not have the module 'M5' in requirements">pkg.m5.C5</error>;
import pkg.m7.C7;
import pkg.lib1.LC1;
import <error descr="The module 'lib.named' does not export the package 'pkg.lib1.impl' to the module 'M'">pkg.lib1.impl.LC1Impl</error>;
import <error descr="The module 'lib.named' does not export the package 'pkg.lib1.impl' to the module 'M'">pkg.lib1.impl</error>.*;
import pkg.lib2.LC2;
import pkg.lib2.impl.LC2Impl;
import static <error descr="The module 'M2' does not export the package 'pkg.m2.impl' to the module 'M'">pkg.m2.impl.C2Impl</error>.make;
/** See also {@link C2Impl#make} */
class C {{
<error descr="The module 'M2' does not export the package 'pkg.m2.impl' to the module 'M'">C2Impl</error>.make();
<error descr="The module 'M2' does not export the package 'pkg.m2.impl' to the module 'M'">pkg.m2.impl.C2Impl</error>.make();
}}
""".trimIndent(), true)
}
fun testLinearModuleGraphBug() {
addFile("module-info.java", "module M6 { requires M7; }", M6)
addFile("module-info.java", "module M7 { }", M7)
@@ -308,8 +382,8 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
//<editor-fold desc="Helpers.">
private fun highlight(text: String) = highlight("module-info.java", text)
private fun highlight(path: String, text: String) {
myFixture.configureFromExistingVirtualFile(addFile(path, text))
private fun highlight(path: String, text: String, isTest: Boolean = false) {
myFixture.configureFromExistingVirtualFile(if (isTest) addTestFile(path, text) else addFile(path, text))
myFixture.checkHighlighting()
}