From 1ab1dccea18eec7e65e10c359db30f6464fc26bc Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Tue, 2 May 2023 16:41:09 +0100 Subject: [PATCH] [Gradle] Support rendering diagnostics reported in execution phase - Introduce 'UsesKotlinDiagnostics' marker-interface for tasks that want to report diagnostics during their execution - Introduce 'transparent mode' to collector. When all projects are evaluated, Collector switches to it and will render all received diagnostics right away. This allows reporting and rendering diagnostics that are reported not from tasks, but after evaluation phase (e.g. diagnostics on resolved dependencies) ^KT-58353 Fixed --- .../gradle/plugin/KotlinPluginWrapper.kt | 22 +++++++++++-- .../KotlinToolingDiagnosticsCollector.kt | 27 +++++++++++++++- .../ToolingDiagnosticRenderingOptions.kt | 32 +++++++++++++++++++ .../UsesKotlinToolingDiagnostics.kt | 22 +++++++++++++ .../diagnostics/renderReportedDiagnostics.kt | 20 ++++++++---- 5 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/ToolingDiagnosticRenderingOptions.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 2fd1c9db84c..c74db370638 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -257,7 +257,7 @@ abstract class KotlinBasePluginWrapper : DefaultKotlinBasePlugin() { project.registerBuildKotlinToolingMetadataTask() - project.launchDiagnosticChecksAndReporting() + project.setupDiagnosticsChecksAndReporting() } internal open fun createTestRegistry(project: Project) = KotlinTestsRegistry(project) @@ -267,8 +267,19 @@ abstract class KotlinBasePluginWrapper : DefaultKotlinBasePlugin() { ): Plugin } -private fun Project.launchDiagnosticChecksAndReporting() { +private fun Project.setupDiagnosticsChecksAndReporting() { + // Setup reporting from tasks + tasks.withType(UsesKotlinToolingDiagnostics::class.java).configureEach { + it.usesService(kotlinToolingDiagnosticsCollectorProvider) + it.toolingDiagnosticsCollector.value(kotlinToolingDiagnosticsCollectorProvider) + it.diagnosticRenderingOptions.set(ToolingDiagnosticRenderingOptions.forProject(this)) + } + + // Launch checkers. Note that they are invoked eagerly to give them a fine-grained + // control over the lifecycle project.launchKotlinGradleProjectCheckers() + + // Schedule diagnostics rendering project.launch { project.configurationResult.await() renderReportedDiagnostics( @@ -277,6 +288,13 @@ private fun Project.launchDiagnosticChecksAndReporting() { project.kotlinPropertiesProvider.internalVerboseDiagnostics ) } + + // Schedule switching of Collector to transparent mode, so that any diagnostics reported + // after projects are evaluated will be transparently rendered right away instead of being + // silently swallowed + gradle.projectsEvaluated { + kotlinToolingDiagnosticsCollector.switchToTransparentMode() + } } abstract class AbstractKotlinPluginWrapper( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt index adb4408428a..31967333b54 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.diagnostics import org.gradle.api.InvalidUserCodeException import org.gradle.api.Project +import org.gradle.api.provider.Provider import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider @@ -20,6 +21,13 @@ private typealias ToolingDiagnosticId = String private typealias GradleProjectPath = String internal abstract class KotlinToolingDiagnosticsCollector : BuildService { + /** + * When collector is in transparent mode, any diagnostics received will be immediately rendered + * instead of collected + */ + @Volatile + private var isTransparent: Boolean = false + private val rawDiagnosticsFromProject: MutableMap> = ConcurrentHashMap() private val reportedIds: MutableSet = Collections.newSetFromMap(ConcurrentHashMap()) @@ -39,6 +47,10 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService + get() = gradle.registerClassLoaderScopedBuildService(KotlinToolingDiagnosticsCollector::class) + + internal val Project.kotlinToolingDiagnosticsCollector: KotlinToolingDiagnosticsCollector - get() = gradle.registerClassLoaderScopedBuildService(KotlinToolingDiagnosticsCollector::class).get() + get() = kotlinToolingDiagnosticsCollectorProvider.get() internal fun Project.reportDiagnostic(diagnostic: ToolingDiagnostic) { kotlinToolingDiagnosticsCollector.report(this, diagnostic) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/ToolingDiagnosticRenderingOptions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/ToolingDiagnosticRenderingOptions.kt new file mode 100644 index 00000000000..8006dbf7d0c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/ToolingDiagnosticRenderingOptions.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.diagnostics + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider + +internal class ToolingDiagnosticRenderingOptions( + val isVerbose: Boolean, + val suppressedWarningIds: List, + val suppressedErrorIds: List +) { + companion object { + fun forProject(project: Project): ToolingDiagnosticRenderingOptions = with(project.kotlinPropertiesProvider) { + ToolingDiagnosticRenderingOptions( + internalVerboseDiagnostics, + suppressedGradlePluginWarnings, + suppressedGradlePluginErrors + ) + } + } +} + +internal fun Collection.withoutSuppressed(options: ToolingDiagnosticRenderingOptions): Collection = + filter { !it.isSuppressed(options) } + +internal fun ToolingDiagnostic.isSuppressed(options: ToolingDiagnosticRenderingOptions): Boolean = + severity == ToolingDiagnostic.Severity.WARNING && id in options.suppressedWarningIds + || severity == ToolingDiagnostic.Severity.ERROR && id in options.suppressedErrorIds diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt new file mode 100644 index 00000000000..0b359540a77 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.diagnostics + +import org.gradle.api.Task +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Internal + +internal interface UsesKotlinToolingDiagnostics : Task { + @get:Internal + val toolingDiagnosticsCollector: Property + + @get:Internal + val diagnosticRenderingOptions: Property + + fun reportDiagnostic(diagnostic: ToolingDiagnostic) { + toolingDiagnosticsCollector.get().report(this, diagnostic) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/renderReportedDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/renderReportedDiagnostics.kt index 832c8f25825..7ec351f70df 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/renderReportedDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/renderReportedDiagnostics.kt @@ -10,14 +10,22 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnostic.Severity internal fun renderReportedDiagnostics(diagnostics: Collection, logger: Logger, isVerbose: Boolean) { for (diagnostic in diagnostics) { - when (diagnostic.severity) { - WARNING -> logger.warn("w: ${diagnostic.render(isVerbose)}\n") + renderReportedDiagnostic(diagnostic, logger, isVerbose) + } +} - ERROR -> logger.error("e: ${diagnostic.render(isVerbose)}\n") +internal fun renderReportedDiagnostic( + diagnostic: ToolingDiagnostic, + logger: Logger, + isVerbose: Boolean +) { + when (diagnostic.severity) { + WARNING -> logger.warn("w: ${diagnostic.render(isVerbose)}\n") - FATAL -> - error("Internal error: FATAL diagnostics throw an exception immediately in KotlinToolingDiagnosticsCollector") - } + ERROR -> logger.error("e: ${diagnostic.render(isVerbose)}\n") + + FATAL -> + error("Internal error: FATAL diagnostics throw an exception immediately in KotlinToolingDiagnosticsCollector") } }