blob: 21df2cf48762e3ae31d8257ccb19cec3c72353b9 [file] [log] [blame]
/*
* 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.XExecutableElement
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.Nullability
internal fun Resolver.findClass(qName: String) = getClassDeclarationByName(
getKSNameFromString(qName)
)
internal fun Resolver.requireClass(qName: String) = checkNotNull(findClass(qName)) {
"cannot find class $qName"
}
internal fun Resolver.requireType(qName: String) = requireClass(qName).asType(emptyList())
internal fun Resolver.requireContinuationClass() = requireClass("kotlin.coroutines.Continuation")
private fun XExecutableElement.getDeclarationForOverride(): KSDeclaration = when (this) {
is KspExecutableElement -> this.declaration
is KspSyntheticPropertyMethodElement -> this.field.declaration
else -> throw IllegalStateException("unexpected XExecutableElement type. $this")
}
internal fun Resolver.overrides(
overriderElement: XMethodElement,
overrideeElement: XMethodElement
): Boolean {
// in addition to functions declared in kotlin, we also synthesize getter/setter functions for
// properties which means we cannot simply send the declaration to KSP for override check
// (otherwise, it won't give us a definitive answer when java methods override property
// getters /setters or even we won't be able to distinguish between our own Getter/Setter
// synthetics).
// By cheaply checking parameter counts, we avoid all those cases and if KSP returns true, it
// won't include false positives.
if (overriderElement.parameters.size != overrideeElement.parameters.size) {
return false
}
val ksOverrider = overriderElement.getDeclarationForOverride()
val ksOverridee = overrideeElement.getDeclarationForOverride()
if (overrides(ksOverrider, ksOverridee)) {
// Make sure it also overrides in JVM descriptors as well.
// This happens in cases where parent class has `<T>` type argument and child class
// declares it has `Int` (a type that might map to a primitive). In those cases,
// KAPT generates two methods, 1 w/ primitive and 1 boxed so we replicate that behavior
// here. This code would change when we generate kotlin code.
if (ksOverridee is KSFunctionDeclaration && ksOverrider is KSFunctionDeclaration) {
return ksOverrider.overridesInJvm(ksOverridee)
}
return true
}
return false
}
/**
* If the overrider specifies a primitive value for a type argument, ignore the override as
* kotlin will generate two class methods for them.
*
* see: b/160258066 for details
*/
private fun KSFunctionDeclaration.overridesInJvm(
other: KSFunctionDeclaration
): Boolean {
parameters.forEachIndexed { index, myParam ->
val myParamType = myParam.type.resolve()
if (myParamType.nullability == Nullability.NOT_NULL) {
val myParamDecl = myParamType.declaration
val paramQName = myParamDecl.qualifiedName?.asString()
if (paramQName != null &&
KspTypeMapper.getPrimitiveJavaTypeName(paramQName) != null
) {
// parameter is a primitive. Check if the parent declared it as a type argument,
// in which case, we should ignore the override.
val otherParamDeclaration = other.parameters
.getOrNull(index)?.type?.resolve()?.declaration
if (otherParamDeclaration is KSTypeParameter) {
return false
}
}
}
}
return true
}