The Kotlin compiler infrastructure providing JVM, JavaScript, and metadata compilation capabilities
—
Comprehensive diagnostic and message reporting system providing multiple output formats, severity levels, and source location tracking. The message handling system supports integration with IDEs, build tools, and command-line interfaces through customizable renderers and collectors.
Core interface for collecting compilation messages and diagnostics with severity-based filtering.
/**
* Interface for collecting compilation messages and diagnostics
* Provides extensible message handling with severity levels
*/
interface MessageCollector {
/**
* Report a compilation message
*
* @param severity Message severity level
* @param message Message text content
* @param location Optional source location information
*/
fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation? = null
)
/**
* Check if message collector has reported errors
*
* @return true if any error messages have been reported
*/
fun hasErrors(): Boolean
/**
* Clear all collected messages
* Resets error state and clears message history
*/
fun clear()
companion object {
/** No-op message collector that discards all messages */
val NONE: MessageCollector
}
}Hierarchical severity levels for categorizing compilation messages.
/**
* Compilation message severity levels
* Hierarchical system from logging to errors
*/
enum class CompilerMessageSeverity(val presentableName: String) {
/** Detailed logging information */
LOGGING("logging"),
/** General information messages */
INFO("info"),
/** General warnings */
WARNING("warning"),
/** Important warnings that should not be ignored */
STRONG_WARNING("strong warning"),
/** Compilation errors */
ERROR("error"),
/** Fatal compiler errors */
EXCEPTION("exception");
companion object {
/** Verbose output including all severity levels */
val VERBOSE: Set<CompilerMessageSeverity>
/**
* Check if severity level is error or higher
*
* @param severity Severity to check
* @return true if error level or higher
*/
fun isError(severity: CompilerMessageSeverity): Boolean
/**
* Check if severity level is warning or higher
*
* @param severity Severity to check
* @return true if warning level or higher
*/
fun isWarning(severity: CompilerMessageSeverity): Boolean
}
}Source file location information for precise error reporting.
/**
* Source location information for diagnostic messages
* Provides file path and position information
*/
interface CompilerMessageSourceLocation {
/** Source file path */
val path: String
/** Line number (1-based) */
val line: Int
/** Column number (1-based) */
val column: Int
/** Line content for context */
val lineContent: String?
companion object {
/**
* Create source location
*
* @param path File path
* @param line Line number (1-based)
* @param column Column number (1-based)
* @param lineContent Optional line content
* @return Source location instance
*/
fun create(
path: String,
line: Int,
column: Int,
lineContent: String?
): CompilerMessageSourceLocation
}
}Pluggable message rendering system for different output formats and tools.
/**
* Interface for rendering diagnostic messages
* Supports different output formats for various tools
*/
interface MessageRenderer {
/**
* Render diagnostic message to string
*
* @param severity Message severity level
* @param message Message content
* @param location Source location information
* @return Formatted message string
*/
fun render(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
): String
/**
* Get path representation
* Controls how file paths are displayed
*
* @param location Source location
* @return Formatted path string
*/
fun getPath(location: CompilerMessageSourceLocation): String
companion object {
/** Plain text renderer with full file paths */
val PLAIN_FULL_PATHS: MessageRenderer
/** Plain text renderer with relative file paths */
val PLAIN_RELATIVE_PATHS: MessageRenderer
/** Plain text renderer without file paths */
val PLAIN_WITHOUT_PATHS: MessageRenderer
}
}Pre-built renderers for common IDE and build tool integration.
/**
* Plain text message renderer
* Standard text output for command-line tools
*/
class PlainTextMessageRenderer(
private val withPaths: Boolean = true
) : MessageRenderer {
override fun render(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
): String
}
/**
* Xcode-compatible message renderer
* Formats messages for Xcode IDE integration
*/
class XcodeStyleMessageRenderer : MessageRenderer {
override fun render(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
): String
}
/**
* Gradle-compatible message renderer
* Formats messages for Gradle build tool integration
*/
class GradleStyleMessageRenderer : MessageRenderer {
override fun render(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
): String
}Concrete message collector that prints messages to output streams.
/**
* Message collector that prints to output streams
* Supports custom renderers and severity filtering
*/
class PrintingMessageCollector(
private val stream: PrintStream,
private val messageRenderer: MessageRenderer,
private val verbose: Boolean
) : MessageCollector {
/**
* Create printing message collector
*
* @param stream Output stream for messages
* @param messageRenderer Message rendering strategy
* @param verbose Enable verbose output (all severity levels)
*/
constructor(
stream: PrintStream,
messageRenderer: MessageRenderer,
verbose: Boolean
)
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
if (verbose || severity != CompilerMessageSeverity.LOGGING) {
val renderedMessage = messageRenderer.render(severity, message, location)
stream.println(renderedMessage)
}
}
override fun hasErrors(): Boolean
override fun clear()
}Message collector that groups related messages together.
/**
* Message collector that groups related messages
* Useful for organizing diagnostic output by file or module
*/
class GroupingMessageCollector(
private val delegate: MessageCollector
) : MessageCollector {
/**
* Start new message group
*
* @param groupName Name for the message group
*/
fun startGroup(groupName: String)
/**
* End current message group
*/
fun endGroup()
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
)
override fun hasErrors(): Boolean
override fun clear()
}Advanced diagnostic reporting with analysis integration.
/**
* Analyzer with integrated compiler reporting
* Combines frontend analysis with diagnostic collection
*/
class AnalyzerWithCompilerReport(
private val messageCollector: MessageCollector,
private val languageVersionSettings: LanguageVersionSettings
) {
/**
* Check for compilation errors
*
* @return true if compilation should stop due to errors
*/
fun hasErrors(): Boolean
/**
* Report analysis result
*
* @param result Analysis result to report
* @param files Source files analyzed
*/
fun reportAnalysisResult(
result: AnalysisResult,
files: Collection<KtFile>
)
/**
* Render diagnostic with context
*
* @param diagnostic Diagnostic to render
* @return Formatted diagnostic message
*/
fun renderDiagnostic(diagnostic: Diagnostic): String
}
/**
* Default diagnostic reporter implementation
* Standard diagnostic message formatting and reporting
*/
class DefaultDiagnosticReporter(
private val messageCollector: MessageCollector
) : DiagnosticReporter {
override fun report(
diagnostic: Diagnostic,
bindingContext: BindingContext
) {
val severity = diagnostic.severity.toCompilerMessageSeverity()
val message = renderDiagnosticMessage(diagnostic)
val location = diagnostic.psiElement?.let {
getLocationFromPsiElement(it)
}
messageCollector.report(severity, message, location)
}
}High-level diagnostic message reporting with rich formatting.
/**
* Rich diagnostic message reporter
* Provides detailed diagnostic information with context
*/
class DiagnosticMessageReporter(
private val messageCollector: MessageCollector,
private val messageRenderer: MessageRenderer = MessageRenderer.PLAIN_FULL_PATHS
) {
/**
* Report diagnostic with full context
*
* @param diagnostic Diagnostic information
* @param file Source file context
* @param context Binding context for resolution
*/
fun reportDiagnostic(
diagnostic: Diagnostic,
file: KtFile,
context: BindingContext
)
/**
* Report multiple diagnostics from analysis
*
* @param diagnostics Collection of diagnostics
* @param context Binding context
*/
fun reportDiagnostics(
diagnostics: Collection<Diagnostic>,
context: BindingContext
)
/**
* Report exception as diagnostic
*
* @param exception Exception to report
* @param location Optional source location
*/
fun reportException(
exception: Throwable,
location: CompilerMessageSourceLocation? = null
)
}import org.jetbrains.kotlin.cli.common.messages.*
import java.io.PrintStream
// Create message collector with plain text renderer
val messageCollector = PrintingMessageCollector(
stream = System.err,
messageRenderer = MessageRenderer.PLAIN_FULL_PATHS,
verbose = true
)
// Report messages with different severity levels
messageCollector.report(
CompilerMessageSeverity.INFO,
"Starting compilation of module 'my-app'"
)
messageCollector.report(
CompilerMessageSeverity.WARNING,
"Unused import: kotlin.collections.List",
CompilerMessageSourceLocation.create(
path = "src/main/kotlin/Main.kt",
line = 3,
column = 1,
lineContent = "import kotlin.collections.List"
)
)
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Unresolved reference: unknownFunction",
CompilerMessageSourceLocation.create(
path = "src/main/kotlin/Main.kt",
line = 10,
column = 5,
lineContent = " unknownFunction()"
)
)
// Check for errors
if (messageCollector.hasErrors()) {
println("Compilation failed due to errors")
}// Custom JSON message renderer
class JsonMessageRenderer : MessageRenderer {
override fun render(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
): String {
val json = buildJsonObject {
put("severity", severity.presentableName)
put("message", message)
put("timestamp", System.currentTimeMillis())
location?.let { loc ->
putJsonObject("location") {
put("path", loc.path)
put("line", loc.line)
put("column", loc.column)
put("lineContent", loc.lineContent)
}
}
}
return json.toString()
}
override fun getPath(location: CompilerMessageSourceLocation): String {
return location.path
}
}
// Use custom renderer
val jsonCollector = PrintingMessageCollector(
stream = System.out,
messageRenderer = JsonMessageRenderer(),
verbose = false
)// Xcode integration
val xcodeCollector = PrintingMessageCollector(
stream = System.err,
messageRenderer = XcodeStyleMessageRenderer(),
verbose = false
)
// Gradle integration
val gradleCollector = PrintingMessageCollector(
stream = System.err,
messageRenderer = GradleStyleMessageRenderer(),
verbose = true
)
// Configure in compiler configuration
val configuration = CompilerConfiguration().apply {
put(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, gradleCollector)
}val baseCollector = PrintingMessageCollector(
System.err,
MessageRenderer.PLAIN_FULL_PATHS,
true
)
val groupingCollector = GroupingMessageCollector(baseCollector)
// Group messages by module
groupingCollector.startGroup("Module: core")
groupingCollector.report(
CompilerMessageSeverity.INFO,
"Compiling core module"
)
groupingCollector.report(
CompilerMessageSeverity.ERROR,
"Missing dependency in core module"
)
groupingCollector.endGroup()
groupingCollector.startGroup("Module: api")
groupingCollector.report(
CompilerMessageSeverity.INFO,
"Compiling api module"
)
groupingCollector.endGroup()// Custom filtering message collector
class FilteringMessageCollector(
private val delegate: MessageCollector,
private val minSeverity: CompilerMessageSeverity
) : MessageCollector {
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
if (severity.ordinal >= minSeverity.ordinal) {
delegate.report(severity, message, location)
}
}
override fun hasErrors(): Boolean = delegate.hasErrors()
override fun clear() = delegate.clear()
}
// Only show warnings and errors
val filteringCollector = FilteringMessageCollector(
delegate = PrintingMessageCollector(
System.err,
MessageRenderer.PLAIN_FULL_PATHS,
true
),
minSeverity = CompilerMessageSeverity.WARNING
)import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
// Create analyzer with reporting
val analyzerWithReport = AnalyzerWithCompilerReport(
messageCollector = messageCollector,
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT
)
// Analyze and report
val analysisResult = analyzeFiles(sourceFiles, environment)
analyzerWithReport.reportAnalysisResult(analysisResult, sourceFiles)
if (analyzerWithReport.hasErrors()) {
return ExitCode.COMPILATION_ERROR
}// Enhanced message collector with exception handling
class ExceptionHandlingMessageCollector(
private val delegate: MessageCollector
) : MessageCollector by delegate {
fun reportException(
exception: Throwable,
context: String? = null,
location: CompilerMessageSourceLocation? = null
) {
val message = buildString {
context?.let { append("$it: ") }
append(exception.message ?: exception.javaClass.simpleName)
// Add stack trace for debugging
if (exception.stackTrace.isNotEmpty()) {
append("\n")
exception.stackTrace.take(5).forEach { frame ->
append(" at ${frame}\n")
}
}
}
delegate.report(
CompilerMessageSeverity.EXCEPTION,
message,
location
)
}
}
// Usage
val exceptionCollector = ExceptionHandlingMessageCollector(messageCollector)
try {
// Compilation code
} catch (e: Exception) {
exceptionCollector.reportException(
e,
"Compilation failed",
CompilerMessageSourceLocation.create("build.gradle.kts", 1, 1, null)
)
}// Message collector with performance monitoring
class PerformanceMonitoringCollector(
private val delegate: MessageCollector
) : MessageCollector by delegate {
private var messageCount = 0
private var errorCount = 0
private var warningCount = 0
private val startTime = System.currentTimeMillis()
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
messageCount++
when (severity) {
CompilerMessageSeverity.ERROR,
CompilerMessageSeverity.EXCEPTION -> errorCount++
CompilerMessageSeverity.WARNING,
CompilerMessageSeverity.STRONG_WARNING -> warningCount++
else -> {}
}
delegate.report(severity, message, location)
}
fun printStatistics() {
val duration = System.currentTimeMillis() - startTime
delegate.report(
CompilerMessageSeverity.INFO,
"Compilation completed in ${duration}ms: " +
"$messageCount messages ($errorCount errors, $warningCount warnings)"
)
}
}
// Usage
val performanceCollector = PerformanceMonitoringCollector(baseCollector)
// ... perform compilation ...
performanceCollector.printStatistics()// Broadcast messages to multiple collectors
class BroadcastMessageCollector(
private val collectors: List<MessageCollector>
) : MessageCollector {
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
collectors.forEach { collector ->
collector.report(severity, message, location)
}
}
override fun hasErrors(): Boolean {
return collectors.any { it.hasErrors() }
}
override fun clear() {
collectors.forEach { it.clear() }
}
}
// Send messages to both console and log file
val consoleCollector = PrintingMessageCollector(
System.err,
MessageRenderer.PLAIN_FULL_PATHS,
false
)
val fileCollector = PrintingMessageCollector(
PrintStream(File("compilation.log")),
JsonMessageRenderer(),
true
)
val broadcastCollector = BroadcastMessageCollector(
listOf(consoleCollector, fileCollector)
)class MessageCollectionException(message: String, cause: Throwable? = null) : Exception(message, cause)
class InvalidMessageRendererException(message: String) : MessageCollectionException(message)
class MessageRenderingException(message: String, cause: Throwable) : MessageCollectionException(message, cause)Common message handling scenarios:
Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-compiler