From 09c1115ff9dd7cb0e3e5bd7e5d74c4b902cad143 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 25 Aug 2017 13:06:56 +0300 Subject: [PATCH] Report errors on usages of unnamed module from named modules in IDE #KT-19493 Fixed --- .../idea/modules/IdeJavaModuleResolver.kt | 21 +--- .../idea/modules/ModuleHighlightUtil2.java | 113 ++++++++++++++++++ .../java9/namedDependsOnUnnamed/main/usage.kt | 6 +- 3 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/ModuleHighlightUtil2.java diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt index 6c6e7173264..18e395e2899 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt @@ -26,31 +26,22 @@ import org.jetbrains.annotations.NotNull import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver -class IdeJavaModuleResolver(project: Project) : JavaModuleResolver { - private val psiManager = PsiManager.getInstance(project) - - private fun findJavaModule(file: VirtualFile): PsiJavaModule? { - return psiManager.findFile(file)?.let(JavaModuleGraphUtil::findDescriptorByElement) - } +class IdeJavaModuleResolver(private val project: Project) : JavaModuleResolver { + private fun findJavaModule(file: VirtualFile): PsiJavaModule? = + ModuleHighlightUtil2.getModuleDescriptor(file, project) override fun checkAccessibility( fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName? ): JavaModuleResolver.AccessError? { val ourModule = fileFromOurModule?.let(this::findJavaModule) - val theirModule = this.findJavaModule(referencedFile) + val theirModule = findJavaModule(referencedFile) - // If we're both in the unnamed module, it's OK, no error should be reported - if (ourModule == null && theirModule == null) return null + if (ourModule?.name == theirModule?.name) return null if (theirModule == null) { - // We should probably prohibit this usage according to JPMS (named module cannot use types from unnamed module), - // but we cannot be sure that a module without module-info.java is going to be actually used as an unnamed module. - // It could also be an automatic module, in which case it would be read by every module. - return null + return JavaModuleResolver.AccessError.ModuleDoesNotReadUnnamedModule } - if (ourModule?.name == theirModule.name) return null - if (ourModule != null && !JavaModuleGraphUtil.reads(ourModule, theirModule)) { return JavaModuleResolver.AccessError.ModuleDoesNotReadModule(theirModule.name) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/ModuleHighlightUtil2.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/ModuleHighlightUtil2.java new file mode 100644 index 00000000000..c9ee3a63e49 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/ModuleHighlightUtil2.java @@ -0,0 +1,113 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.modules; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.PsiJavaModule; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.light.LightJavaModule; +import com.intellij.psi.search.FilenameIndex; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Collectors; + +import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE; + +// Copied from com.intellij.codeInsight.daemon.impl.analysis.ModuleHighlightUtil +public class ModuleHighlightUtil2 { + private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release"); + + @Nullable + static PsiJavaModule getModuleDescriptor(@NotNull VirtualFile file, @NotNull Project project) { + ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project); + if (index.isInLibrary(file)) { + VirtualFile root; + if ((root = index.getClassRootForFile(file)) != null) { + VirtualFile descriptorFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE); + if (descriptorFile == null) { + VirtualFile alt = root.findFileByRelativePath("META-INF/versions/9/" + PsiJavaModule.MODULE_INFO_CLS_FILE); + if (alt != null && isMultiReleaseJar(root)) { + descriptorFile = alt; + } + } + if (descriptorFile != null) { + PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile); + if (psiFile instanceof PsiJavaFile) { + return ((PsiJavaFile) psiFile).getModuleDeclaration(); + } + } + else if (root.getFileSystem() instanceof JarFileSystem && "jar".equalsIgnoreCase(root.getExtension())) { + return LightJavaModule.getModule(PsiManager.getInstance(project), root); + } + } + else if ((root = index.getSourceRootForFile(file)) != null) { + VirtualFile descriptorFile = root.findChild(MODULE_INFO_FILE); + if (descriptorFile != null) { + PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile); + if (psiFile instanceof PsiJavaFile) { + return ((PsiJavaFile) psiFile).getModuleDeclaration(); + } + } + } + } + else { + Module module = index.getModuleForFile(file); + if (module != null) { + boolean isTest = index.isInTestSourceContent(file); + List 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; + } + + private static boolean isMultiReleaseJar(VirtualFile root) { + if (root.getFileSystem() instanceof JarFileSystem) { + VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME); + if (manifest != null) { + try (InputStream stream = manifest.getInputStream()) { + return Boolean.valueOf(new Manifest(stream).getMainAttributes().getValue(MULTI_RELEASE)); + } + catch (IOException ignored) { + } + } + } + + return false; + } +} diff --git a/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt index a03370a901c..7a7e5a71945 100644 --- a/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt +++ b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt @@ -1,6 +1,6 @@ -import dependency.J +import dependency.J fun usage(): String { - val j: J = J() - return j.toString() + val j: J = J() + return j.toString() }