From f2adaef3e560e3e6a2bdd50e9473cb0bb40bc844 Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Sun, 19 Jul 2026 18:54:49 +0200 Subject: [PATCH] KTIJ-39616 IJ-CR-214556 [kotlin] Drop annotationProcessor when its kapt configuration is provided (cherry picked from commit aa1e58f65ae83363a70f3dea99b295c641ef783d) GitOrigin-RevId: 7ace2cb183b6f56563490065f4acb98c00df8bc8 --- .../KaptGradleProjectConfigurators.kt | 89 +++++++++++-------- .../after/build.gradle.kts | 1 - 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/configuration/KaptGradleProjectConfigurators.kt b/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/configuration/KaptGradleProjectConfigurators.kt index 889f925a4166..3700ec9926d5 100644 --- a/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/configuration/KaptGradleProjectConfigurators.kt +++ b/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/configuration/KaptGradleProjectConfigurators.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.idea.gradleCodeInsightCommon.GradleBuildScriptSuppor import org.jetbrains.kotlin.idea.gradleCodeInsightCommon.getBuildScriptPsiFile import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtPsiFactory @@ -77,45 +78,46 @@ private fun PsiFile.configureKaptDependenciesIfNeeded(changedFiles: ChangedConfi val dependenciesToAdd = processorDependencyMatches .distinctBy { it.kaptConfiguration to it.notation } .filterNot { KaptDependency(it.kaptConfiguration, it.notation) in kaptDependencies } - if (dependenciesToAdd.isEmpty()) return + val dependenciesToRemove = processorDependencyMatches + .filter { it.dropOriginal } - if (document == null) { - changedFiles.storeOriginalFileContent(this) - addKaptDependenciesToPsi(dependenciesToAdd) - return - } + if (dependenciesToAdd.isEmpty() && dependenciesToRemove.isEmpty()) return + + val dependenciesBlock = findTopLevelBlock("dependencies") ?: return - val lastProcessorMatch = dependenciesToAdd.last().match - val insertOffset = fileText.indexOf('\n', lastProcessorMatch.range.last + 1).takeIf { it >= 0 } ?: fileText.length - val indent = lastProcessorMatch.groupValues[1] - val dependencyLines = dependenciesToAdd.joinToString(separator = "") { dependency -> - "\n$indent${kaptDependencyNotation(dependency.kaptConfiguration, dependency.notation)}" - } changedFiles.storeOriginalFileContent(this) - document.insertString(insertOffset, dependencyLines) - psiDocumentManager.commitDocument(document) + dependenciesBlock.addDependencies(dependenciesToAdd) + dependenciesBlock.removeDependencies(dependenciesToRemove) + + val codeStyleManager = CodeStyleManager.getInstance(project) + codeStyleManager.reformat(dependenciesBlock, true) } -private fun KtFile.addKaptDependenciesToPsi(dependenciesToAdd: List) { - val dependenciesBlock = findTopLevelBlock("dependencies") ?: return +private fun KtBlockExpression.removeDependencies(removedDependencies: List) { + val existingDependencies = statements.associateBy { it.text } + removedDependencies.forEach { dependency: KaptProcessorDependency -> + val dependencyText = kaptDependencyNotation(dependency.dependencyConfiguration, dependency.notation) + val expression = existingDependencies[dependencyText] + expression?.delete() + } +} + +private fun KtBlockExpression.addDependencies(dependenciesToAdd: List) { val sourceDependencyTexts = dependenciesToAdd.map { it.match.value.trim() } - val lastSourceDependency = dependenciesBlock.statements.lastOrNull { statement -> + val lastSourceDependency = this.statements.lastOrNull { statement -> sourceDependencyTexts.any { StringUtil.equalsIgnoreWhitespaces(statement.text, it) } } ?: return val psiFactory = KtPsiFactory(project) var anchor: PsiElement = lastSourceDependency - val existingDependencyTexts = dependenciesBlock.statements.map { it.text } + val existingDependencyTexts = statements.map { it.text } - for ((_, kaptConfiguration, notation) in dependenciesToAdd) { - val kaptDependencyText = kaptDependencyNotation(kaptConfiguration, notation) - if (existingDependencyTexts.any { StringUtil.equalsIgnoreWhitespaces(it, kaptDependencyText) }) continue + for (dependency in dependenciesToAdd) { + val dependencyText = kaptDependencyNotation(dependency.kaptConfiguration, dependency.notation) + if (existingDependencyTexts.any { StringUtil.equalsIgnoreWhitespaces(it, dependencyText) }) continue - anchor = dependenciesBlock.addAfter(psiFactory.createExpression(kaptDependencyText), anchor) + anchor = addAfter(psiFactory.createExpression(dependencyText), anchor) .apply { addNewLinesIfNeeded() } } - - val codeStyleManager = CodeStyleManager.getInstance(project) - codeStyleManager.reformat(dependenciesBlock, true) } private fun KtFile.findTopLevelBlock(name: String): KtBlockExpression? = @@ -227,18 +229,20 @@ private fun String.isLombokDependencyNotation(): Boolean = split(':').getOrNull(1) == "lombok" || contains("lombok", ignoreCase = true) private fun MatchResult.toKaptProcessorDependency(): KaptProcessorDependency? { - val sourceConfiguration = GradleProcessorDependencyConfiguration.byName(groupValues[2]) ?: return null - val notation = groupValues[3] - if (notation.isLombokDependencyNotation()) return null - if (!sourceConfiguration.acceptsAnyProcessor) { - val processorPath = GradleProcessorPath.of(notation) ?: return null - if (processorPath !in KNOWN_PROCESSOR_ARTIFACTS) return null - } - return KaptProcessorDependency( - match = this, - kaptConfiguration = sourceConfiguration.kaptConfiguration, - notation = notation, - ) + val sourceConfiguration = GradleProcessorDependencyConfiguration.byName(groupValues[2]) ?: return null + val notation = groupValues[3] + if (notation.isLombokDependencyNotation()) return null + if (!sourceConfiguration.acceptsAnyProcessor) { + val processorPath = GradleProcessorPath.of(notation) ?: return null + if (processorPath !in KNOWN_PROCESSOR_ARTIFACTS) return null + } + return KaptProcessorDependency( + match = this, + dependencyConfiguration = sourceConfiguration.dependencyConfiguration, + kaptConfiguration = sourceConfiguration.kaptConfiguration, + notation = notation, + dropOriginal = sourceConfiguration.dropOriginal + ) } private fun kaptPluginExpression(forKotlinDsl: Boolean): String = @@ -247,16 +251,23 @@ private fun kaptPluginExpression(forKotlinDsl: Boolean): String = private fun kaptDependencyNotation(configuration: String, dependency: String): String = "$configuration(\"$dependency\")" -private data class KaptProcessorDependency(val match: MatchResult, val kaptConfiguration: String, val notation: String) +private data class KaptProcessorDependency( + val match: MatchResult, + val dependencyConfiguration: String, + val kaptConfiguration: String, + val notation: String, + val dropOriginal: Boolean +) private data class KaptDependency(val configuration: String, val notation: String) private enum class GradleProcessorDependencyConfiguration( val dependencyConfiguration: String, val kaptConfiguration: String, + val dropOriginal: Boolean = false, val acceptsAnyProcessor: Boolean, ) { - ANNOTATION_PROCESSOR("annotationProcessor", "kapt", acceptsAnyProcessor = true), + ANNOTATION_PROCESSOR("annotationProcessor", "kapt", dropOriginal = true, acceptsAnyProcessor = true), TEST_ANNOTATION_PROCESSOR("testAnnotationProcessor", "kaptTest", acceptsAnyProcessor = true), IMPLEMENTATION("implementation", "kapt", acceptsAnyProcessor = false), TEST_IMPLEMENTATION("testImplementation", "kaptTest", acceptsAnyProcessor = false); diff --git a/plugins/kotlin/idea/tests/testData/gradle/fixes/addKaptCompilerPluginForAnnotationProcessorKts/after/build.gradle.kts b/plugins/kotlin/idea/tests/testData/gradle/fixes/addKaptCompilerPluginForAnnotationProcessorKts/after/build.gradle.kts index 1c06847656a7..28adcb906e77 100644 --- a/plugins/kotlin/idea/tests/testData/gradle/fixes/addKaptCompilerPluginForAnnotationProcessorKts/after/build.gradle.kts +++ b/plugins/kotlin/idea/tests/testData/gradle/fixes/addKaptCompilerPluginForAnnotationProcessorKts/after/build.gradle.kts @@ -10,7 +10,6 @@ repositories { dependencies { implementation("org.mapstruct:mapstruct:1.6.3") - annotationProcessor("org.mapstruct:mapstruct-processor:1.6.3") kapt("org.mapstruct:mapstruct-processor:1.6.3") testImplementation(kotlin("test")) }