Repository: detekt/detekt (kotlin)

Issue:
Report issues creating compilation environment
## Expected Behavior
Users are warned when there are issues with compilations when running detekt without having to enable debug output.

One option is reverting https://github.com/detekt/detekt/pull/5449. We could improve the message though to remove reference to BindingContext, and also split out the warning vs error count. Errors always require attention, but the actual compilation may have warnings which are ignored by the dev so they're less important?

## Current Behavior
No errors are shown unless running with debug output enabled.

## Context
Now #4465 has been addressed and https://github.com/detekt/detekt/pull/7432 was merged, there should be no useless or incorrect compilation warnings when detekt is run using tasks that were setup by detekt Gradle plugin, unless someone has misconfigured something or there's some other bug that requires our attention.

All warnings generated by detekt's compilation step are now things that can be addressed by updating config or making an update in detekt. With our strong recommendation that users don't roll their own analysis tasks in Gradle the auto-configured tasks should all run cleanly.

detekt can now analyze itself without any extra unnecessary output when run with `debug` enabled (except detekt-gradle-plugin since that uses an older DGP version to analyze itself, so that's expected).

To prove this, enable `debug` output on detekt extension in root build file, then execute `./gradle detektMain detektTest detektTestFixtures detektFunctionalTest`. There are no issues reported.

This should be the case when these conditions are met:
1. detekt understands the flags that the Kotlin compilation tasks are using. We will sometimes have problems when people use experimental flags on new Kotlin versions that aren't supported by the Kotlin version detekt is using.
2. detekt configured the analysis task & CLI flags. Though it's possible for users to roll their own compilation analysis tasks this should not be necessary at all anymore thanks to the improvements I've made to the Gradle plugin which creates a task for every compilation and auto configures it with all relevant flags.

If users create their own tasks or use full analysis mode calling CLI directly they're on their own and the compilation warnings are valid. If they are using incompatible flags then I think it's important to show warnings - if detekt can't compile cleanly then we're not doing analysis in a like for like environment.

Pull request:
Closes #7439

Based on https://github.com/detekt/detekt/pull/4423 and a revert of commit eef1dfa4bda12b91e327948e028fe6bbcf8ea0ae.

Patch:
diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt
index e565522f8188..2beb8e6067fd 100644
--- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt
+++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt
@@ -59,7 +59,7 @@ internal class DefaultLifecycle(
     override val settings: ProcessingSettings,
     override val parsingStrategy: ParsingStrategy,
     override val bindingProvider: (files: List<KtFile>) -> BindingContext =
-        { generateBindingContext(settings.environment, settings.classpath, it, settings::debug) },
+        { generateBindingContext(settings.environment, settings.classpath, it, settings::debug, settings::info) },
     override val processorsProvider: () -> List<FileProcessListener> =
         { FileProcessorLocator(settings).load() },
     override val ruleSetsProvider: () -> List<RuleSetProvider> =
diff --git a/detekt-parser/src/main/kotlin/io/github/detekt/parser/BindingContextUtils.kt b/detekt-parser/src/main/kotlin/io/github/detekt/parser/BindingContextUtils.kt
index e0f35bd64080..7b239a4d9102 100644
--- a/detekt-parser/src/main/kotlin/io/github/detekt/parser/BindingContextUtils.kt
+++ b/detekt-parser/src/main/kotlin/io/github/detekt/parser/BindingContextUtils.kt
@@ -17,6 +17,7 @@ fun generateBindingContext(
     classpath: List<String>,
     files: List<KtFile>,
     debugPrinter: (() -> String) -> Unit,
+    warningPrinter: (String) -> Unit,
 ): BindingContext {
     if (classpath.isEmpty()) {
         return BindingContext.EMPTY
@@ -25,6 +26,7 @@ fun generateBindingContext(
     val messageCollector = DetektMessageCollector(
         minSeverity = CompilerMessageSeverity.ERROR,
         debugPrinter = debugPrinter,
+        warningPrinter = warningPrinter,
     )
 
     val analyzer = AnalyzerWithCompilerReport(
@@ -42,12 +44,15 @@ fun generateBindingContext(
         )
     }
 
+    messageCollector.printIssuesCountIfAny()
+
     return analyzer.analysisResult.bindingContext
 }
 
 internal class DetektMessageCollector(
     private val minSeverity: CompilerMessageSeverity,
     private val debugPrinter: (() -> String) -> Unit,
+    private val warningPrinter: (String) -> Unit,
 ) : MessageCollector by MessageCollector.NONE {
     private var messages = 0
 
@@ -57,6 +62,15 @@ internal class DetektMessageCollector(
             messages++
         }
     }
+
+    fun printIssuesCountIfAny() {
+        if (messages > 0) {
+            warningPrinter(
+                "There were $messages compiler errors found during analysis. This affects accuracy of reporting.\n" +
+                    "Run detekt CLI with --debug or set `detekt { debug = true }` in Gradle to see the error messages."
+            )
+        }
+    }
 }
 
 private object DetektMessageRenderer : PlainTextMessageRenderer() {


Test patch:
diff --git a/detekt-parser/src/test/kotlin/io/github/detekt/parser/DetektMessageCollectorSpec.kt b/detekt-parser/src/test/kotlin/io/github/detekt/parser/DetektMessageCollectorSpec.kt
index 9f7374f53ce0..0ece71243036 100644
--- a/detekt-parser/src/test/kotlin/io/github/detekt/parser/DetektMessageCollectorSpec.kt
+++ b/detekt-parser/src/test/kotlin/io/github/detekt/parser/DetektMessageCollectorSpec.kt
@@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test
 class DetektMessageCollectorSpec {
 
     private val debugPrinter = FakePrinter()
+    private val warningPrinter = FakeWarningPrinter()
     private lateinit var subject: DetektMessageCollector
 
     @BeforeEach
@@ -17,6 +18,7 @@ class DetektMessageCollectorSpec {
         subject = DetektMessageCollector(
             minSeverity = CompilerMessageSeverity.INFO,
             debugPrinter = debugPrinter,
+            warningPrinter = warningPrinter,
         )
     }
 
@@ -31,6 +33,18 @@ class DetektMessageCollectorSpec {
         fun `prints the message`() {
             assertThat(debugPrinter.messages).contains("info: message")
         }
+
+        @Test
+        fun `adds up to the message count`() {
+            subject.printIssuesCountIfAny()
+
+            assertThat(warningPrinter.messages).contains(
+                """
+                    There were 1 compiler errors found during analysis. This affects accuracy of reporting.
+                    Run detekt CLI with --debug or set `detekt { debug = true }` in Gradle to see the error messages.
+                """.trimIndent()
+            )
+        }
     }
 
     @Nested
@@ -44,6 +58,18 @@ class DetektMessageCollectorSpec {
         fun `prints the message`() {
             assertThat(debugPrinter.messages).contains("warning: message")
         }
+
+        @Test
+        fun `adds up to the message count`() {
+            subject.printIssuesCountIfAny()
+
+            assertThat(warningPrinter.messages).contains(
+                """
+                    There were 1 compiler errors found during analysis. This affects accuracy of reporting.
+                    Run detekt CLI with --debug or set `detekt { debug = true }` in Gradle to see the error messages.
+                """.trimIndent()
+            )
+        }
     }
 
     @Nested
@@ -59,10 +85,24 @@ class DetektMessageCollectorSpec {
         }
     }
 
+    @Test
+    fun `doesn't add up to the message count`() {
+        subject.printIssuesCountIfAny()
+
+        assertThat(warningPrinter.messages).isEmpty()
+    }
+
     class FakePrinter : (() -> String) -> Unit {
         val messages = mutableListOf<String>()
         override fun invoke(param: () -> String) {
             messages.add(param())
         }
     }
+
+    class FakeWarningPrinter : (String) -> Unit {
+        val messages = mutableListOf<String>()
+        override fun invoke(param: String) {
+            messages.add(param)
+        }
+    }
 }