Check REPL snippet syntax before calling analysis
restores the behaviour of the GenericRepl implementation and fixes issues then invalid code is being compiled #KT-34888 fixed
This commit is contained in:
+7
@@ -44,6 +44,13 @@ class LegacyReplTest : TestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
fun testReplSyntaxErrorsChecked() {
|
||||
LegacyTestRepl().use { repl ->
|
||||
val res = repl.compileAndEval(repl.nextCodeLine("data class Q(val x: Int, val: String)"))
|
||||
TestCase.assertTrue("Expected compile error", res.first is ReplCompileResult.Error)
|
||||
}
|
||||
}
|
||||
|
||||
fun testReplCodeFormat() {
|
||||
LegacyTestRepl().use { repl ->
|
||||
val codeLine0 = ReplCodeLine(0, 0, "val l1 = 1\r\nl1\r\n")
|
||||
|
||||
+62
-32
@@ -10,6 +10,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.cli.common.repl.BasicReplStageHistory
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import kotlin.script.experimental.api.*
|
||||
@@ -28,9 +29,7 @@ class ReplTest : TestCase() {
|
||||
@Test
|
||||
fun testCompileAndEval() {
|
||||
val out = captureOut {
|
||||
chechEvaluateInRepl(
|
||||
simpleScriptompilationConfiguration,
|
||||
simpleScriptEvaluationConfiguration,
|
||||
checkEvaluateInRepl(
|
||||
sequenceOf(
|
||||
"val x = 3",
|
||||
"x + 4",
|
||||
@@ -44,9 +43,7 @@ class ReplTest : TestCase() {
|
||||
|
||||
@Test
|
||||
fun testEvalWithResult() {
|
||||
chechEvaluateInRepl(
|
||||
simpleScriptompilationConfiguration,
|
||||
simpleScriptEvaluationConfiguration,
|
||||
checkEvaluateInRepl(
|
||||
sequenceOf(
|
||||
"val x = 5",
|
||||
"x + 6",
|
||||
@@ -58,9 +55,7 @@ class ReplTest : TestCase() {
|
||||
|
||||
@Test
|
||||
fun testEvalWithIfResult() {
|
||||
chechEvaluateInRepl(
|
||||
simpleScriptompilationConfiguration,
|
||||
simpleScriptEvaluationConfiguration,
|
||||
checkEvaluateInRepl(
|
||||
sequenceOf(
|
||||
"val x = 5",
|
||||
"x + 6",
|
||||
@@ -73,27 +68,25 @@ class ReplTest : TestCase() {
|
||||
@Test
|
||||
fun testImplicitReceiver() {
|
||||
val receiver = TestReceiver()
|
||||
chechEvaluateInRepl(
|
||||
simpleScriptompilationConfiguration.with {
|
||||
implicitReceivers(TestReceiver::class)
|
||||
},
|
||||
simpleScriptEvaluationConfiguration.with {
|
||||
implicitReceivers(receiver)
|
||||
},
|
||||
checkEvaluateInRepl(
|
||||
sequenceOf(
|
||||
"val x = 4",
|
||||
"x + prop1",
|
||||
"res1 * 3"
|
||||
),
|
||||
sequenceOf(null, 7, 21)
|
||||
sequenceOf(null, 7, 21),
|
||||
simpleScriptompilationConfiguration.with {
|
||||
implicitReceivers(TestReceiver::class)
|
||||
},
|
||||
simpleScriptEvaluationConfiguration.with {
|
||||
implicitReceivers(receiver)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEvalWithError() {
|
||||
chechEvaluateInRepl(
|
||||
simpleScriptompilationConfiguration,
|
||||
simpleScriptEvaluationConfiguration,
|
||||
checkEvaluateInRepl(
|
||||
sequenceOf(
|
||||
"throw RuntimeException(\"abc\")",
|
||||
"val x = 3",
|
||||
@@ -102,12 +95,30 @@ class ReplTest : TestCase() {
|
||||
sequenceOf(RuntimeException("abc"), null, 4)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSyntaxErrors() {
|
||||
checkEvaluateInReplDiags(
|
||||
sequenceOf(
|
||||
"data class Q(val x: Int, val: String)",
|
||||
"fun g(): Unit { return }}",
|
||||
"fun f() : Int { return 1",
|
||||
"6*7"
|
||||
),
|
||||
sequenceOf(
|
||||
makeFailureResult("Parameter name expected"),
|
||||
makeFailureResult("Unexpected symbol"),
|
||||
makeFailureResult("Expecting '}'"),
|
||||
42.asSuccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun evaluateInRepl(
|
||||
compilationConfiguration: ScriptCompilationConfiguration,
|
||||
evaluationConfiguration: ScriptEvaluationConfiguration?,
|
||||
snippets: Sequence<String>
|
||||
snippets: Sequence<String>,
|
||||
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptompilationConfiguration,
|
||||
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration
|
||||
): Sequence<ResultWithDiagnostics<EvaluationResult>> {
|
||||
val replCompilerProxy =
|
||||
KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
|
||||
@@ -139,18 +150,24 @@ fun evaluateInRepl(
|
||||
}
|
||||
}
|
||||
|
||||
fun chechEvaluateInRepl(
|
||||
compilationConfiguration: ScriptCompilationConfiguration,
|
||||
evaluationConfiguration: ScriptEvaluationConfiguration?,
|
||||
fun checkEvaluateInReplDiags(
|
||||
snippets: Sequence<String>,
|
||||
expected: Sequence<Any?>
|
||||
expected: Sequence<ResultWithDiagnostics<Any?>>,
|
||||
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptompilationConfiguration,
|
||||
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration
|
||||
) {
|
||||
val expectedIter = expected.iterator()
|
||||
evaluateInRepl(compilationConfiguration, evaluationConfiguration, snippets).forEachIndexed { index, res ->
|
||||
when (res) {
|
||||
is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res")
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
val expectedVal = expectedIter.next()
|
||||
evaluateInRepl(snippets, compilationConfiguration, evaluationConfiguration).forEachIndexed { index, res ->
|
||||
val expectedRes = expectedIter.next()
|
||||
when {
|
||||
res is ResultWithDiagnostics.Failure && expectedRes is ResultWithDiagnostics.Failure -> {
|
||||
Assert.assertTrue(
|
||||
"#$index: Expected $expectedRes, got $res",
|
||||
res.reports.map { it.message } == expectedRes.reports.map { it.message }
|
||||
)
|
||||
}
|
||||
res is ResultWithDiagnostics.Success && expectedRes is ResultWithDiagnostics.Success -> {
|
||||
val expectedVal = expectedRes.value
|
||||
when (val resVal = res.value.returnValue) {
|
||||
is ResultValue.Value -> Assert.assertEquals(
|
||||
"#$index: Expected $expectedVal, got $resVal",
|
||||
@@ -165,8 +182,21 @@ fun chechEvaluateInRepl(
|
||||
else -> Assert.assertTrue("#$index: Expected $expectedVal, got unknown result $resVal", expectedVal == null)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Assert.fail("#$index: Expected $expectedRes, got $res")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkEvaluateInRepl(
|
||||
snippets: Sequence<String>,
|
||||
expected: Sequence<Any?>,
|
||||
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptompilationConfiguration,
|
||||
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration
|
||||
) = checkEvaluateInReplDiags(
|
||||
snippets, expected.map { ResultWithDiagnostics.Success(it) }, compilationConfiguration,
|
||||
evaluationConfiguration
|
||||
)
|
||||
|
||||
class TestReceiver(val prop1: Int = 3)
|
||||
|
||||
+8
-8
@@ -60,15 +60,15 @@ class ResolveDependenciesTest : TestCase() {
|
||||
|
||||
@Test
|
||||
fun testReplResolveFunAndValFromClassloader() {
|
||||
chechEvaluateInRepl(
|
||||
configurationWithDependenciesFromClassloader, null,
|
||||
sequenceOf(funAndValAccessScriptText, funAndValAccessScriptText),
|
||||
sequenceOf(42, 42)
|
||||
checkEvaluateInRepl(
|
||||
sequenceOf(funAndValAccessScriptText, funAndValAccessScriptText), sequenceOf(42, 42),
|
||||
configurationWithDependenciesFromClassloader,
|
||||
null
|
||||
)
|
||||
chechEvaluateInRepl(
|
||||
configurationWithDependenciesFromClassloader, null,
|
||||
funAndValImportScriptText.split('\n').asSequence(),
|
||||
sequenceOf(null, null, 42)
|
||||
checkEvaluateInRepl(
|
||||
funAndValImportScriptText.split('\n').asSequence(), sequenceOf(null, null, 42),
|
||||
configurationWithDependenciesFromClassloader,
|
||||
null
|
||||
)
|
||||
runScriptAndCheckResult(funAndValImportScript, configurationWithDependenciesFromClassloader, null, 42)
|
||||
}
|
||||
|
||||
+3
@@ -96,6 +96,9 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
|
||||
)
|
||||
.valueOr { return it }
|
||||
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(snippetKtFile, errorHolder)
|
||||
if (syntaxErrorReport.isHasErrors) return failure(messageCollector)
|
||||
|
||||
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(
|
||||
context,
|
||||
snippetKtFile,
|
||||
|
||||
Reference in New Issue
Block a user