mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java] Jigsaw package accessibility highlighting (source dependencies)
This commit is contained in:
+23
-17
@@ -65,6 +65,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private JavaSdkVersion myJavaSdkVersion;
|
||||
|
||||
@SuppressWarnings("StatefulEp") private PsiFile myFile;
|
||||
@SuppressWarnings("StatefulEp") private PsiJavaModule myJavaModule;
|
||||
|
||||
// map codeBlock->List of PsiReferenceExpression of uninitialized final variables
|
||||
private final Map<PsiElement, Collection<PsiReferenceExpression>> myUninitializedVarProblems = new THashMap<>();
|
||||
@@ -155,13 +156,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
|
||||
@Override
|
||||
public boolean analyze(@NotNull PsiFile file, boolean updateWholeFile, @NotNull HighlightInfoHolder holder, @NotNull Runnable highlight) {
|
||||
myFile = file;
|
||||
myHolder = Holder.CHECK_ELEMENT_LEVEL ? new CheckLevelHighlightInfoHolder(file, holder) : holder;
|
||||
|
||||
boolean success = true;
|
||||
try {
|
||||
myLanguageLevel = PsiUtil.getLanguageLevel(file);
|
||||
myJavaSdkVersion = notNull(JavaVersionService.getInstance().getJavaSdkVersion(file), JavaSdkVersion.fromLanguageLevel(myLanguageLevel));
|
||||
prepare(Holder.CHECK_ELEMENT_LEVEL ? new CheckLevelHighlightInfoHolder(file, holder) : holder, file);
|
||||
if (updateWholeFile) {
|
||||
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (progress == null) throw new IllegalStateException("Must be run under progress");
|
||||
@@ -193,6 +190,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
myReassignedParameters.clear();
|
||||
|
||||
myRefCountHolder = null;
|
||||
myJavaModule = null;
|
||||
myFile = null;
|
||||
myHolder = null;
|
||||
myDuplicateMethods.clear();
|
||||
@@ -202,6 +200,18 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
return success;
|
||||
}
|
||||
|
||||
protected void prepareToRunAsInspection(@NotNull HighlightInfoHolder holder) {
|
||||
prepare(holder, holder.getContextFile());
|
||||
}
|
||||
|
||||
private void prepare(HighlightInfoHolder holder, PsiFile file) {
|
||||
myHolder = holder;
|
||||
myFile = file;
|
||||
myLanguageLevel = PsiUtil.getLanguageLevel(file);
|
||||
myJavaSdkVersion = notNull(JavaVersionService.getInstance().getJavaSdkVersion(file), JavaSdkVersion.fromLanguageLevel(myLanguageLevel));
|
||||
myJavaModule = myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_9) ? ModuleHighlightUtil.getModuleDescriptor(file) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (myRefCountHolder != null && myFile instanceof ServerPageFile) {
|
||||
@@ -1030,8 +1040,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
|
||||
@Override
|
||||
public void visitReferenceElement(PsiJavaCodeReferenceElement ref) {
|
||||
JavaResolveResult resolveResult = doVisitReferenceElement(ref);
|
||||
if (resolveResult != null && !myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkRawOnParameterizedType(ref, resolveResult.getElement()));
|
||||
JavaResolveResult result = doVisitReferenceElement(ref);
|
||||
if (result != null) {
|
||||
PsiElement resolved = result.getElement();
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkRawOnParameterizedType(ref, resolved));
|
||||
if (!myHolder.hasErrorResults() && resolved != null && myJavaModule != null) {
|
||||
myHolder.add(ModuleHighlightUtil.checkPackageAccessibility(ref, resolved, myJavaModule));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JavaResolveResult doVisitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
@@ -1688,14 +1704,4 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private HighlightInfo checkFeature(@NotNull PsiElement element, @NotNull Feature feature) {
|
||||
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
|
||||
}
|
||||
|
||||
protected void prepareToRunAsInspection(@NotNull HighlightInfoHolder holder) {
|
||||
PsiFile file = holder.getContextFile();
|
||||
JavaSdkVersion sdkVersion = JavaVersionService.getInstance().getJavaSdkVersion(file);
|
||||
|
||||
myHolder = holder;
|
||||
myFile = file;
|
||||
myLanguageLevel = PsiUtil.getLanguageLevel(file);
|
||||
myJavaSdkVersion = sdkVersion != null ? sdkVersion : JavaSdkVersion.fromLanguageLevel(myLanguageLevel);
|
||||
}
|
||||
}
|
||||
+99
-3
@@ -24,10 +24,12 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.util.CachedValueProvider.Result;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -49,6 +51,13 @@ public class JavaModuleGraphUtil {
|
||||
return ContainerUtil.find(cycles, set -> set.contains(module));
|
||||
}
|
||||
|
||||
public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) {
|
||||
Project project = source.getProject();
|
||||
RequiresGraph graph = CachedValuesManager.getManager(project).getCachedValue(project, () ->
|
||||
Result.create(ReadAction.compute(() -> buildRequiresGraph(project)), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
|
||||
return graph.reads(source, destination);
|
||||
}
|
||||
|
||||
// Looks for cycles between Java modules in the project sources.
|
||||
// Library/JDK modules are excluded - in assumption there can't be any lib -> src dependencies.
|
||||
// Module references are resolved "globally" (i.e., without taking project dependencies into account).
|
||||
@@ -65,15 +74,15 @@ public class JavaModuleGraphUtil {
|
||||
|
||||
if (!projectModules.isEmpty()) {
|
||||
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
|
||||
for (PsiJavaModule moduleDeclaration : projectModules) {
|
||||
for (PsiRequiresStatement statement : psiTraverser().children(moduleDeclaration).filter(PsiRequiresStatement.class)) {
|
||||
for (PsiJavaModule module : projectModules) {
|
||||
for (PsiRequiresStatement statement : psiTraverser().children(module).filter(PsiRequiresStatement.class)) {
|
||||
Optional.ofNullable(statement.getReferenceElement())
|
||||
.map(PsiJavaModuleReferenceElement::getReference)
|
||||
.map(ref -> ref.multiResolve(true))
|
||||
.map(a -> a.length == 1 ? a[0].getElement() : null)
|
||||
.map(e -> e instanceof PsiJavaModule ? (PsiJavaModule)e : null)
|
||||
.filter(projectModules::contains)
|
||||
.ifPresent(dependency -> relations.putValue(moduleDeclaration, dependency));
|
||||
.ifPresent(dependency -> relations.putValue(module, dependency));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +99,74 @@ public class JavaModuleGraphUtil {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Starting from source modules, collects all module dependencies in the project.
|
||||
// The resulting graph is used for tracing readability.
|
||||
private static RequiresGraph buildRequiresGraph(Project project) {
|
||||
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
|
||||
Set<String> publicEdges = ContainerUtil.newTroveSet();
|
||||
for (Module module : ModuleManager.getInstance(project).getModules()) {
|
||||
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
|
||||
Optional.ofNullable(ContainerUtil.getFirstItem(files))
|
||||
.map(PsiManager.getInstance(project)::findFile)
|
||||
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
|
||||
.ifPresent(m -> visit(m, relations, publicEdges));
|
||||
}
|
||||
|
||||
GraphGenerator<PsiJavaModule> graph = GraphGenerator.create(new RequiresSemiGraph(relations));
|
||||
return new RequiresGraph(graph, publicEdges);
|
||||
}
|
||||
|
||||
private static void visit(PsiJavaModule module, MultiMap<PsiJavaModule, PsiJavaModule> relations, Set<String> publicEdges) {
|
||||
if (!relations.containsKey(module)) {
|
||||
relations.putValues(module, Collections.emptyList());
|
||||
for (PsiRequiresStatement statement : psiTraverser().children(module).filter(PsiRequiresStatement.class)) {
|
||||
Optional.ofNullable(statement.getReferenceElement())
|
||||
.map(PsiJavaModuleReferenceElement::getReference)
|
||||
.map(PsiReference::resolve)
|
||||
.map(e -> e instanceof PsiJavaModule ? (PsiJavaModule)e : null)
|
||||
.ifPresent(dependency -> {
|
||||
relations.putValue(module, dependency);
|
||||
if (isPublic(statement)) publicEdges.add(RequiresGraph.key(dependency, module));
|
||||
visit(dependency, relations, publicEdges);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPublic(PsiRequiresStatement statement) {
|
||||
for (PsiElement child = statement.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (PsiUtil.isJavaToken(child, JavaTokenType.PUBLIC_KEYWORD)) return true;
|
||||
if (child instanceof PsiJavaModuleReferenceElement) break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class RequiresGraph {
|
||||
private final Graph<PsiJavaModule> myGraph;
|
||||
private final Set<String> myPublicEdges;
|
||||
|
||||
public RequiresGraph(Graph<PsiJavaModule> graph, Set<String> publicEdges) {
|
||||
myGraph = graph;
|
||||
myPublicEdges = publicEdges;
|
||||
}
|
||||
|
||||
public boolean reads(PsiJavaModule source, PsiJavaModule destination) {
|
||||
Iterator<PsiJavaModule> directReaders = myGraph.getOut(destination);
|
||||
while (directReaders.hasNext()) {
|
||||
PsiJavaModule next = directReaders.next();
|
||||
if (source.equals(next) || myPublicEdges.contains(key(destination, next)) && reads(source, next)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String key(PsiJavaModule module, PsiJavaModule exporter) {
|
||||
return module.getModuleName() + '/' + exporter.getModuleName();
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold desc="Helpers.">
|
||||
private static class SourceSemiGraph implements Graph<PsiJavaModule> {
|
||||
private final MultiMap<PsiJavaModule, PsiJavaModule> myMap;
|
||||
|
||||
@@ -112,4 +189,23 @@ public class JavaModuleGraphUtil {
|
||||
return myMap.get(n).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RequiresSemiGraph implements GraphGenerator.SemiGraph<PsiJavaModule> {
|
||||
private final MultiMap<PsiJavaModule, PsiJavaModule> myMap;
|
||||
|
||||
public RequiresSemiGraph(MultiMap<PsiJavaModule, PsiJavaModule> map) {
|
||||
myMap = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PsiJavaModule> getNodes() {
|
||||
return myMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<PsiJavaModule> getIn(PsiJavaModule n) {
|
||||
return myMap.get(n).iterator();
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
}
|
||||
+92
@@ -54,6 +54,34 @@ import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
|
||||
import static com.intellij.psi.SyntaxTraverser.psiTraverser;
|
||||
|
||||
public class ModuleHighlightUtil {
|
||||
@Nullable
|
||||
static PsiJavaModule getModuleDescriptor(@NotNull PsiElement element) {
|
||||
VirtualFile file = Optional.of(element)
|
||||
.map(e -> e instanceof PsiFileSystemItem ? (PsiFileSystemItem)e : e.getContainingFile())
|
||||
.map(PsiFileSystemItem::getVirtualFile)
|
||||
.orElse(null);
|
||||
if (file == null) return null;
|
||||
|
||||
Project project = element.getProject();
|
||||
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
if (element instanceof PsiCompiledElement) {
|
||||
return Optional.ofNullable(index.getClassRootForFile(file))
|
||||
.map(r -> r.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE))
|
||||
.map(PsiManager.getInstance(project)::findFile)
|
||||
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
|
||||
.orElse(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(PsiManager.getInstance(project)::findFile)
|
||||
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkFileName(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
|
||||
if (!MODULE_INFO_FILE.equals(file.getName())) {
|
||||
@@ -307,6 +335,70 @@ public class ModuleHighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkPackageAccessibility(@NotNull PsiJavaCodeReferenceElement ref,
|
||||
@NotNull PsiElement target,
|
||||
@NotNull PsiJavaModule refModule) {
|
||||
Module module = ModuleUtilCore.findModuleForPsiElement(ref);
|
||||
if (module != null) {
|
||||
if (target instanceof PsiClass && !(target instanceof PsiCompiledElement) && module != ModuleUtilCore.findModuleForPsiElement(target)) {
|
||||
PsiElement targetFile = target.getParent();
|
||||
if (targetFile instanceof PsiClassOwner) {
|
||||
PsiJavaModule targetModule = getModuleDescriptor(target);
|
||||
String packageName = ((PsiClassOwner)targetFile).getPackageName();
|
||||
return checkPackageAccessibility(ref, refModule, targetModule, packageName);
|
||||
}
|
||||
}
|
||||
else if (target instanceof PsiPackage) {
|
||||
PsiElement refImport = ref.getParent();
|
||||
if (refImport instanceof PsiImportStatementBase && ((PsiImportStatementBase)refImport).isOnDemand()) {
|
||||
PsiDirectory[] dirs = ((PsiPackage)target).getDirectories(module.getModuleWithDependenciesAndLibrariesScope(false));
|
||||
if (dirs.length == 1 && ModuleUtilCore.findModuleForPsiElement(dirs[0]) != module) {
|
||||
PsiJavaModule targetModule = getModuleDescriptor(dirs[0]);
|
||||
String packageName = ((PsiPackage)target).getQualifiedName();
|
||||
return checkPackageAccessibility(ref, refModule, targetModule, packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HighlightInfo checkPackageAccessibility(PsiJavaCodeReferenceElement ref,
|
||||
PsiJavaModule refModule,
|
||||
PsiJavaModule targetModule,
|
||||
String packageName) {
|
||||
if (targetModule == null) {
|
||||
String message = JavaErrorMessages.message("module.package.on.classpath");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
|
||||
}
|
||||
|
||||
String refModuleName = refModule.getModuleName();
|
||||
String requiredName = targetModule.getModuleName();
|
||||
if (!(targetModule instanceof PsiCompiledElement) && !isExported(targetModule, packageName)) {
|
||||
String message = JavaErrorMessages.message("module.package.not.exported", requiredName, packageName, refModuleName);
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
|
||||
}
|
||||
|
||||
if (!(PsiJavaModule.JAVA_BASE.equals(requiredName) || JavaModuleGraphUtil.reads(refModule, targetModule))) {
|
||||
String message = JavaErrorMessages.message("module.not.in.requirements", refModuleName, requiredName);
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isExported(PsiJavaModule module, String packageName) {
|
||||
for (PsiExportsStatement statement : psiTraverser().children(module).filter(PsiExportsStatement.class)) {
|
||||
String exportedName = Optional.ofNullable(statement.getPackageReference()).map(ModuleHighlightUtil::refText).orElse("");
|
||||
if (packageName.equals(exportedName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static HighlightInfo moduleResolveError(PsiJavaModuleReferenceElement refElement, PsiPolyVariantReference ref) {
|
||||
boolean missing = ref.multiResolve(true).length == 0;
|
||||
String message = JavaErrorMessages.message(missing ? "module.not.found" : "module.not.on.path", refElement.getReferenceText());
|
||||
|
||||
@@ -26,6 +26,7 @@ public interface PsiJavaModule extends NavigatablePsiElement, PsiNamedElement, P
|
||||
String MODULE_INFO_CLASS = "module-info";
|
||||
String MODULE_INFO_FILE = MODULE_INFO_CLASS + ".java";
|
||||
String MODULE_INFO_CLS_FILE = MODULE_INFO_CLASS + ".class";
|
||||
String JAVA_BASE = "java.base";
|
||||
|
||||
@NotNull PsiJavaModuleReferenceElement getNameElement();
|
||||
@NotNull String getModuleName();
|
||||
|
||||
@@ -411,6 +411,9 @@ module.service.abstract=The service implementation is an abstract class: {0}
|
||||
module.service.no.ctor=The service implementation does not have a default constructor: {0}
|
||||
module.service.hidden.ctor=The default constructor of the service implementation is not public: {0}
|
||||
module.service.unused=Service interface provided but not exported or used
|
||||
module.package.not.exported=The module ''{0}'' does not export the package ''{1}'' to the module ''{2}''
|
||||
module.package.on.classpath=A named module cannot access packages of an unnamed one
|
||||
module.not.in.requirements=The module ''{0}'' does not have the module ''{1}'' in requirements
|
||||
|
||||
feature.generics=Generics
|
||||
feature.annotations=Annotations
|
||||
|
||||
@@ -17,8 +17,7 @@ package com.intellij.codeInsight.daemon
|
||||
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
|
||||
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.M2
|
||||
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.M3
|
||||
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
||||
@@ -136,6 +135,33 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
|
||||
fixes("module M { uses <caret>pkg.m3.C3; }", "AddModuleDependencyFix")
|
||||
}
|
||||
|
||||
fun testPackageAccessibility() {
|
||||
addFile("module-info.java", "module M { requires M2; requires M6; }")
|
||||
addFile("module-info.java", "module M2 { exports pkg.m2; }", 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 public 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 static <error descr="The module 'M2' does not export the package 'pkg.m2.impl' to the module 'M'">pkg.m2.impl.C2Impl</error>.make;
|
||||
|
||||
class C { }
|
||||
""".trimIndent(), true)
|
||||
}
|
||||
|
||||
//<editor-fold desc="Helpers.">
|
||||
private fun highlight(text: String, filter: Boolean = false) = highlight("module-info.java", text, filter)
|
||||
|
||||
|
||||
+21
-6
@@ -20,10 +20,7 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ContentEntry
|
||||
import com.intellij.openapi.roots.LanguageLevelModuleExtension
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.*
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.ex.temp.TempFileSystem
|
||||
@@ -32,13 +29,17 @@ import com.intellij.testFramework.IdeaTestUtil
|
||||
import com.intellij.testFramework.LightPlatformTestCase
|
||||
|
||||
/**
|
||||
* Dependencies: 'main' -> 'm2', 'm3'
|
||||
* Dependencies: 'main' -> 'm2', 'main' -> 'm4', 'main' -> 'm5', 'main' -> 'm6' => 'm7'
|
||||
*/
|
||||
object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() {
|
||||
enum class ModuleDescriptor(internal val moduleName: String, internal val rootName: String) {
|
||||
MAIN(TEST_MODULE_NAME, "/not_used/"),
|
||||
M2("${TEST_MODULE_NAME}_m2", "src_m2"),
|
||||
M3("${TEST_MODULE_NAME}_m3", "src_m3");
|
||||
M3("${TEST_MODULE_NAME}_m3", "src_m3"),
|
||||
M4("${TEST_MODULE_NAME}_m4", "src_m4"),
|
||||
M5("${TEST_MODULE_NAME}_m5", "src_m5"),
|
||||
M6("${TEST_MODULE_NAME}_m6", "src_m6"),
|
||||
M7("${TEST_MODULE_NAME}_m7", "src_m7");
|
||||
|
||||
fun root(): VirtualFile =
|
||||
if (this == MAIN) LightPlatformTestCase.getSourceRoot() else TempFileSystem.getInstance().findFileByPath("/$rootName")!!
|
||||
@@ -50,9 +51,23 @@ object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() {
|
||||
super.setUpProject(project, handler)
|
||||
runWriteAction {
|
||||
val main = ModuleManager.getInstance(project).findModuleByName(TEST_MODULE_NAME)!!
|
||||
|
||||
val m2 = makeModule(project, ModuleDescriptor.M2)
|
||||
ModuleRootModificationUtil.addDependency(main, m2)
|
||||
|
||||
makeModule(project, ModuleDescriptor.M3)
|
||||
|
||||
val m4 = makeModule(project, ModuleDescriptor.M4)
|
||||
ModuleRootModificationUtil.addDependency(main, m4)
|
||||
|
||||
val m5 = makeModule(project, ModuleDescriptor.M5)
|
||||
ModuleRootModificationUtil.addDependency(main, m5)
|
||||
|
||||
val m6 = makeModule(project, ModuleDescriptor.M6)
|
||||
ModuleRootModificationUtil.addDependency(main, m6)
|
||||
|
||||
val m7 = makeModule(project, ModuleDescriptor.M7)
|
||||
ModuleRootModificationUtil.addDependency(m6, m7, DependencyScope.COMPILE, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user