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") } }