From e66362d4cce7b2f316f3c0954fafd20609be7872 Mon Sep 17 00:00:00 2001 From: Dmitry Kichinsky Date: Sat, 1 Feb 2025 10:35:48 +0100 Subject: [PATCH] [maven] IDEA-255308 fix "The service implementation must be defined in the same module" error for multi-release modules GitOrigin-RevId: ca9fbd1ea7611357844fdf3c77a731e0e911c651 --- .../impl/analysis/ModuleHighlightUtil.java | 4 +- .../daemon/impl/analysis/MultiReleaseUtil.kt | 42 +++++++++++++++++++ .../psi/impl/JavaPlatformModuleSystem.kt | 34 +-------------- 3 files changed, 46 insertions(+), 34 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/MultiReleaseUtil.kt diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java index c07b0acda213..5745477bf658 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java @@ -425,7 +425,9 @@ final class ModuleHighlightUtil { PsiElement implTarget = implRef.resolve(); if (implTarget instanceof PsiClass implClass) { - if (ModuleUtilCore.findModuleForFile(file) != ModuleUtilCore.findModuleForFile(implClass.getContainingFile())) { + Module fileModule = ModuleUtilCore.findModuleForFile(file); + Module implModule = ModuleUtilCore.findModuleForFile(implClass.getContainingFile()); + if (fileModule != implModule && !MultiReleaseUtil.inSameMultiReleaseModule(implModule, fileModule)) { String message = JavaErrorBundle.message("module.service.alien"); HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/MultiReleaseUtil.kt b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/MultiReleaseUtil.kt new file mode 100644 index 000000000000..e0c4afd785ac --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/MultiReleaseUtil.kt @@ -0,0 +1,42 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +@file:JvmName("MultiReleaseUtil") +package com.intellij.codeInsight.daemon.impl.analysis + +import com.intellij.openapi.module.Module +import com.intellij.platform.backend.workspace.workspaceModel +import com.intellij.platform.workspace.jps.entities.ModuleId +import com.intellij.platform.workspace.jps.entities.exModuleOptions +import org.jetbrains.annotations.ApiStatus.Internal +import org.jetbrains.jps.model.serialization.SerializationConstants.MAVEN_EXTERNAL_SOURCE_ID +import java.util.regex.Pattern + +private const val MAIN = "main" +private val javaVersionPattern: Pattern by lazy { Pattern.compile("java\\d+") } + +@Internal +fun inSameMultiReleaseModule(mainModule: Module, additionalModule: Module): Boolean { + // Maven + val project = additionalModule.project + val storage = project.workspaceModel.currentSnapshot + val placeModuleName = additionalModule.name + val targetModuleName = mainModule.name + val placeModuleExOptions = storage.resolve(ModuleId(placeModuleName))?.exModuleOptions + val targetModuleExOptions = storage.resolve(ModuleId(targetModuleName))?.exModuleOptions + if (placeModuleExOptions?.externalSystem == MAVEN_EXTERNAL_SOURCE_ID + && targetModuleExOptions?.externalSystem == MAVEN_EXTERNAL_SOURCE_ID) { + val baseModuleName = targetModuleName.substringBeforeLast('.') + if (placeModuleName.startsWith(baseModuleName) + && placeModuleExOptions.externalSystemModuleType == "MAIN_ONLY_ADDITIONAL" // StandardMavenModuleType.MAIN_ONLY_ADDITIONAL + && targetModuleExOptions.externalSystemModuleType == "MAIN_ONLY" // StandardMavenModuleType.MAIN_ONLY + ) { + return true + } + } + + // Gradle + if (targetModuleName.endsWith(".$MAIN")) { + val baseModuleName = targetModuleName.substringBeforeLast(MAIN) + return javaVersionPattern.matcher(placeModuleName.substringAfter(baseModuleName)).matches() + } + return false +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt b/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt index 3077cb882a22..b48e2f66c874 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt +++ b/java/java-impl/src/com/intellij/psi/impl/JavaPlatformModuleSystem.kt @@ -28,10 +28,7 @@ import com.intellij.openapi.roots.JdkOrderEntry import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.roots.ProjectRootManager -import com.intellij.platform.backend.workspace.workspaceModel import com.intellij.platform.eel.provider.utils.EelPathUtils -import com.intellij.platform.workspace.jps.entities.ModuleId -import com.intellij.platform.workspace.jps.entities.exModuleOptions import com.intellij.pom.java.JavaFeature import com.intellij.psi.* import com.intellij.psi.JavaModuleSystem.* @@ -43,16 +40,11 @@ import com.intellij.util.concurrency.AppExecutorUtil import com.intellij.util.containers.CollectionFactory import com.intellij.util.indexing.DumbModeAccessType import org.jetbrains.annotations.NonNls -import org.jetbrains.jps.model.serialization.SerializationConstants.MAVEN_EXTERNAL_SOURCE_ID import java.nio.file.Path import java.util.concurrent.CompletableFuture import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException -import java.util.regex.Pattern - -private const val MAIN = "main" -private val javaVersionPattern: Pattern by lazy { Pattern.compile("java\\d+") } /** * Checks package accessibility according to JLS 7 "Packages and Modules". @@ -309,31 +301,7 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx { private fun inSameMultiReleaseModule(current: ModuleInfo, target: ModuleInfo): Boolean { val placeModule = current.jpsModule ?: return false val targetModule = target.jpsModule ?: return false - - // Maven - val project = placeModule.project - val storage = project.workspaceModel.currentSnapshot - val placeModuleName = placeModule.name - val targetModuleName = targetModule.name - val placeModuleExOptions = storage.resolve(ModuleId(placeModuleName))?.exModuleOptions - val targetModuleExOptions = storage.resolve(ModuleId(targetModuleName))?.exModuleOptions - if (placeModuleExOptions?.externalSystem == MAVEN_EXTERNAL_SOURCE_ID - && targetModuleExOptions?.externalSystem == MAVEN_EXTERNAL_SOURCE_ID) { - val baseModuleName = targetModuleName.substringBeforeLast('.') - if (placeModuleName.startsWith(baseModuleName) - && placeModuleExOptions.externalSystemModuleType == "MAIN_ONLY_ADDITIONAL" // StandardMavenModuleType.MAIN_ONLY_ADDITIONAL - && targetModuleExOptions.externalSystemModuleType == "MAIN_ONLY" // StandardMavenModuleType.MAIN_ONLY - ) { - return true - } - } - - // Gradle - if (targetModuleName.endsWith(".$MAIN")) { - val baseModuleName = targetModuleName.substringBeforeLast(MAIN) - return javaVersionPattern.matcher(placeModuleName.substringAfter(baseModuleName)).matches() - } - return false + return com.intellij.codeInsight.daemon.impl.analysis.inSameMultiReleaseModule(targetModule, placeModule) } private fun detectAutomaticModule(current: ModuleInfo): PsiJavaModule? {