mirror of
https://github.com/fankes/moshi.git
synced 2025-10-18 23:49:21 +08:00
Kotlin 2.1 and friends (#1966)
* Kotlin 2.1 and friends * fix kotlin 2.1 aliases nullability (#1982) --------- Co-authored-by: Florian LE FICHER <florian.leficher@gmail.com>
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen.ksp
|
||||
|
||||
import com.google.devtools.ksp.getClassDeclarationByName
|
||||
import com.google.devtools.ksp.processing.KSPLogger
|
||||
import com.google.devtools.ksp.processing.Resolver
|
||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
|
@@ -30,8 +30,10 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSType
|
||||
import com.google.devtools.ksp.symbol.KSTypeAlias
|
||||
import com.google.devtools.ksp.symbol.KSTypeParameter
|
||||
import com.google.devtools.ksp.symbol.Modifier
|
||||
import com.google.devtools.ksp.symbol.Nullability
|
||||
import com.google.devtools.ksp.symbol.Origin
|
||||
import com.squareup.kotlinpoet.AnnotationSpec
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
@@ -258,7 +260,8 @@ private fun KSPropertyDeclaration.toPropertySpec(
|
||||
): PropertySpec {
|
||||
return PropertySpec.builder(
|
||||
name = simpleName.getShortName(),
|
||||
type = resolvedType.toTypeName(typeParameterResolver).unwrapTypeAlias(),
|
||||
type = resolvedType.toTypeName(typeParameterResolver).unwrapTypeAlias()
|
||||
.fixTypeAliasNullability(resolvedType),
|
||||
)
|
||||
.mutable(isMutable)
|
||||
.addModifiers(modifiers.map { KModifier.valueOf(it.name) })
|
||||
@@ -278,3 +281,13 @@ private fun KSPropertyDeclaration.toPropertySpec(
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun TypeName.fixTypeAliasNullability(resolvedType: KSType): TypeName {
|
||||
return if (resolvedType.declaration is KSTypeAlias) {
|
||||
copy(
|
||||
nullable = resolvedType.nullability == Nullability.NULLABLE,
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
@@ -16,10 +16,10 @@
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen.ksp
|
||||
|
||||
import com.google.devtools.ksp.processing.Resolver
|
||||
import com.google.devtools.ksp.symbol.KSAnnotated
|
||||
import com.google.devtools.ksp.symbol.KSAnnotation
|
||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSType
|
||||
import com.google.devtools.ksp.symbol.KSValueArgument
|
||||
import java.lang.reflect.InvocationHandler
|
||||
@@ -32,15 +32,6 @@ import kotlin.reflect.KClass
|
||||
* Copied experimental utilities from KSP.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Find a class in the compilation classpath for the given name.
|
||||
*
|
||||
* @param name fully qualified name of the class to be loaded; using '.' as separator.
|
||||
* @return a KSClassDeclaration, or null if not found.
|
||||
*/
|
||||
internal fun Resolver.getClassDeclarationByName(name: String): KSClassDeclaration? =
|
||||
getClassDeclarationByName(getKSNameFromString(name))
|
||||
|
||||
internal fun <T : Annotation> KSAnnotated.getAnnotationsByType(annotationKClass: KClass<T>): Sequence<T> {
|
||||
return this.annotations.filter {
|
||||
it.shortName.getShortName() == annotationKClass.simpleName &&
|
||||
@@ -74,46 +65,67 @@ private fun KSAnnotation.createInvocationHandler(clazz: Class<*>): InvocationHan
|
||||
"$methodName=$value"
|
||||
}.toList()
|
||||
} else {
|
||||
val argument = try {
|
||||
arguments.first { it.name?.asString() == method.name }
|
||||
} catch (e: NullPointerException) {
|
||||
throw IllegalArgumentException("This is a bug using the default KClass for an annotation", e)
|
||||
}
|
||||
val argument = arguments.first { it.name?.asString() == method.name }
|
||||
when (val result = argument.value ?: method.defaultValue) {
|
||||
is Proxy -> result
|
||||
|
||||
is List<*> -> {
|
||||
val value = { result.asArray(method) }
|
||||
val value = { result.asArray(method, clazz) }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
// Workaround for java annotation value array type
|
||||
// https://github.com/google/ksp/issues/1329
|
||||
method.returnType.isArray -> {
|
||||
if (result !is Array<*>) {
|
||||
val value = { result.asArray(method, clazz) }
|
||||
cache.getOrPut(Pair(method.returnType, value), value)
|
||||
} else {
|
||||
throw IllegalStateException("unhandled value type, please file a bug at https://github.com/google/ksp/issues/new")
|
||||
}
|
||||
}
|
||||
method.returnType.isEnum -> {
|
||||
val value = { result.asEnum(method.returnType) }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
|
||||
method.returnType.isAnnotation -> {
|
||||
val value = { (result as KSAnnotation).asAnnotation(method.returnType) }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
|
||||
method.returnType.name == "java.lang.Class" -> {
|
||||
val value = { (result as KSType).asClass() }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
cache.getOrPut(Pair(method.returnType, result)) {
|
||||
when (result) {
|
||||
is KSType -> result.asClass(clazz)
|
||||
// Handles com.intellij.psi.impl.source.PsiImmediateClassType using reflection
|
||||
// since api doesn't contain a reference to this
|
||||
else -> Class.forName(
|
||||
result.javaClass.methods
|
||||
.first { it.name == "getCanonicalText" }
|
||||
.invoke(result, false) as String,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method.returnType.name == "byte" -> {
|
||||
val value = { result.asByte() }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
|
||||
method.returnType.name == "short" -> {
|
||||
val value = { result.asShort() }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
|
||||
method.returnType.name == "long" -> {
|
||||
val value = { result.asLong() }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
method.returnType.name == "float" -> {
|
||||
val value = { result.asFloat() }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
method.returnType.name == "double" -> {
|
||||
val value = { result.asDouble() }
|
||||
cache.getOrPut(Pair(method.returnType, result), value)
|
||||
}
|
||||
else -> result // original value
|
||||
}
|
||||
}
|
||||
@@ -134,42 +146,28 @@ private fun KSAnnotation.asAnnotation(
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun List<*>.asArray(method: Method) =
|
||||
private fun List<*>.asArray(method: Method, proxyClass: Class<*>) =
|
||||
when (method.returnType.componentType.name) {
|
||||
"boolean" -> (this as List<Boolean>).toBooleanArray()
|
||||
|
||||
"byte" -> (this as List<Byte>).toByteArray()
|
||||
|
||||
"short" -> (this as List<Short>).toShortArray()
|
||||
|
||||
"char" -> (this as List<Char>).toCharArray()
|
||||
|
||||
"double" -> (this as List<Double>).toDoubleArray()
|
||||
|
||||
"float" -> (this as List<Float>).toFloatArray()
|
||||
|
||||
"int" -> (this as List<Int>).toIntArray()
|
||||
|
||||
"long" -> (this as List<Long>).toLongArray()
|
||||
|
||||
"java.lang.Class" -> (this as List<KSType>).map {
|
||||
Class.forName(it.declaration.qualifiedName!!.asString())
|
||||
}.toTypedArray()
|
||||
|
||||
"java.lang.Class" -> (this as List<KSType>).asClasses(proxyClass).toTypedArray()
|
||||
"java.lang.String" -> (this as List<String>).toTypedArray()
|
||||
|
||||
else -> { // arrays of enums or annotations
|
||||
when {
|
||||
method.returnType.componentType.isEnum -> {
|
||||
this.toArray(method) { result -> result.asEnum(method.returnType.componentType) }
|
||||
}
|
||||
|
||||
method.returnType.componentType.isAnnotation -> {
|
||||
this.toArray(method) { result ->
|
||||
(result as KSAnnotation).asAnnotation(method.returnType.componentType)
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("Unable to process type ${method.returnType.componentType.name}")
|
||||
}
|
||||
}
|
||||
@@ -192,9 +190,10 @@ private fun <T> Any.asEnum(returnType: Class<T>): T =
|
||||
returnType.getDeclaredMethod("valueOf", String::class.java)
|
||||
.invoke(
|
||||
null,
|
||||
// Change from upstream KSP - https://github.com/google/ksp/pull/685
|
||||
if (this is KSType) {
|
||||
this.declaration.simpleName.getShortName()
|
||||
} else if (this is KSClassDeclaration) {
|
||||
this.simpleName.getShortName()
|
||||
} else {
|
||||
this.toString()
|
||||
},
|
||||
@@ -204,4 +203,53 @@ private fun Any.asByte(): Byte = if (this is Int) this.toByte() else this as Byt
|
||||
|
||||
private fun Any.asShort(): Short = if (this is Int) this.toShort() else this as Short
|
||||
|
||||
private fun KSType.asClass() = Class.forName(this.declaration.qualifiedName!!.asString())
|
||||
private fun Any.asLong(): Long = if (this is Int) this.toLong() else this as Long
|
||||
|
||||
private fun Any.asFloat(): Float = if (this is Int) this.toFloat() else this as Float
|
||||
|
||||
private fun Any.asDouble(): Double = if (this is Int) this.toDouble() else this as Double
|
||||
|
||||
// for Class/KClass member
|
||||
internal class KSTypeNotPresentException(val ksType: KSType, cause: Throwable) : RuntimeException(cause)
|
||||
|
||||
// for Class[]/Array<KClass<*>> member.
|
||||
internal class KSTypesNotPresentException(val ksTypes: List<KSType>, cause: Throwable) : RuntimeException(cause)
|
||||
|
||||
private fun KSType.asClass(proxyClass: Class<*>) = try {
|
||||
Class.forName(this.declaration.toJavaClassName(), true, proxyClass.classLoader)
|
||||
} catch (e: Exception) {
|
||||
throw KSTypeNotPresentException(this, e)
|
||||
}
|
||||
|
||||
private fun List<KSType>.asClasses(proxyClass: Class<*>) = try {
|
||||
this.map { type -> type.asClass(proxyClass) }
|
||||
} catch (e: Exception) {
|
||||
throw KSTypesNotPresentException(this, e)
|
||||
}
|
||||
|
||||
private fun Any.asArray(method: Method, proxyClass: Class<*>) = listOf(this).asArray(method, proxyClass)
|
||||
|
||||
private fun KSDeclaration.toJavaClassName(): String {
|
||||
val nameDelimiter = '.'
|
||||
val packageNameString = packageName.asString()
|
||||
val qualifiedNameString = qualifiedName!!.asString()
|
||||
val simpleNames = qualifiedNameString
|
||||
.removePrefix("${packageNameString}$nameDelimiter")
|
||||
.split(nameDelimiter)
|
||||
|
||||
return if (simpleNames.size > 1) {
|
||||
buildString {
|
||||
append(packageNameString)
|
||||
append(nameDelimiter)
|
||||
|
||||
simpleNames.forEachIndexed { index, s ->
|
||||
if (index > 0) {
|
||||
append('$')
|
||||
}
|
||||
append(s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qualifiedNameString
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user