[REPL] Report script codegen errors in advance

This commit is contained in:
Ilya Muradyan
2022-05-17 17:41:10 +03:00
parent eb1d7110ec
commit 00819c421f
3 changed files with 126 additions and 31 deletions
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.diagnostics.impl.SimpleDiagnosticsCollector
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
@@ -60,6 +61,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
configuration: ScriptCompilationConfiguration
): ResultWithDiagnostics<LinkedSnippet<KJvmCompiledScript>> =
snippets.map { snippet ->
// TODO: get rid of messageCollector to avoid creation of additional entities
withMessageCollector(snippet) { messageCollector ->
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
return it
@@ -124,10 +126,25 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
val isIr = context.environment.configuration.getBoolean(JVMConfigurationKeys.IR)
val codegenDiagnosticsCollector = SimpleDiagnosticsCollector()
val genStateBuilder = GenerationState.Builder(
snippetKtFile.project,
ClassBuilderFactories.BINARIES,
compilationState.analyzerEngine.module,
compilationState.analyzerEngine.trace.bindingContext,
compilationState.environment.configuration
).diagnosticReporter(codegenDiagnosticsCollector)
val generationState = if (isIr) {
generateWithBackendIr(compilationState, snippetKtFile, sourceFiles)
generateWithBackendIr(compilationState, sourceFiles, genStateBuilder)
} else {
generateWithOldBackend(compilationState, snippetKtFile, sourceFiles)
generateWithOldBackend(snippetKtFile, sourceFiles, genStateBuilder)
}
if (codegenDiagnosticsCollector.hasErrors) {
val scriptDiagnostics = codegenDiagnosticsCollector.scriptDiagnostics(snippet)
return failure(messageCollector, *scriptDiagnostics.toTypedArray())
}
state.history.push(LineId(snippetNo, 0, snippet.hashCode()), scriptDescriptor)
@@ -156,17 +173,11 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
}.last()
private fun generateWithOldBackend(
compilationState: ReplCompilationState<AnalyzerT>,
snippetKtFile: KtFile,
sourceFiles: List<KtFile>
sourceFiles: List<KtFile>,
prebuiltState: GenerationState.Builder,
): GenerationState {
val generationState = GenerationState.Builder(
snippetKtFile.project,
ClassBuilderFactories.BINARIES,
compilationState.analyzerEngine.module,
compilationState.analyzerEngine.trace.bindingContext,
compilationState.environment.configuration
).build().also { generationState ->
val generationState = prebuiltState.build().also { generationState ->
generationState.scriptSpecific.earlierScriptsForReplInterpreter = state.history.map { it.item }
generationState.beforeCompile()
generationState.oldBEInitTrace(sourceFiles)
@@ -178,8 +189,8 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
private fun generateWithBackendIr(
compilationState: ReplCompilationState<AnalyzerT>,
snippetKtFile: KtFile,
sourceFiles: List<KtFile>
sourceFiles: List<KtFile>,
prebuiltState: GenerationState.Builder,
): GenerationState {
val generatorExtensions = object : JvmGeneratorExtensionsImpl(compilationState.environment.configuration) {
override fun getPreviousScripts() = state.history.map { compilationState.symbolTable.referenceScript(it.item) }
@@ -189,14 +200,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
compilationState.environment.configuration.get(CLIConfigurationKeys.PHASE_CONFIG),
compilationState.mangler, compilationState.symbolTable, generatorExtensions
)
val generationState = GenerationState.Builder(
snippetKtFile.project,
ClassBuilderFactories.BINARIES,
compilationState.analyzerEngine.module,
compilationState.analyzerEngine.trace.bindingContext,
compilationState.environment.configuration
)
.build()
val generationState = prebuiltState.build()
codegenFactory.generateModule(
generationState,
@@ -11,6 +11,11 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
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.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.scripting.definitions.MessageReporter
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import kotlin.reflect.KMutableProperty1
@@ -18,6 +23,7 @@ import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.asErrorDiagnostics
import kotlin.script.experimental.jvm.util.toSourceCodePosition
class ScriptDiagnosticsMessageCollector(private val parentMessageCollector: MessageCollector?) : MessageCollector {
@@ -205,3 +211,37 @@ val MessageCollector.reporter: MessageReporter
this.report(severity.toCompilerMessageSeverity(), message)
}
fun KtDiagnostic.asScriptDiagnostic(sourceCode: SourceCode): ScriptDiagnostic {
val (diagnosticCode, scriptSeverity) = when (severity) {
Severity.INFO -> ScriptDiagnostic.unspecifiedInfo to ScriptDiagnostic.Severity.INFO
Severity.ERROR -> ScriptDiagnostic.unspecifiedError to ScriptDiagnostic.Severity.ERROR
Severity.WARNING -> ScriptDiagnostic.unspecifiedInfo to ScriptDiagnostic.Severity.WARNING
}
val renderer = RootDiagnosticRendererFactory(this)
val location = if (textRanges.isEmpty()) {
null
} else {
val firstRange = textRanges.first()
val lastRange = textRanges.last()
SourceCode.LocationWithId(
element.psi?.containingFile?.virtualFile?.path.orEmpty(),
SourceCode.Location(
firstRange.startOffset.toSourceCodePosition(sourceCode),
lastRange.endOffset.toSourceCodePosition(sourceCode)
)
)
}
return ScriptDiagnostic(
diagnosticCode,
renderer.render(this),
scriptSeverity,
location
)
}
fun BaseDiagnosticsCollector.scriptDiagnostics(sourceCode: SourceCode) = diagnostics.map {
it.asScriptDiagnostic(sourceCode)
}