[REPL] Fix syntax errors analysis and incompleteness detection

This commit is contained in:
Ilya Muradyan
2021-03-29 14:37:10 +03:00
parent 736efe0403
commit d40ada5d07
5 changed files with 45 additions and 12 deletions
@@ -92,7 +92,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
snippet, snippet,
messageCollector, messageCollector,
compilationState, compilationState,
checkSyntaxErrors = true failOnSyntaxErrors = true
).valueOr { return@withMessageCollector it } ).valueOr { return@withMessageCollector it }
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment( val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(
@@ -242,7 +242,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
snippet: SourceCode, snippet: SourceCode,
parentMessageCollector: MessageCollector, parentMessageCollector: MessageCollector,
compilationState: JvmReplCompilerState.Compilation, compilationState: JvmReplCompilerState.Compilation,
checkSyntaxErrors: Boolean failOnSyntaxErrors: Boolean
): ResultWithDiagnostics<AnalyzePreparationResult> = ): ResultWithDiagnostics<AnalyzePreparationResult> =
withMessageCollector( withMessageCollector(
snippet, snippet,
@@ -269,15 +269,11 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
) )
.valueOr { return it } .valueOr { return it }
if (checkSyntaxErrors) { val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(snippetKtFile, errorHolder)
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(snippetKtFile, errorHolder) if (syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof) {
if (syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof) return failure( messageCollector.report(ScriptDiagnostic(ScriptDiagnostic.incompleteCode, "Incomplete code"))
messageCollector, ScriptDiagnostic(ScriptDiagnostic.incompleteCode, "Incomplete code")
)
if (syntaxErrorReport.isHasErrors) return failure(
messageCollector
)
} }
if (failOnSyntaxErrors && syntaxErrorReport.isHasErrors) return failure(messageCollector)
return AnalyzePreparationResult( return AnalyzePreparationResult(
context, context,
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import org.jetbrains.kotlin.cli.common.arguments.Argument import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
@@ -58,6 +59,28 @@ class ScriptDiagnosticsMessageCollector(private val parentMessageCollector: Mess
} }
parentMessageCollector?.report(severity, message, location) parentMessageCollector?.report(severity, message, location)
} }
fun report(diagnostic: ScriptDiagnostic) {
_diagnostics.add(diagnostic)
if (parentMessageCollector == null) return
if (parentMessageCollector is ScriptDiagnosticsMessageCollector) {
parentMessageCollector.report(diagnostic)
return
}
val locationStart = diagnostic.location?.start
parentMessageCollector.report(
diagnostic.severity.toCompilerMessageSeverity(),
diagnostic.message,
CompilerMessageLocation.create(
null,
locationStart?.line ?: -1,
locationStart?.col ?: -1,
null
)
)
}
} }
private fun CompilerMessageSeverity.toScriptingSeverity(): ScriptDiagnostic.Severity? = when (this) { private fun CompilerMessageSeverity.toScriptingSeverity(): ScriptDiagnostic.Severity? = when (this) {
@@ -198,6 +198,20 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
} }
} }
@Test
fun testIncompleteCode() = test {
run {
doErrorCheck
code = "fun g(): Int { return 1"
expect {
addError(1, 24, 1, 24, "Expecting '}'", "ERROR")
errors.add(ScriptDiagnostic(ScriptDiagnostic.incompleteCode, "Incomplete code"))
}
}
}
@Test @Test
fun testCompletionDuplication() = test { fun testCompletionDuplication() = test {
for (i in 1..6) { for (i in 1..6) {
@@ -283,7 +283,7 @@ private suspend fun checkEvaluateInRepl(
checkLists(index, "completions", expectedCompletions.list, completionsRes, expectedCompletions) checkLists(index, "completions", expectedCompletions.list, completionsRes, expectedCompletions)
val expectedErrorsWithPath = expectedErrors.list.map { val expectedErrorsWithPath = expectedErrors.list.map {
it.copy(sourcePath = errorsRes.firstOrNull()?.sourcePath) if (it.location != null) it.copy(sourcePath = errorsRes.firstOrNull()?.sourcePath) else it
} }
checkLists(index, "errors", expectedErrorsWithPath, errorsRes, expectedErrors) checkLists(index, "errors", expectedErrorsWithPath, errorsRes, expectedErrors)
TestCase.assertEquals("Analysis result types are different", expectedResultType, resultType) TestCase.assertEquals("Analysis result types are different", expectedResultType, resultType)
@@ -111,7 +111,7 @@ class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfigurat
newSnippet, newSnippet,
messageCollector, messageCollector,
compilationState, compilationState,
checkSyntaxErrors = false failOnSyntaxErrors = false
).valueOr { return it } ).valueOr { return it }
val analysisResult = val analysisResult =