[java jpms] Consider file system case sensitivity when processing JDK modules IDEA-371051

GitOrigin-RevId: e8f7ac3f9d2a3b10a1bd92173cae966d30ee2bc9
This commit is contained in:
Aleksey Dobrynin
2025-05-07 15:47:27 +00:00
committed by intellij-monorepo-bot
parent bc94355e67
commit dbeeca52b4
2 changed files with 55 additions and 8 deletions
@@ -6,6 +6,8 @@ import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.roots.JdkOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.pom.java.JavaFeature
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightJavaModule
@@ -170,7 +172,7 @@ data class JpmsModuleAccessInfo(val current: JpmsModuleInfo.CurrentModuleInfo, v
val currentJpsModule = current.jpsModule ?: return false
return inAddedExports(currentJpsModule, targetModule.name, target.packageName, current.name)
}
fun isAccessible(): Boolean {
val currentModule = current.module ?: return false
val targetModule = target.module ?: return false
@@ -210,11 +212,11 @@ data class JpmsModuleAccessInfo(val current: JpmsModuleInfo.CurrentModuleInfo, v
}
private fun isJdkModule(jpsModule: Module, psiModule: PsiJavaModule): Boolean {
val sdkHomePath = ModuleRootManager.getInstance(jpsModule).getSdk()?.homePath?.replace('\\', '/')
val moduleFilePath = psiModule.containingFile?.virtualFile?.path?.replace('\\', '/')
val sdkHomePath = toLocalVirtualFile(ModuleRootManager.getInstance(jpsModule).getSdk()?.homeDirectory)
val moduleFilePath = toLocalVirtualFile(psiModule.containingFile?.virtualFile)
if (sdkHomePath != null && moduleFilePath != null) {
return moduleFilePath.startsWith("$sdkHomePath!") ||
moduleFilePath.startsWith(if (sdkHomePath.last() == '/') sdkHomePath else "$sdkHomePath/")
return VfsUtilCore.isAncestor(sdkHomePath, moduleFilePath, false)
}
else {
return psiModule.name.startsWith("java.") ||
@@ -222,6 +224,11 @@ data class JpmsModuleAccessInfo(val current: JpmsModuleInfo.CurrentModuleInfo, v
}
}
private fun toLocalVirtualFile(file: VirtualFile?): VirtualFile? {
if (file == null) return null
return VfsUtilCore.getVirtualFileForJar(file) ?: file
}
private fun inSameMultiReleaseModule(current: JpmsModuleInfo, target: JpmsModuleInfo): Boolean {
val placeModule = current.jpsModule ?: return false
val targetModule = target.jpsModule ?: return false
@@ -289,7 +296,7 @@ data class JpmsModuleAccessInfo(val current: JpmsModuleInfo.CurrentModuleInfo, v
}
private fun isUnnamedModule(module: PsiJavaModule?) = module == null || module is LightJavaModule
companion object {
const val ALL_UNNAMED: String = "ALL-UNNAMED"
const val ALL_SYSTEM: String = "ALL-SYSTEM"
@@ -14,6 +14,7 @@ import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescripto
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
import com.intellij.java.workspace.entities.JavaModuleSettingsEntity
import com.intellij.java.workspace.entities.javaSettings
import com.intellij.mock.MockLocalFileSystem
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.application.runWriteActionAndWait
@@ -30,6 +31,7 @@ import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.LocalFileSystem
@@ -51,6 +53,7 @@ import com.intellij.psi.util.PsiUtilCore
import com.intellij.testFramework.DumbModeTestUtils
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.VfsTestUtil
import com.intellij.testFramework.replaceService
import com.intellij.testFramework.workspaceModel.updateProjectModel
import com.intellij.util.ThrowableRunnable
import junit.framework.AssertionFailedError
@@ -1030,6 +1033,18 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
}
}
fun testIsJdkModuleCaseInsensitiveForWindows() {
withCaseInsensitiveFs {
withInternalJdk(INTERNAL_MAIN, LanguageLevel.JDK_11, true) {
highlight("Main.java", """
public class Main {
private javax.smartcardio.ATR attr;
}
""".trimIndent(), INTERNAL_MAIN)
}
}
}
fun testMultiReleaseJarWithDifferentJavaVersions() {
val location = JavaTestUtil.getJavaTestDataPath() + "/codeInsight/jigsaw/multi-release.jar"
val libraryFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(location))!!
@@ -1102,15 +1117,17 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
assertThat(available).describedAs(availableIntentions.toString()).containsExactlyInAnyOrder(*fixes)
}
private fun withInternalJdk(moduleDescriptor: ModuleDescriptor, level: LanguageLevel, block: () -> Unit) {
private fun withInternalJdk(moduleDescriptor: ModuleDescriptor, level: LanguageLevel, caseInsensitive: Boolean = false, block: () -> Unit) {
val name = "INTERNAL_JDK_TEST"
val module = ModuleManager.getInstance(project).findModuleByName(moduleDescriptor.moduleName)!!
try {
WriteAction.runAndWait<RuntimeException?>(ThrowableRunnable {
var path = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().getHomePath()!!
if (caseInsensitive) path = breakPath(path)
val jdk = ProjectJdkTable.getInstance().findJdk(name)
?: JavaSdk.getInstance().createJdk(name, JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().getHomePath()!!, false)
?: JavaSdk.getInstance().createJdk(name, path, false)
ProjectJdkTable.getInstance().addJdk(jdk, project)
ModuleRootModificationUtil.setModuleSdk(module, jdk)
@@ -1131,6 +1148,29 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
}
}
private fun breakPath(path: String): String {
val lastLetterIndex = path.indexOfLast { char -> char.isLetter() }
if (lastLetterIndex == -1) return path
val targetChar = path[lastLetterIndex]
val modifiedChar = if (targetChar.isUpperCase()) targetChar.lowercaseChar() else targetChar.uppercaseChar()
return path.replaceRange(lastLetterIndex, lastLetterIndex + 1, modifiedChar.toString())
}
private fun withCaseInsensitiveFs(action: () -> Unit) {
val mockFs = object : MockLocalFileSystem() {
override fun isCaseSensitive() = false
}
ApplicationManager.getApplication().replaceService(LocalFileSystem::class.java, mockFs, testRootDisposable)
try {
action()
}
finally {
Disposer.dispose(testRootDisposable)
}
}
private fun ModuleDescriptor.createSourceRoot(srcPathPrefix: String): VirtualFile? {
val module = ModuleManager.getInstance(project).findModuleByName(moduleName) ?: return null
val dummyRoot = VirtualFileManager.getInstance().findFileByUrl("temp:///") ?: return null