[java] package accessibility check for library classes

This commit is contained in:
Roman Shevchenko
2016-10-13 16:57:54 +02:00
parent ccec504ffb
commit 35be5f5b7b
5 changed files with 52 additions and 34 deletions
@@ -152,11 +152,14 @@ public class JavaModuleGraphUtil {
}
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;
Collection<PsiJavaModule> nodes = myGraph.getNodes();
if (nodes.contains(destination) && nodes.contains(source)) {
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;
@@ -55,16 +55,13 @@ 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);
static PsiJavaModule getModuleDescriptor(@NotNull PsiFileSystemItem fsItem) {
VirtualFile file = fsItem.getVirtualFile();
if (file == null) return null;
Project project = element.getProject();
Project project = fsItem.getProject();
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
if (element instanceof PsiCompiledElement) {
if (index.isInLibraryClasses(file)) {
return Optional.ofNullable(index.getClassRootForFile(file))
.map(r -> r.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE))
.map(PsiManager.getInstance(project)::findFile)
@@ -96,7 +93,7 @@ public class ModuleHighlightUtil {
@Nullable
static HighlightInfo checkFileDuplicates(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
Module module = ModuleUtilCore.findModuleForPsiElement(element);
Module module = findModule(file);
if (module != null) {
Project project = file.getProject();
Collection<VirtualFile> others = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
@@ -173,12 +170,12 @@ public class ModuleHighlightUtil {
}
}
Module host = ModuleUtilCore.findModuleForPsiElement(module);
Module host = findModule(module);
for (PsiProvidesStatement statement : psiTraverser().children(module).filter(PsiProvidesStatement.class)) {
PsiJavaCodeReferenceElement ref = statement.getInterfaceReference();
if (ref != null) {
PsiElement target = ref.resolve();
if (target instanceof PsiClass && ModuleUtilCore.findModuleForPsiElement(target) == host) {
if (target instanceof PsiClass && findModule(target) == host) {
String className = refText(ref), packageName = StringUtil.getPackageName(className);
if (!exports.contains(packageName) && !uses.contains(className)) {
String message = JavaErrorMessages.message("module.service.unused");
@@ -243,7 +240,7 @@ public class ModuleHighlightUtil {
if (refElement != null) {
PsiElement target = refElement.resolve();
if (target instanceof PsiPackage) {
Module module = ModuleUtilCore.findModuleForPsiElement(refElement);
Module module = findModule(refElement);
if (module != null) {
String packageName = ((PsiPackage)target).getQualifiedName();
PsiDirectory[] directories = ((PsiPackage)target).getDirectories(module.getModuleScope(false));
@@ -339,12 +336,12 @@ public class ModuleHighlightUtil {
static HighlightInfo checkPackageAccessibility(@NotNull PsiJavaCodeReferenceElement ref,
@NotNull PsiElement target,
@NotNull PsiJavaModule refModule) {
Module module = ModuleUtilCore.findModuleForPsiElement(ref);
Module module = findModule(refModule);
if (module != null) {
if (target instanceof PsiClass && !(target instanceof PsiCompiledElement) && module != ModuleUtilCore.findModuleForPsiElement(target)) {
if (target instanceof PsiClass) {
PsiElement targetFile = target.getParent();
if (targetFile instanceof PsiClassOwner) {
PsiJavaModule targetModule = getModuleDescriptor(target);
PsiJavaModule targetModule = getModuleDescriptor((PsiFileSystemItem)targetFile);
String packageName = ((PsiClassOwner)targetFile).getPackageName();
return checkPackageAccessibility(ref, refModule, targetModule, packageName);
}
@@ -353,7 +350,7 @@ public class ModuleHighlightUtil {
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) {
if (dirs.length == 1) {
PsiJavaModule targetModule = getModuleDescriptor(dirs[0]);
String packageName = ((PsiPackage)target).getQualifiedName();
return checkPackageAccessibility(ref, refModule, targetModule, packageName);
@@ -369,26 +366,35 @@ public class ModuleHighlightUtil {
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();
}
if (!refModule.equals(targetModule)) {
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) && !JavaModuleGraphUtil.exports(targetModule, packageName, refModule)) {
String message = JavaErrorMessages.message("module.package.not.exported", requiredName, packageName, refModuleName);
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
}
String refModuleName = refModule.getModuleName();
String requiredName = targetModule.getModuleName();
if (!JavaModuleGraphUtil.exports(targetModule, packageName, refModule)) {
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();
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 Module findModule(PsiElement element) {
return Optional.ofNullable(element.getContainingFile())
.map(PsiFile::getVirtualFile)
.map(f -> ModuleUtilCore.findModuleForFile(f, element.getProject()))
.orElse(null);
}
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());
Binary file not shown.
@@ -134,7 +134,7 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
}
fun testPackageAccessibility() {
addFile("module-info.java", "module M { requires M2; requires M6; }")
addFile("module-info.java", "module M { requires M2; requires M6; requires LIB1; }")
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)
@@ -154,6 +154,10 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
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 'LIB1' does not export the package 'pkg.lib1.impl' to the module 'M'">pkg.lib1.impl.LC1Impl</error>;
import <error descr="The module 'LIB1' does not export the package 'pkg.lib1.impl' to the module 'M'">pkg.lib1.impl</error>.*;
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 { }
@@ -15,6 +15,7 @@
*/
package com.intellij.testFramework.fixtures
import com.intellij.openapi.application.ex.PathManagerEx
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
@@ -49,6 +50,7 @@ object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() {
override fun setUpProject(project: Project, handler: SetupHandler) {
super.setUpProject(project, handler)
runWriteAction {
val main = ModuleManager.getInstance(project).findModuleByName(TEST_MODULE_NAME)!!
@@ -68,6 +70,9 @@ object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() {
val m7 = makeModule(project, ModuleDescriptor.M7)
ModuleRootModificationUtil.addDependency(m6, m7, DependencyScope.COMPILE, true)
val libDir = "jar://${PathManagerEx.getTestDataPath()}/codeInsight/jigsaw/"
ModuleRootModificationUtil.addModuleLibrary(main, libDir + "lib1.jar!/")
}
}