[JS BE] Make sourceMap generation more precise

- Don't produce mapping for closing bracket in case of expressionBody
 - Map Kt*Function declaration into corresponding js fun declaration
 - Update test data & add new test
This commit is contained in:
Roman Artemev
2019-07-23 18:51:58 +03:00
committed by romanart
parent 12a1b0296b
commit 46b98a1e98
56 changed files with 98 additions and 57 deletions
@@ -605,6 +605,8 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitFunction(@NotNull JsFunction x) {
pushSourceInfo(x.getSource());
p.print(CHARS_FUNCTION);
space();
if (x.getName() != null) {
@@ -624,10 +626,12 @@ public class JsToStringGenerationVisitor extends JsVisitor {
lineBreakAfterBlock = false;
sourceLocationConsumer.pushSourceInfo(null);
printJsBlock(x.getBody(), true, x.getSource());
printJsBlock(x.getBody(), true, x.getBody().getSource());
sourceLocationConsumer.popSourceInfo();
needSemi = true;
popSourceInfo();
}
@Override
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.js.coroutine
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
import org.jetbrains.kotlin.js.backend.ast.metadata.forceStateMachine
@@ -132,7 +133,8 @@ class CoroutineFunctionTransformer(private val function: JsFunction, name: Strin
val psiElement = context.metadata.psiElement
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
constructor.source = psiElement?.finalElement
constructor.source = psiElement
constructor.body.source = psiElement?.finalElement as? LeafPsiElement
constructor.name = className
if (context.metadata.hasReceiver) {
constructor.parameters += JsParameter(context.receiverFieldName)
@@ -202,7 +204,9 @@ class CoroutineFunctionTransformer(private val function: JsFunction, name: Strin
statements: MutableList<JsStatement>
) {
val resumeFunction = JsFunction(function.scope.parent, JsBlock(), "resume function")
resumeFunction.source = context.metadata.psiElement?.finalElement
val psi = context.metadata.psiElement
resumeFunction.source = psi
resumeFunction.body.source = psi?.finalElement as? LeafPsiElement
val coroutineBody = generateCoroutineBody(context, coroutineBlocks)
functionWithBody.body.statements.clear()
@@ -346,6 +346,10 @@ public class Node implements Cloneable {
return position;
}
public void setPosition(CodePosition position) {
this.position = position;
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public double getDouble() throws UnsupportedOperationException {
throw new UnsupportedOperationException(this + " is not a number node");
@@ -222,7 +222,6 @@ public class Parser {
int savedFunctionNumber = functionNumber;
Node args;
Node body;
CodePosition closingBracketPosition;
try {
functionNumber = 0;
args = nf.createLeaf(TokenStream.LP, ts.tokenPosition);
@@ -241,7 +240,7 @@ public class Parser {
mustMatchToken(ts, TokenStream.LC, "msg.no.brace.body");
body = parseFunctionBody(ts);
closingBracketPosition = ts.tokenPosition;
body.setPosition(ts.tokenPosition);
mustMatchToken(ts, TokenStream.RC, "msg.no.brace.after.body");
// skip the last EOL so nested functions work...
}
@@ -250,7 +249,7 @@ public class Parser {
functionNumber = savedFunctionNumber;
}
Node pn = nf.createFunction(nameNode, args, body, closingBracketPosition);
Node pn = nf.createFunction(nameNode, args, body, basePosition);
if (memberExprNode != null) {
pn = nf.createBinary(TokenStream.ASSIGN, TokenStream.NOP, memberExprNode, pn, basePosition);
}
@@ -39,6 +39,11 @@ public class SourceMapGenerationSmokeTestGenerated extends AbstractSourceMapGene
runTest("js/js.translator/testData/sourcemap/emptyIfInsideInlineLambda.kt");
}
@TestMetadata("expressionBody.kt")
public void testExpressionBody() throws Exception {
runTest("js/js.translator/testData/sourcemap/expressionBody.kt");
}
@TestMetadata("methodCallInMethod.kt")
public void testMethodCallInMethod() throws Exception {
runTest("js/js.translator/testData/sourcemap/methodCallInMethod.kt");
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.translate.declaration
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -106,7 +107,8 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor<Unit>() {
context: TranslationContext
): Pair<JsExpression, TranslationContext> {
val function = context.getFunctionObject(descriptor)
function.source = expression.finalElement
function.source = expression
function.body.source = expression.finalElement as? LeafPsiElement
val innerContext = context.newDeclaration(descriptor).translateAndAliasParameters(descriptor, function.parameters)
if (descriptor.isSuspend) {
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.translate.declaration
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
@@ -261,14 +262,17 @@ private class PropertyTranslator(
private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer {
val descriptor = BindingUtils.getFunctionDescriptor(bindingContext(), expression)
val function = JsFunction(context().getScopeForDescriptor(descriptor), JsBlock(), descriptor.toString())
function.source = expression.finalElement
function.source = expression
function.body.source = expression.finalElement as? LeafPsiElement
context().translateAndAliasParameters(descriptor, function.parameters).translateFunction(expression, function)
return translateFunctionAsEcma5PropertyDescriptor(function, descriptor, context())
}
private fun createFunction(descriptor: VariableAccessorDescriptor): JsFunction {
val function = JsFunction(context().getScopeForDescriptor(descriptor), JsBlock(), accessorDescription(descriptor))
function.source = descriptor.source.getPsi()?.finalElement
val psi = descriptor.source.getPsi()
function.source = psi
function.body.source = psi?.finalElement as? LeafPsiElement
return function
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.translate.expression
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.js.backend.ast.*
@@ -64,7 +65,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
lambda.body.statements += setDefaultValueForArguments(descriptor, functionContext)
lambda.body.statements += translateFunctionBody(descriptor, declaration, functionContext)
lambda.functionDescriptor = descriptor
lambda.source = finalElement
lambda.source = declaration
lambda.body.source = finalElement as? LeafPsiElement
val tracker = functionContext.usageTracker()!!
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.translate.reference
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.backend.ast.*
@@ -204,7 +205,7 @@ object CallableReferenceTranslator {
translator: (TranslationContext, ResolvedCall<out PropertyDescriptor>, JsExpression, JsExpression?) -> JsExpression
): JsExpression {
val accessorFunction = JsFunction(context.scope(), JsBlock(), "")
accessorFunction.source = expression.finalElement
accessorFunction.source = expression
val accessorContext = context.innerBlock(accessorFunction.body)
val receiverParam = if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) {
val name = JsScope.declareTemporaryName(Namer.getReceiverParameterName())
@@ -226,6 +227,7 @@ object CallableReferenceTranslator {
val accessorResult = translator(accessorContext, call, valueParam, receiverParam)
accessorFunction.body.statements += if (isSetter) accessorResult.makeStmt() else JsReturn(accessorResult)
accessorFunction.body.source = expression.finalElement as? LeafPsiElement
return bindIfNecessary(accessorFunction, receiver)
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.js.translate.utils
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.util.SmartList
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -90,7 +91,8 @@ fun generateDelegateCall(
invocation.source = source
val functionObject = simpleReturnFunction(context.scope(), invocation)
functionObject.source = source?.finalElement
functionObject.source = source
functionObject.body.source = source?.finalElement as? LeafPsiElement
functionObject.parameters.addAll(parameters)
if (functionObject.isSuspend) {
functionObject.fillCoroutineMetadata(context, fromDescriptor, false)
@@ -6,4 +6,4 @@ fun box(x: Int, y: Int) {
fun foo(x: Int) = x
// LINES: 5 3 3 4 3 3 3 3 * 3 3 4 4 4 4 * 4 4 4 4 2 3 7 7 7
// LINES: 1 5 3 3 4 3 3 3 3 * 3 3 4 4 4 4 * 4 4 4 4 2 3 7 7 7
+1 -1
View File
@@ -12,4 +12,4 @@ open class A {
}
}
// LINES: 1 2 2 4 4 2 2 2 2 5 5 5 6 12 8 9 10 11
// LINES: 1 2 2 4 4 2 2 2 2 5 5 5 6 7 12 8 9 10 11
+1 -1
View File
@@ -19,4 +19,4 @@ fun bar() {
}
}
// LINES: 11 3 3 5 5 6 6 8 8 9 9 2 2 * 20 15 15 18 18
// LINES: 1 11 3 3 5 5 6 6 8 8 9 9 2 2 * 13 20 15 15 18 18
+1 -1
View File
@@ -14,4 +14,4 @@ class C {
val baz: dynamic get() = null
}
// LINES: 9 2 4 3 2 6 8 7 6 11 12 12 12 14 14 14
// LINES: 1 9 2 4 3 2 6 8 7 6 11 12 12 12 14 14 14
@@ -14,4 +14,4 @@ fun A.foo() {
fun baz() = 23
// LINES: 1 * 5 5 5 9 7 7 8 8 * 13 4 4 11 11 12 12 15 15 15
// LINES: 1 * 5 5 5 6 9 7 7 8 8 * 3 13 4 4 11 11 12 12 15 15 15
+1 -1
View File
@@ -5,4 +5,4 @@ fun box(x: Int, y: Int): Int {
return foo(y)
}
// LINES: 2 2 2 4 4 6 2 2 5 5
// LINES: 2 2 2 4 4 1 6 2 2 5 5
@@ -11,4 +11,4 @@ fun baz() = 1
fun bar() = 2
// LINES: 8 3 3 2 2 4 3 4 4 4 5 5 2 8 8 10 10 10 12 12 12
// LINES: 1 3 3 2 2 4 3 4 4 4 5 5 2 8 8 10 10 10 12 12 12
@@ -14,4 +14,4 @@ private inline fun foo(): Int {
return 23
}
// LINES: 10 3 3 3 4 3 6 13 13 3 14 2 15 13 13 14 14
// LINES: 1 10 3 3 3 4 3 6 13 13 3 14 2 12 15 13 13 14 14
+1 -1
View File
@@ -14,4 +14,4 @@ suspend fun bar(): Unit {
println(a + b)
}
// LINES: 38 4 4 4 5 5 44 44 5 89 44 5 5 6 4 4 4 15 9 9 9 * 15 10 10 11 11 11 11 11 * 11 12 12 13 13 13 13 13 13 13 14 14 * 15 9 9 9 9
// LINES: 38 4 4 4 7 5 5 44 44 5 89 44 5 5 6 4 4 4 9 15 9 9 9 * 9 15 10 10 11 11 11 11 11 * 11 12 12 13 13 13 13 13 13 13 14 14 * 9 15 9 9 9 9
@@ -8,4 +8,4 @@ suspend fun foo() {
suspend fun delay() {
}
// LINES: 6 1 1 * 6 2 2 2 2 2 * 3 3 4 4 4 4 4 5 5 * 6 1 1 1 1 9
// LINES: 1 6 1 1 * 1 6 2 2 2 2 2 * 3 3 4 4 4 4 4 5 5 * 1 6 1 1 1 1 8 9
+1 -1
View File
@@ -13,4 +13,4 @@ class Delegate(val f: () -> Int) {
}
}
// LINES: 1 2 3 * 2 2 3 3 3 * 8 7 7 10 10 13 12 12
// LINES: 1 2 3 * 2 2 3 3 3 3 * 6 8 7 7 10 10 11 13 12 12
+1 -1
View File
@@ -15,4 +15,4 @@ class B {
}
}
// LINES: 3 4 5 * 4 4 4 * 8 11 10 10 15 14 14
// LINES: 3 4 5 * 4 4 4 * 8 9 11 10 10 13 15 14 14
+1 -1
View File
@@ -18,4 +18,4 @@ val o = object : I {
}
}
// LINES: 1 2 3 2 2 2 2 2 2 2 * 11 12 12 12 15 15 15 18 17 17
// LINES: 1 2 3 2 2 2 2 2 2 2 * 11 12 12 12 15 15 15 16 18 17 17
+1 -1
View File
@@ -27,4 +27,4 @@ fun bar(f: (Pair<String, String>) -> Unit) {
}
// LINES: 22 16 16 17 18 20 20 21 21 22 22 23 9 9 9 9 4 9 9 9 5 5 6 7 11 11 12 12 15 15 27 26 26 * 1 * 1
// LINES: 15 22 16 16 17 18 20 20 21 21 22 22 3 23 9 9 9 9 4 9 9 9 5 5 6 7 11 11 12 12 15 15 25 27 26 26 * 1 * 1
@@ -32,4 +32,4 @@ inline operator fun P.component1() = a
inline operator fun P.component2() = b
// LINES: 22 17 17 31 18 18 33 20 20 21 21 22 22 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
// LINES: 15 22 17 17 31 18 18 33 20 20 21 21 22 22 3 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 25 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
@@ -13,4 +13,4 @@ fun box() {
)
}
// LINES: 14 8 8 7 3 2 2 3 3 3 8 8 11 11 * 3 7 12 3 3 4 4
// LINES: 1 14 8 8 7 3 2 2 3 3 3 8 8 11 11 * 3 7 12 3 3 4 4
+1 -1
View File
@@ -9,4 +9,4 @@ fun box(x: String?) {
fun foo() = "bar"
// LINES: 8 7 7 4 4 4 5 5 7 7 7 7 * 5 4 5 10 10 10 * 1 * 1
// LINES: 3 8 7 7 4 4 4 5 5 7 7 7 7 * 5 4 5 10 10 10 * 1 * 1
@@ -4,4 +4,4 @@ fun box() =
fun foo() =
23
// LINES: 2 2 2 5 5 5
// LINES: 1 2 2 4 5 5
+1 -1
View File
@@ -17,4 +17,4 @@ fun box() {
}
}
// LINES: 18 2 2 10 2 2 2 2 2 2 3 3 6 6 6 6 7 7 10 10 10 10 10 10 11 11 14 14 15 15 15 15 16 16
// LINES: 1 18 2 2 10 2 2 2 2 2 2 3 3 6 6 6 6 7 7 10 10 10 10 10 10 11 11 14 14 15 15 15 15 16 16
+1 -1
View File
@@ -8,4 +8,4 @@ fun foo(x: Int) {
println(y)
}
// LINES: 9 2 2 3 3 4 4 5 5 6 6 7 7 8 8
// LINES: 1 9 2 2 3 3 4 4 5 5 6 6 7 7 8 8
+1 -1
View File
@@ -11,4 +11,4 @@ inline fun foo(x: Int) {
fun bar() = 23
// LINES: 5 4 4 8 8 9 9 10 8 8 9 9 12 12 12 * 1 * 1
// LINES: 3 5 4 4 8 8 9 9 7 10 8 8 9 9 12 12 12 * 1 * 1
@@ -9,4 +9,4 @@ fun bar() {
foo(42)
}
// LINES: 1 1 1 1 1 6 2 2 3 3 4 4 10 2 2 9 2 3 3 4 4
// LINES: 1 1 1 1 1 6 2 2 3 3 4 4 8 10 2 2 9 2 3 3 4 4
@@ -21,4 +21,4 @@ fun box() {
foo("42")
}
// LINES: 6 22 7 7 20 7 8 8 20 8 7 7 21 7 8 8 21 8
// LINES: 6 19 22 7 7 20 7 8 8 20 8 7 7 21 7 8 8 21 8
+1 -1
View File
@@ -21,4 +21,4 @@ fun bar(x: Int) {
println("%")
}
// LINES: 1 1 1 1 1 1 1 1 1 17 2 2 3 3 4 7 7 9 9 10 10 11 14 14 16 16 22 20 20 20 20 2 2 3 3 4 3 7 7 9 9 10 10 11 10 14 14 16 16 * 20 21 21
// LINES: 1 1 1 1 1 1 1 1 1 17 2 2 3 3 4 7 7 9 9 10 10 11 14 14 16 16 19 22 20 20 20 20 17 2 2 3 3 4 3 7 7 9 9 10 10 11 10 14 14 16 16 20 21 21
+1 -1
View File
@@ -11,4 +11,4 @@ inline fun bar() {
println("bar2")
}
// LINES: 7 2 2 10 10 11 11 4 4 10 10 11 11 6 6 9 9 9 9 9 12 10 10 11 11
// LINES: 1 7 2 2 10 10 11 11 4 4 10 10 11 11 6 6 9 9 9 9 9 12 10 10 11 11
@@ -16,4 +16,4 @@ inline fun foo(f: () -> Unit) {
println("after")
}
// LINES: 11 2 2 14 14 4 4 16 16 6 6 14 14 8 8 16 16 10 10 13 13 13 13 13 17 14 14 15 15 16 16
// LINES: 1 11 2 2 14 14 4 4 16 16 6 6 14 14 8 8 16 16 10 10 13 13 13 13 13 17 14 14 15 15 16 16
+1 -1
View File
@@ -6,4 +6,4 @@ class A(val x: Int) {
}
}
// LINES: 1 1 2 2 5 4 4
// LINES: 1 1 2 2 3 5 4 4
+1 -1
View File
@@ -11,4 +11,4 @@ inline fun foo(): Boolean {
return true
}
// LINES: 7 4 4 4 10 10 4 11 4 5 5 12 10 10 11 11 * 1 * 1
// LINES: 3 7 4 4 4 10 10 4 11 4 5 5 9 12 10 10 11 11 * 1 * 1
+1 -1
View File
@@ -5,4 +5,4 @@ fun foo() {
println("after: $x")
}
// LINES: 6 2 2 3 3 4 4 5 5
// LINES: 1 6 2 2 3 3 4 4 5 5
+1 -1
View File
@@ -4,4 +4,4 @@ fun foo(x: Int): () -> Unit = {
fun bar() = 23
// LINES: 1 1 1 2 2 3 3 1 1 1 5 5 5
// LINES: 1 1 1 3 2 2 3 3 1 1 1 5 5 5
@@ -24,4 +24,4 @@ fun baz() = "baz"
fun boo() = "boo"
// LINES: 17 4 6 6 7 7 3 3 3 * 13 12 13 13 14 21 20 20 23 23 23 25 25 25
// LINES: 1 17 9 4 6 6 7 7 3 3 3 * 13 12 13 13 14 19 21 20 20 23 23 23 25 25 25
+1 -1
View File
@@ -26,4 +26,4 @@ fun bar(vararg x: Any?) {
println(x)
}
// LINES: 1 22 3 4 5 6 7 8 9 10 13 14 15 16 17 18 19 20 * 27 26 26
// LINES: 1 2 22 3 4 5 6 7 8 9 10 13 14 15 16 17 18 19 20 * 25 27 26 26
+1 -1
View File
@@ -5,4 +5,4 @@ fun foo() {
0L)
}
// LINES: 3 * 5 6 2 4
// LINES: 3 * 5 1 6 2 4
@@ -10,4 +10,4 @@ class B : A() {
}
}
// LINES: 1 2 2 2 2 2 2 2 2 2 4 4 4 * 7 7 10 9 9
// LINES: 1 2 2 2 2 2 2 2 2 2 4 4 4 * 7 7 8 10 9 9
@@ -12,4 +12,4 @@ object O {
val y = 23
}
// LINES: 9 2 2 3 3 4 4 6 6 7 7 8 8 11 11 12 12 * 11 11 11 11 11 11
// LINES: 1 9 2 2 3 3 4 4 6 6 7 7 8 8 11 11 12 12 * 11 11 11 11 11 11
@@ -4,4 +4,4 @@ fun foo() {
println("foo")
}
// LINES: 1 1 * 1 1 1 1 1 1 5 4 4
// LINES: 1 1 * 1 1 1 1 1 1 3 5 4 4
+1 -1
View File
@@ -7,4 +7,4 @@ fun box(
println(y)
}
// LINES: 8 2 2 2 2 3 3 3 4 6 6 7 7
// LINES: 1 8 2 2 2 2 3 3 3 4 6 6 7 7
+1 -1
View File
@@ -3,4 +3,4 @@ fun box() {
println("bar")
}
// LINES: 4 2 2 3 3
// LINES: 1 4 2 2 3 3
+1 -1
View File
@@ -4,4 +4,4 @@ fun box(x: Int): String {
"suffix"
}
// LINES: 5 2 2 3 4
// LINES: 1 5 2 2 3 4
+1 -1
View File
@@ -7,4 +7,4 @@ fun foo() {
A(23, "foo")
}
// LINES: 1 2 3 * 8 7 7
// LINES: 1 2 3 * 6 8 7 7
@@ -18,4 +18,4 @@ fun box(x: Int) {
)
}
// LINES: 19 4 4 3 3 4 6 9 9 6 11 13 13 11 16 16 3 2
// LINES: 1 19 4 4 3 3 4 6 9 9 6 11 13 13 11 16 16 3 2
@@ -28,4 +28,4 @@ fun four() = 4
fun five() = 5
// LINES: 19 4 4 3 6 4 6 4 7 4 8 9 9 11 4 11 4 12 13 13 16 16 2 21 21 21 23 23 23 25 25 25 27 27 27 29 29 29
// LINES: 1 19 4 4 3 6 4 6 4 7 4 8 9 9 11 4 11 4 12 13 13 16 16 2 21 21 21 23 23 23 25 25 25 27 27 27 29 29 29
+1 -1
View File
@@ -13,4 +13,4 @@ fun foo(): Int = 23
fun bar(): IntRange = 1000..2000
// LINES: 10 2 2 2 2 3 3 4 4 5 5 6 6 7 7 8 8 12 12 12 14 14 14
// LINES: 1 10 2 2 2 2 3 3 4 4 5 5 6 6 7 7 8 8 12 12 12 14 14 14
+1 -1
View File
@@ -13,4 +13,4 @@ open class A
open class B : A()
// LINES: 10 2 2 2 2 3 3 4 4 5 5 6 6 8 8 12 * 14 14
// LINES: 1 10 2 2 2 2 3 3 4 4 5 5 6 6 8 8 12 * 14 14
@@ -12,4 +12,4 @@ fun box() {
}
}
// LINES: 13 5 5 4 2 2 3 5 5 8 8 * 3 4 9 3 11 11
// LINES: 1 13 5 5 4 2 2 3 5 5 8 8 * 3 4 9 3 11 11
+13
View File
@@ -0,0 +1,13 @@
package foo
fun test1() = "O"
fun test2(s: String): String {
return s
}
fun test(s:String) =
test1() +
test2(s)
fun box()
= test("K")