Update KSP to 20201120
I've also updated KSP implementation to treat internals as KAPT.
This means, any method that receives or returns inline is hidden.
Method / property names are now read from KSP which properly modifies
them to have their jvm names applies (including internals).
Unfortunately, this hits a bug in KSP. I've safe guarded it for now
only where it reproduces to avoid hiding possible other places where
it might occur.
https://github.com/google/ksp/issues/164
Bug: 160322705
Test: InternalModifierTest
Change-Id: I739de215f40a453f4ddfb82290c969e0478de471
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 50f90376..02db235 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -77,7 +77,7 @@
"com.squareup:kotlinpoet-classinspector-elements:1.4.0"
const val KOTLIN_COMPILE_TESTING = "com.github.tschuchortdev:kotlin-compile-testing:1.3.1"
const val KOTLIN_COMPILE_TESTING_KSP = "com.github.tschuchortdev:kotlin-compile-testing-ksp:1.3.1"
-const val KSP_VERSION = "1.4.10-dev-experimental-20201110"
+const val KSP_VERSION = "1.4.10-dev-experimental-20201120"
const val KOTLIN_KSP_API = "com.google.devtools.ksp:symbol-processing-api:$KSP_VERSION"
const val KOTLIN_KSP = "com.google.devtools.ksp:symbol-processing:$KSP_VERSION"
const val KOTLIN_GRADLE_PLUGIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
diff --git a/room/compiler-processing/build.gradle b/room/compiler-processing/build.gradle
index 8f3616f..795a691 100644
--- a/room/compiler-processing/build.gradle
+++ b/room/compiler-processing/build.gradle
@@ -16,6 +16,7 @@
import androidx.build.LibraryGroups
import androidx.build.LibraryType
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import static androidx.build.dependencies.DependenciesKt.*
@@ -56,11 +57,13 @@
testImplementation(project(":room:room-compiler-processing-testing"))
}
-compileKotlin {
+tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
- freeCompilerArgs += "-Xopt-in=kotlin.contracts.ExperimentalContracts"
+ freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn",
+ "-Xopt-in=kotlin.contracts.ExperimentalContracts"]
}
}
+
androidx {
name = "AndroidX Room XProcessor"
type = LibraryType.ANNOTATION_PROCESSOR
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
index 61e7271..31b2ad9 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
@@ -18,6 +18,7 @@
import androidx.room.compiler.processing.javac.kotlin.typeNameFromJvmSignature
import androidx.room.compiler.processing.tryBox
+import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.processing.Resolver
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
@@ -29,6 +30,7 @@
import com.google.devtools.ksp.symbol.KSTypeArgument
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.Variance
import com.squareup.javapoet.ArrayTypeName
@@ -51,6 +53,7 @@
/**
* Turns a KSDeclaration into a TypeName in java's type system.
*/
+@OptIn(KspExperimental::class)
internal fun KSDeclaration.typeName(resolver: Resolver): TypeName {
// if there is no qualified name, it is a resolution error so just return shared instance
// KSP may improve that later and if not, we can improve it in Room
@@ -147,4 +150,6 @@
internal fun KSTypeReference.isTypeParameterReference(): Boolean {
return this.resolve().declaration is KSTypeParameter
-}
\ No newline at end of file
+}
+
+fun KSType.isInline() = declaration.modifiers.contains(Modifier.INLINE)
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
index 8f8e32d..90f46c7 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
@@ -46,7 +46,7 @@
declaration.simpleName.asString()
}
- override val type: XType by lazy {
+ override val type: KspType by lazy {
env.wrap(
originatingReference = declaration.type,
ksType = declaration.typeAsMemberOf(env.resolver, containing.type.ksType)
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
index c88abf7..48841e32 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
@@ -23,6 +23,7 @@
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XTypeElement
import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticContinuationParameterElement
+import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
@@ -39,8 +40,15 @@
),
XMethodElement {
+ @OptIn(KspExperimental::class)
override val name: String by lazy {
- declaration.simpleName.asString()
+ try {
+ env.resolver.getJvmName(declaration)
+ } catch (ignored: ClassCastException) {
+ // TODO remove this catch once that issue is fixed.
+ // workaround for https://github.com/google/ksp/issues/164
+ declaration.simpleName.asString()
+ }
}
override val executableType: XMethodType by lazy {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index f4575aa..eb88523 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -147,6 +147,11 @@
private val syntheticGetterSetterMethods: List<XMethodElement> by lazy {
val setters = _declaredPropertyFields.mapNotNull {
+ if (it.type.ksType.isInline()) {
+ // KAPT does not generate getters/setters for inlines, we'll hide them as well
+ // until room generates kotlin code
+ return@mapNotNull null
+ }
val setter = it.declaration.setter
val needsSetter = if (setter != null) {
// kapt does not generate synthetics for private fields/setters so we won't either
@@ -164,6 +169,11 @@
}
}
val getters = _declaredPropertyFields.mapNotNull {
+ if (it.type.ksType.isInline()) {
+ // KAPT does not generate getters/setters for inlines, we'll hide them as well
+ // until room generates kotlin code
+ return@mapNotNull null
+ }
val getter = it.declaration.getter
val needsGetter = if (getter != null) {
// kapt does not generate synthetics for private fields/getters so we won't either]
@@ -211,17 +221,23 @@
}
private val _declaredMethods by lazy {
- val myMethods = declaration.getDeclaredFunctions()
- .filter {
+ val myMethods = declaration.getDeclaredFunctions().asSequence()
+ .filterNot {
// filter out constructors
- it.simpleName.asString() != name
+ it.simpleName.asString() == name
+ }.filterNot {
+ // if it receives or returns inline, drop it.
+ // we can re-enable these once room generates kotlin code
+ it.parameters.any {
+ it.type.resolve().isInline()
+ } || it.returnType?.resolve()?.isInline() == true
}.map {
KspMethodElement.create(
env = env,
containing = this,
declaration = it
)
- }
+ }.toList()
val companionMethods = declaration.findCompanionObject()
?.let {
env.wrapClassDeclaration(it)
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index a6c4e46..868a69c 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -35,6 +35,7 @@
import androidx.room.compiler.processing.ksp.KspProcessingEnv
import androidx.room.compiler.processing.ksp.KspTypeElement
import androidx.room.compiler.processing.ksp.overrides
+import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.KSPropertyAccessor
import java.util.Locale
@@ -117,7 +118,11 @@
arrayOf(field, "getter")
}
+ @OptIn(KspExperimental::class)
override val name: String by lazy {
+ field.declaration.getter?.let {
+ return@lazy env.resolver.getJvmName(it)
+ }
// see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#properties
val propName = field.name
if (propName.startsWith("is")) {
@@ -168,7 +173,11 @@
arrayOf(field, "setter")
}
+ @OptIn(KspExperimental::class)
override val name: String by lazy {
+ field.declaration.setter?.let {
+ return@lazy env.resolver.getJvmName(it)
+ }
// see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#properties
val propName = field.name
if (propName.startsWith("is")) {
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
new file mode 100644
index 0000000..f604802
--- /dev/null
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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
+
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.runKaptTest
+import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class InternalModifierTest {
+ @OptIn(ExperimentalStdlibApi::class)
+ @Test
+ fun testInternalsAndInlines() {
+ /**
+ * parse same file w/ kapt and KSP and ensure results are the same.
+ */
+ val source = Source.kotlin(
+ "Subject.kt",
+ """
+ package mainPackage;
+ internal class InternalClass(val value: String)
+ inline class InlineClass(val value:String)
+ class Subject {
+ var normalProp: String = TODO()
+ var inlineProp: InlineClass = TODO()
+ internal var internalProp: String = TODO()
+ internal var internalInlineProp: InlineClass = TODO()
+ private var internalTypeProp : InternalClass = TODO()
+ @get:JvmName("explicitGetterName")
+ @set:JvmName("explicitSetterName")
+ var jvmNameProp:String
+ fun normalFun() {}
+ @JvmName("explicitJvmName")
+ fun hasJvmName() {}
+ fun inlineReceivingFun(value: InlineClass) {}
+ fun inlineReturningFun(): InlineClass = TODO()
+ internal fun internalInlineReceivingFun(value: InlineClass) {}
+ internal fun internalInlineReturningFun(): InlineClass = TODO()
+ inline fun inlineFun() {
+ TODO()
+ }
+ }
+ """.trimIndent()
+ )
+
+ fun XType.toSignature() = this.typeName.toString()
+
+ fun XFieldElement.toSignature() = "$name : ${type.toSignature()}"
+
+ fun XMethodElement.toSignature() = buildString {
+ append(name)
+ append("(")
+ parameters.forEach {
+ append(it.type.toSignature())
+ }
+ append(")")
+ append(":")
+ append(returnType.toSignature())
+ }
+
+ fun traverse(env: XProcessingEnv) = buildList<String> {
+ val subject = env.requireTypeElement("mainPackage.Subject")
+ add(subject.name)
+ add(subject.qualifiedName)
+ subject.getDeclaredMethods().forEach {
+ add(it.toSignature())
+ }
+ subject.getAllFieldsIncludingPrivateSupers().map {
+ add(it.toSignature())
+ }
+ }.sorted().joinToString("\n")
+
+ var kaptResult: String = "pending"
+ var kspResult: String = "pending"
+ runKaptTest(
+ sources = listOf(source)
+ ) { invocation ->
+ kaptResult = traverse(invocation.processingEnv)
+ }
+
+ runKspTest(
+ sources = listOf(source)
+ ) { invocation ->
+ kspResult = traverse(invocation.processingEnv)
+ }
+
+ assertThat(kspResult).isEqualTo(kaptResult)
+ }
+}
\ No newline at end of file