Add diagnostic code to the ScriptDiagnostic

to allow checking for specific errors, e.g. incomplete statements in
the REPL
This commit is contained in:
Ilya Chernikov
2020-02-10 18:03:00 +01:00
parent 77eacae958
commit b96109f23f
12 changed files with 38 additions and 12 deletions
@@ -55,6 +55,7 @@ fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContex
fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) { fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) {
diagnostics.add( diagnostics.add(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message, message,
mapLegacyDiagnosticSeverity(severity), mapLegacyDiagnosticSeverity(severity),
context.script.locationId, context.script.locationId,
@@ -14,12 +14,14 @@ import java.io.Serializable
/** /**
* The single script diagnostic report * The single script diagnostic report
* @param code diagnostic identifier
* @param message diagnostic message * @param message diagnostic message
* @param severity diagnostic severity ({@link ScriptDiagnostic#Severity}) * @param severity diagnostic severity ({@link ScriptDiagnostic#Severity})
* @param location optional source location for the diagnostic * @param location optional source location for the diagnostic
* @param exception optional exception caused the diagnostic * @param exception optional exception caused the diagnostic
*/ */
data class ScriptDiagnostic( data class ScriptDiagnostic(
val code: Int,
val message: String, val message: String,
val severity: Severity = Severity.ERROR, val severity: Severity = Severity.ERROR,
val sourcePath: String? = null, val sourcePath: String? = null,
@@ -77,6 +79,14 @@ data class ScriptDiagnostic(
} }
} }
} }
companion object {
private const val serialVersionUID: Long = 0L
const val unspecifiedInfo = 0
const val unspecifiedError = -1
const val unspecifiedException = -2
}
} }
/** /**
@@ -198,24 +208,29 @@ fun makeFailureResult(vararg reports: ScriptDiagnostic): ResultWithDiagnostics.F
* Makes Failure result with diagnostic [message] with optional [path] and [location] * Makes Failure result with diagnostic [message] with optional [path] and [location]
*/ */
fun makeFailureResult(message: String, path: String? = null, location: SourceCode.Location? = null): ResultWithDiagnostics.Failure = fun makeFailureResult(message: String, path: String? = null, location: SourceCode.Location? = null): ResultWithDiagnostics.Failure =
ResultWithDiagnostics.Failure(message.asErrorDiagnostics(path, location)) ResultWithDiagnostics.Failure(message.asErrorDiagnostics(ScriptDiagnostic.unspecifiedError, path, location))
/** /**
* Converts the receiver Throwable to the Failure results wrapper with optional [customMessage], [path] and [location] * Converts the receiver Throwable to the Failure results wrapper with optional [customMessage], [path] and [location]
*/ */
fun Throwable.asDiagnostics( fun Throwable.asDiagnostics(
code: Int = ScriptDiagnostic.unspecifiedException,
customMessage: String? = null, customMessage: String? = null,
path: String? = null, path: String? = null,
location: SourceCode.Location? = null, location: SourceCode.Location? = null,
severity: ScriptDiagnostic.Severity = ScriptDiagnostic.Severity.ERROR severity: ScriptDiagnostic.Severity = ScriptDiagnostic.Severity.ERROR
): ScriptDiagnostic = ): ScriptDiagnostic =
ScriptDiagnostic(customMessage ?: message ?: "$this", severity, path, location, this) ScriptDiagnostic(code, customMessage ?: message ?: "$this", severity, path, location, this)
/** /**
* Converts the receiver String to error diagnostic report with optional [path] and [location] * Converts the receiver String to error diagnostic report with optional [path] and [location]
*/ */
fun String.asErrorDiagnostics(path: String? = null, location: SourceCode.Location? = null): ScriptDiagnostic = fun String.asErrorDiagnostics(
ScriptDiagnostic(this, ScriptDiagnostic.Severity.ERROR, path, location) code: Int = ScriptDiagnostic.unspecifiedError,
path: String? = null,
location: SourceCode.Location? = null
): ScriptDiagnostic =
ScriptDiagnostic(code, this, ScriptDiagnostic.Severity.ERROR, path, location)
/** /**
* Extracts the result value from the receiver wrapper or null if receiver represents a Failure * Extracts the result value from the receiver wrapper or null if receiver represents a Failure
@@ -14,7 +14,7 @@ import kotlin.script.experimental.api.ScriptDiagnostic
fun makeResolveFailureResult(message: String) = makeResolveFailureResult(listOf(message)) fun makeResolveFailureResult(message: String) = makeResolveFailureResult(listOf(message))
fun makeResolveFailureResult(messages: Iterable<String>) = fun makeResolveFailureResult(messages: Iterable<String>) =
ResultWithDiagnostics.Failure(messages.map { ScriptDiagnostic(it, ScriptDiagnostic.Severity.WARNING) }) ResultWithDiagnostics.Failure(messages.map { ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, it, ScriptDiagnostic.Severity.WARNING) })
fun RepositoryCoordinates.toRepositoryUrlOrNull(): URL? = fun RepositoryCoordinates.toRepositoryUrlOrNull(): URL? =
try { try {
@@ -33,6 +33,7 @@ class JsScriptEvaluator : ScriptEvaluator {
} catch (e: Exception) { } catch (e: Exception) {
ResultWithDiagnostics.Failure( ResultWithDiagnostics.Failure(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = e.localizedMessage, message = e.localizedMessage,
severity = ScriptDiagnostic.Severity.ERROR, severity = ScriptDiagnostic.Severity.ERROR,
exception = e exception = e
@@ -44,7 +44,7 @@ open class BasicJvmScriptClassFilesGenerator(val outputDir: File) : ScriptEvalua
return ResultWithDiagnostics.Success(EvaluationResult(ResultValue.NotEvaluated, scriptEvaluationConfiguration)) return ResultWithDiagnostics.Success(EvaluationResult(ResultValue.NotEvaluated, scriptEvaluationConfiguration))
} catch (e: Throwable) { } catch (e: Throwable) {
return ResultWithDiagnostics.Failure( return ResultWithDiagnostics.Failure(
e.asDiagnostics("Cannot generate script classes: ${e.message}", path = compiledScript.sourceLocationId) e.asDiagnostics(customMessage = "Cannot generate script classes: ${e.message}", path = compiledScript.sourceLocationId)
) )
} }
} }
@@ -103,7 +103,7 @@ open class BasicJvmScriptJarGenerator(val outputJar: File) : ScriptEvaluator {
return ResultWithDiagnostics.Success(EvaluationResult(ResultValue.NotEvaluated, scriptEvaluationConfiguration)) return ResultWithDiagnostics.Success(EvaluationResult(ResultValue.NotEvaluated, scriptEvaluationConfiguration))
} catch (e: Throwable) { } catch (e: Throwable) {
return ResultWithDiagnostics.Failure( return ResultWithDiagnostics.Failure(
e.asDiagnostics("Cannot generate script jar: ${e.message}", path = compiledScript.sourceLocationId) e.asDiagnostics(customMessage = "Cannot generate script jar: ${e.message}", path = compiledScript.sourceLocationId)
) )
} }
} }
@@ -51,10 +51,12 @@ fun mapToLegacyScriptReportPosition(pos: SourceCode.Location?): ScriptReport.Pos
pos?.let { ScriptReport.Position(pos.start.line, pos.start.col, pos.end?.line, pos.end?.col) } pos?.let { ScriptReport.Position(pos.start.line, pos.start.col, pos.end?.line, pos.end?.col) }
fun Iterable<ScriptReport>.mapToDiagnostics(): List<ScriptDiagnostic> = map { (message, severity, position) -> fun Iterable<ScriptReport>.mapToDiagnostics(): List<ScriptDiagnostic> = map { (message, severity, position) ->
ScriptDiagnostic(message, mapLegacyDiagnosticSeverity(severity), null, mapLegacyScriptPosition(position)) ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError, message, mapLegacyDiagnosticSeverity(severity), null, mapLegacyScriptPosition(position)
)
} }
fun Iterable<ScriptDiagnostic>.mapToLegacyReports(): List<ScriptReport> = map { (message, severity, _, location, exception) -> fun Iterable<ScriptDiagnostic>.mapToLegacyReports(): List<ScriptReport> = map { (_, message, severity, _, location, exception) ->
val reportMessage = if (exception == null) message else "$message ($exception)" val reportMessage = if (exception == null) message else "$message ($exception)"
ScriptReport(reportMessage, mapToLegacyScriptReportSeverity(severity), mapToLegacyScriptReportPosition(location)) ScriptReport(reportMessage, mapToLegacyScriptReportSeverity(severity), mapToLegacyScriptReportPosition(location))
} }
@@ -86,6 +86,7 @@ class KJvmCompiledScript<out ScriptBase : Any> internal constructor(
} catch (e: Throwable) { } catch (e: Throwable) {
ResultWithDiagnostics.Failure( ResultWithDiagnostics.Failure(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
"Unable to instantiate class ${data.scriptClassFQName}", "Unable to instantiate class ${data.scriptClassFQName}",
sourcePath = sourceLocationId, sourcePath = sourceLocationId,
exception = e exception = e
@@ -109,6 +109,7 @@ class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) { fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) {
diagnostics.add( diagnostics.add(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message, message,
mapLegacyDiagnosticSeverity(severity), mapLegacyDiagnosticSeverity(severity),
context.script.locationId, context.script.locationId,
@@ -49,7 +49,9 @@ class ScriptCompilationConfigurationFromDefinition(
}.orEmpty() }.orEmpty()
) )
val reports = resolveResult.reports.map { ScriptDiagnostic(it.message, mapLegacyDiagnosticSeverity(it.severity)) } val reports = resolveResult.reports.map {
ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, it.message, mapLegacyDiagnosticSeverity(it.severity))
}
val resolvedDeps = (resolveResult as? DependenciesResolver.ResolveResult.Success)?.dependencies val resolvedDeps = (resolveResult as? DependenciesResolver.ResolveResult.Success)?.dependencies
if (resolvedDeps == null) ResultWithDiagnostics.Failure(reports) if (resolvedDeps == null) ResultWithDiagnostics.Failure(reports)
@@ -43,10 +43,11 @@ class JsScriptCompilerWithDependenciesProxy(private val environment: KotlinCoreE
) )
} }
is ReplCompileResult.Incomplete -> ResultWithDiagnostics.Failure( is ReplCompileResult.Incomplete -> ResultWithDiagnostics.Failure(
ScriptDiagnostic("Incomplete code") ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, "Incomplete code")
) )
is ReplCompileResult.Error -> ResultWithDiagnostics.Failure( is ReplCompileResult.Error -> ResultWithDiagnostics.Failure(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = compileResult.message, message = compileResult.message,
severity = ScriptDiagnostic.Severity.ERROR severity = ScriptDiagnostic.Severity.ERROR
) )
@@ -44,7 +44,7 @@ internal class ScriptDiagnosticsMessageCollector(private val parentMessageCollec
) )
) )
} }
_diagnostics.add(ScriptDiagnostic(message, mappedSeverity, location?.path, mappedLocation)) _diagnostics.add(ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, message, mappedSeverity, location?.path, mappedLocation))
} }
parentMessageCollector?.report(severity, message, location) parentMessageCollector?.report(severity, message, location)
} }
@@ -57,12 +57,14 @@ fun getScriptKtFile(
return when { return when {
ktFile == null -> ResultWithDiagnostics.Failure( ktFile == null -> ResultWithDiagnostics.Failure(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = "Cannot create PSI", message = "Cannot create PSI",
severity = ScriptDiagnostic.Severity.ERROR severity = ScriptDiagnostic.Severity.ERROR
) )
) )
ktFile.declarations.firstIsInstanceOrNull<KtScript>() == null -> ResultWithDiagnostics.Failure( ktFile.declarations.firstIsInstanceOrNull<KtScript>() == null -> ResultWithDiagnostics.Failure(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = "There is not Script", message = "There is not Script",
severity = ScriptDiagnostic.Severity.ERROR severity = ScriptDiagnostic.Severity.ERROR
) )