Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Less noisy output for detekt with Type solving #4423

Merged
merged 2 commits into from Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -3,29 +3,36 @@ package io.gitlab.arturbosch.detekt.core
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.PlainTextMessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import java.io.PrintStream

internal fun generateBindingContext(
environment: KotlinCoreEnvironment,
classpath: List<String>,
files: List<KtFile>,
debugPrinter: (() -> String) -> Unit,
warningPrinter: (String) -> Unit,
): BindingContext {
if (classpath.isEmpty()) {
return BindingContext.EMPTY
}

val messageCollector = DetektMessageCollector(
minSeverity = CompilerMessageSeverity.ERROR,
debugPrinter = debugPrinter,
warningPrinter = warningPrinter,
)

val analyzer = AnalyzerWithCompilerReport(
DetektMessageCollector(minSeverity = CompilerMessageSeverity.ERROR),
environment.configuration.languageVersionSettings
messageCollector,
environment.configuration.languageVersionSettings,
)
analyzer.analyzeAndReport(files) {
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
Expand All @@ -37,17 +44,32 @@ internal fun generateBindingContext(
::FileBasedDeclarationProviderFactory
)
}

messageCollector.printIssuesCountIfAny()

return analyzer.analysisResult.bindingContext
}

internal class DetektMessageCollector(
errorStream: PrintStream = System.err,
verbose: Boolean = false,
private val minSeverity: CompilerMessageSeverity = CompilerMessageSeverity.ERROR
) : PrintingMessageCollector(errorStream, DetektMessageRenderer, verbose) {
private val minSeverity: CompilerMessageSeverity,
private val debugPrinter: (() -> String) -> Unit,
private val warningPrinter: (String) -> Unit,
) : MessageCollector by MessageCollector.NONE {
private var messages = 0

override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) {
if (severity.ordinal <= minSeverity.ordinal) {
super.report(severity, message, location)
debugPrinter { DetektMessageRenderer.render(severity, message, location) }
messages++
}
}

fun printIssuesCountIfAny() {
if (messages > 0) {
warningPrinter(
"The BindingContext was created with $messages issues. " +
"Run detekt with --debug to see the error messages."
)
}
}
}
Expand Down
Expand Up @@ -63,7 +63,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) },
{ generateBindingContext(settings.environment, settings.classpath, it, settings::debug, settings::info) },
override val processorsProvider: () -> List<FileProcessListener> =
{ FileProcessorLocator(settings).load() },
override val ruleSetsProvider: () -> List<RuleSetProvider> =
Expand Down
@@ -1,44 +1,88 @@
package io.gitlab.arturbosch.detekt.core

import io.mockk.Called
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.spekframework.spek2.Spek
import org.spekframework.spek2.lifecycle.CachingMode
import org.spekframework.spek2.style.specification.describe
import java.io.PrintStream

internal object DetektMessageCollectorSpec : Spek({
BraisGabin marked this conversation as resolved.
Show resolved Hide resolved
describe("DetektMessageCollector") {
val errorStream by memoized { CollectingPrintStream() }
val subject by memoized {
val debugPrinter: (() -> String) -> Unit by memoized(CachingMode.TEST) {
mockk {
every { this@mockk.invoke(any()) } returns Unit
}
}
val warningPrinter: (String) -> Unit by memoized(CachingMode.TEST) {
mockk {
every { this@mockk.invoke(any()) } returns Unit
}
}
val subject by memoized(CachingMode.TEST) {
DetektMessageCollector(
errorStream = errorStream,
minSeverity = CompilerMessageSeverity.INFO
minSeverity = CompilerMessageSeverity.INFO,
debugPrinter = debugPrinter,
warningPrinter = warningPrinter,
)
}

describe("message with min severity") {
beforeEachTest { subject.report(CompilerMessageSeverity.INFO, "message", null) }

it("prints the message") {
assertThat(errorStream.messages).containsExactly("info: message")
val slot = slot<() -> String>()
verify { debugPrinter.invoke(capture(slot)) }
assertThat(slot.captured()).isEqualTo("info: message")
}

it("adds up to the message count") {
subject.printIssuesCountIfAny()

verify {
warningPrinter(
"The BindingContext was created with 1 issues. " +
"Run detekt with --debug to see the error messages."
)
}
}
}
describe("message with higher severity than the min severity") {
beforeEachTest { subject.report(CompilerMessageSeverity.WARNING, "message", null) }

it("prints the message") {
assertThat(errorStream.messages).containsExactly("warning: message")
val slot = slot<() -> String>()
verify { debugPrinter.invoke(capture(slot)) }
assertThat(slot.captured()).isEqualTo("warning: message")
}

it("adds up to the message count") {
subject.printIssuesCountIfAny()

verify {
warningPrinter(
"The BindingContext was created with 1 issues. " +
"Run detekt with --debug to see the error messages."
)
}
}
}
describe("message with lower severity than the min severity") {
beforeEachTest { subject.report(CompilerMessageSeverity.LOGGING, "message", null) }

it("ignores the message") {
assertThat(errorStream.messages).isEmpty()
verify { debugPrinter wasNot Called }
}

it("doesn't add up to the message count") {
subject.printIssuesCountIfAny()

verify { warningPrinter wasNot Called }
}
}
}
})

private class CollectingPrintStream(val messages: MutableList<String> = mutableListOf()) : PrintStream(System.err) {
override fun println(message: String) {
messages.add(message)
}
}