Adds ability to resolve XConstructorElement into an executable type.
This CL generalizes some types and methods from XMethodElement and
XMethodType so that they also apply to constructors. This is needed
because constructors can also reference type parameters from their
parameter list and we need to be able to resolve those for a particular
type -- similar to XMethodElement#asMemberOf(XType).
To do this, I've moved XMethodElement#executableType() and
XMethodElement#asMemberOf() into XExecutableElement so that both methods
and constructors can get executable types. XExecutableType is a new super
type for both the existing XMethodType and the new XConstructorType.
Test: ./gradlew :room:r-c-p:test
Change-Id: I79b88bdc6a1c5194fbc5d715aaf6481c0e057ef7
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt
index fff671c..3053849 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt
@@ -37,4 +37,15 @@
)
append(")")
}
+
+ /** The type representation of the method where more type parameters might be resolved. */
+ override val executableType: XConstructorType
+
+ /**
+ * Returns the constructor as if it is declared in [other].
+ *
+ * This is specifically useful if you have a constructor that has type arguments and there is a
+ * subclass ([other]) where type arguments are specified to actual types.
+ */
+ override fun asMemberOf(other: XType): XConstructorType
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorType.kt
new file mode 100644
index 0000000..3c8332d
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorType.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing
+
+/**
+ * Represents a type information for a constructor.
+ *
+ * It is not an XType as it does not represent a class or primitive.
+ */
+interface XConstructorType : XExecutableType
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableElement.kt
index 815f45e..0da3ed0 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableElement.kt
@@ -49,4 +49,17 @@
* Returns true if this method receives a vararg parameter.
*/
fun isVarArgs(): Boolean
+
+ /**
+ * The type representation of the method where more type parameters might be resolved.
+ */
+ val executableType: XExecutableType
+
+ /**
+ * Returns the method as if it is declared in [other].
+ *
+ * This is specifically useful if you have a method that has type arguments and there is a
+ * subclass ([other]) where type arguments are specified to actual types.
+ */
+ fun asMemberOf(other: XType): XExecutableType
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableType.kt
new file mode 100644
index 0000000..f9f6a09
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableType.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing
+
+/**
+ * Represents a type information for a method or constructor.
+ *
+ * It is not an XType as it does not represent a class or primitive.
+ */
+interface XExecutableType {
+ /** Parameter types of the method or constructor. */
+ val parameterTypes: List<XType>
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodElement.kt
index c87c982..5596483 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodElement.kt
@@ -37,7 +37,7 @@
/**
* The type representation of the method where more type parameters might be resolved.
*/
- val executableType: XMethodType
+ override val executableType: XMethodType
override val fallbackLocationText: String
get() = buildString {
@@ -69,7 +69,7 @@
* This is specifically useful if you have a method that has type arguments and there is a
* subclass ([other]) where type arguments are specified to actual types.
*/
- fun asMemberOf(other: XType): XMethodType
+ override fun asMemberOf(other: XType): XMethodType
/**
* Returns true if this method has a default implementation in Kotlin.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodType.kt
index 9dbb428..9fb09bc 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XMethodType.kt
@@ -24,18 +24,13 @@
*
* It is not an XType as it does not represent a class or primitive.
*/
-interface XMethodType {
+interface XMethodType : XExecutableType {
/**
* The return type of the method
*/
val returnType: XType
/**
- * Parameter types of the method.
- */
- val parameterTypes: List<XType>
-
- /**
* Returns the names of [TypeVariableName]s for this executable.
*/
val typeVariableNames: List<TypeVariableName>
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
index 19ae2d7..b3ad06c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
@@ -20,9 +20,9 @@
import androidx.room.compiler.processing.XAnnotationValue
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.XExecutableElement
+import androidx.room.compiler.processing.XExecutableType
import androidx.room.compiler.processing.XFiler
import androidx.room.compiler.processing.XMessager
-import androidx.room.compiler.processing.XMethodType
import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.XRoundEnv
import androidx.room.compiler.processing.XType
@@ -32,8 +32,8 @@
import androidx.room.compiler.processing.javac.JavacAnnotationValue
import androidx.room.compiler.processing.javac.JavacElement
import androidx.room.compiler.processing.javac.JavacExecutableElement
+import androidx.room.compiler.processing.javac.JavacExecutableType
import androidx.room.compiler.processing.javac.JavacFiler
-import androidx.room.compiler.processing.javac.JavacMethodType
import androidx.room.compiler.processing.javac.JavacProcessingEnv
import androidx.room.compiler.processing.javac.JavacProcessingEnvMessager
import androidx.room.compiler.processing.javac.JavacRoundEnv
@@ -89,7 +89,7 @@
fun XType.toJavac(): TypeMirror = (this as JavacType).typeMirror
@JvmStatic
- fun XMethodType.toJavac(): ExecutableType = (this as JavacMethodType).executableType
+ fun XExecutableType.toJavac(): ExecutableType = (this as JavacExecutableType).executableType
@JvmStatic
fun Element.toXProcessing(env: XProcessingEnv): XElement {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
index b9ca374..1eae5ff 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
@@ -17,8 +17,11 @@
package androidx.room.compiler.processing.javac
import androidx.room.compiler.processing.XConstructorElement
+import androidx.room.compiler.processing.XConstructorType
+import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XTypeElement
import androidx.room.compiler.processing.javac.kotlin.KmConstructor
+import com.google.auto.common.MoreTypes
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
@@ -42,6 +45,28 @@
element.requireEnclosingType(env)
}
+ override val executableType: XConstructorType by lazy {
+ val asMemberOf = env.typeUtils.asMemberOf(containing.type.typeMirror, element)
+ JavacConstructorType(
+ env = env,
+ element = this,
+ executableType = MoreTypes.asExecutable(asMemberOf)
+ )
+ }
+
+ override fun asMemberOf(other: XType): XConstructorType {
+ return if (other !is JavacDeclaredType || containing.type.isSameType(other)) {
+ executableType
+ } else {
+ val asMemberOf = env.typeUtils.asMemberOf(other.typeMirror, element)
+ JavacConstructorType(
+ env = env,
+ element = this,
+ executableType = MoreTypes.asExecutable(asMemberOf)
+ )
+ }
+ }
+
override val kotlinMetadata: KmConstructor? by lazy {
(enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getConstructorMetadata(element)
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorType.kt
new file mode 100644
index 0000000..3fa32b4
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorType.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.javac
+
+import androidx.room.compiler.processing.XConstructorType
+import javax.lang.model.type.ExecutableType
+
+internal class JavacConstructorType(
+ env: JavacProcessingEnv,
+ override val element: JavacConstructorElement,
+ executableType: ExecutableType
+) : JavacExecutableType(env, element, executableType), XConstructorType
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableType.kt
new file mode 100644
index 0000000..fa43af6
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableType.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.javac
+
+import androidx.room.compiler.processing.XExecutableType
+import javax.lang.model.type.ExecutableType
+
+internal abstract class JavacExecutableType(
+ val env: JavacProcessingEnv,
+ open val element: JavacExecutableElement,
+ val executableType: ExecutableType
+) : XExecutableType {
+
+ override val parameterTypes: List<JavacType> by lazy {
+ executableType.parameterTypes.mapIndexed { index, typeMirror ->
+ env.wrap(
+ typeMirror = typeMirror,
+ kotlinType = element.parameters[index].kotlinType,
+ elementNullability = element.parameters[index].element.nullability
+ )
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is JavacExecutableType) return false
+ return executableType == other.executableType
+ }
+
+ override fun hashCode(): Int {
+ return executableType.hashCode()
+ }
+
+ override fun toString(): String {
+ return executableType.toString()
+ }
+}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodType.kt
index cb42583..54287bb 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodType.kt
@@ -24,10 +24,10 @@
import javax.lang.model.type.ExecutableType
internal sealed class JavacMethodType(
- val env: JavacProcessingEnv,
- val element: JavacMethodElement,
- val executableType: ExecutableType
-) : XMethodType {
+ env: JavacProcessingEnv,
+ override val element: JavacMethodElement,
+ executableType: ExecutableType
+) : JavacExecutableType(env, element, executableType), XMethodType {
override val returnType: JavacType by lazy {
env.wrap<JavacType>(
typeMirror = executableType.returnType,
@@ -48,29 +48,6 @@
}
}
- override val parameterTypes: List<JavacType> by lazy {
- executableType.parameterTypes.mapIndexed { index, typeMirror ->
- env.wrap<JavacType>(
- typeMirror = typeMirror,
- kotlinType = element.parameters[index].kotlinType,
- elementNullability = element.parameters[index].element.nullability
- )
- }
- }
-
- override fun equals(other: Any?): Boolean {
- if (other !is JavacMethodType) return false
- return executableType == other.executableType
- }
-
- override fun hashCode(): Int {
- return executableType.hashCode()
- }
-
- override fun toString(): String {
- return executableType.toString()
- }
-
private class NormalMethodType(
env: JavacProcessingEnv,
element: JavacMethodElement,
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt
index fb3ff2c..e8ff39e 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt
@@ -17,6 +17,8 @@
package androidx.room.compiler.processing.ksp
import androidx.room.compiler.processing.XConstructorElement
+import androidx.room.compiler.processing.XConstructorType
+import androidx.room.compiler.processing.XType
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
internal class KspConstructorElement(
@@ -33,4 +35,21 @@
declaration.requireEnclosingMemberContainer(env) as? KspTypeElement
?: error("Constructor parent must be a type element $this")
}
+
+ override val executableType: XConstructorType by lazy {
+ KspConstructorType(
+ env = env,
+ origin = this,
+ containing = this.containing.type
+ )
+ }
+
+ override fun asMemberOf(other: XType): XConstructorType {
+ check(other is KspType)
+ return KspConstructorType(
+ env = env,
+ origin = this,
+ containing = other
+ )
+ }
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorType.kt
new file mode 100644
index 0000000..87e4bef
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorType.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp
+
+import androidx.room.compiler.processing.XConstructorType
+
+internal class KspConstructorType(
+ env: KspProcessingEnv,
+ override val origin: KspConstructorElement,
+ containing: KspType?
+) : KspExecutableType(env, origin, containing), XConstructorType
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableType.kt
new file mode 100644
index 0000000..93e8ce0
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableType.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp
+
+import androidx.room.compiler.processing.XConstructorType
+import androidx.room.compiler.processing.XType
+
+internal abstract class KspExecutableType(
+ val env: KspProcessingEnv,
+ open val origin: KspExecutableElement,
+ val containing: KspType?
+) : XConstructorType {
+ override val parameterTypes: List<XType> by lazy {
+ if (containing == null) {
+ origin.parameters.map {
+ it.type
+ }
+ } else {
+ origin.parameters.map {
+ it.asMemberOf(containing)
+ }
+ }
+ }
+}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
index 7907934..c6c7cf70 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
@@ -22,22 +22,10 @@
import com.squareup.javapoet.TypeVariableName
internal sealed class KspMethodType(
- val env: KspProcessingEnv,
- val origin: KspMethodElement,
- val containing: KspType?
-) : XMethodType {
- override val parameterTypes: List<XType> by lazy {
- if (containing == null) {
- origin.parameters.map {
- it.type
- }
- } else {
- origin.parameters.map {
- it.asMemberOf(containing)
- }
- }
- }
-
+ env: KspProcessingEnv,
+ override val origin: KspMethodElement,
+ containing: KspType?
+) : KspExecutableType(env, origin, containing), XMethodType {
override val typeVariableNames: List<TypeVariableName> by lazy {
origin.declaration.typeParameters.map {
val typeParameterBounds = it.bounds.map {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index cb0c6aa..b1d44e3 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -23,13 +23,65 @@
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.typeName
import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
import com.squareup.javapoet.WildcardTypeName
import org.junit.Test
class XExecutableTypeTest {
@Test
+ fun constructorInheritanceResolution() {
+ runProcessorTest(
+ sources = listOf(
+ Source.kotlin(
+ "KotlinClass.kt",
+ """
+ abstract class KotlinClass<T> constructor(t: T) {
+ abstract fun foo(): KotlinClass<String>
+ }
+ """.trimIndent()
+ ),
+ Source.java(
+ "JavaClass.java",
+ """
+ abstract class JavaClass<T> {
+ JavaClass(T t) {}
+ abstract JavaClass<String> foo();
+ }
+ """.trimIndent()
+ )
+ )
+ ) { invocation ->
+ fun checkConstructor(className: String) {
+ val typeElement = invocation.processingEnv.requireTypeElement(className)
+ val constructorElement = typeElement.getConstructors().single()
+ val constructorType = constructorElement.executableType
+
+ // Assert that the XConstructorElement parameter is unresolved type, T.
+ // TODO(10/06/2021): Should KSP also return T?
+ if (invocation.isKsp) {
+ assertThat(constructorType.parameterTypes.single().typeName)
+ .isEqualTo(TypeVariableName.OBJECT)
+ } else {
+ assertThat(constructorType.parameterTypes.single().typeName)
+ .isEqualTo(TypeVariableName.get("T"))
+ }
+
+ val resolvedType = typeElement.getDeclaredMethods().single().returnType
+ val resolvedConstructorType = constructorElement.asMemberOf(resolvedType)
+
+ // Assert that the XConstructorType parameter is resolved type, String
+ assertThat(resolvedConstructorType.parameterTypes.single().typeName)
+ .isEqualTo(ClassName.get("java.lang", "String"))
+ }
+ checkConstructor("JavaClass")
+ checkConstructor("KotlinClass")
+ }
+ }
+
+ @Test
fun inheritanceResolution() {
val src = Source.kotlin(
"Foo.kt",