Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length=100

[*.{kt, kts}]
kotlin_imports_layout = ascii
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ import java.lang.reflect.Type
import kotlin.annotation.AnnotationRetention.RUNTIME

@JsonClass(generateAdapter = true)
data class ExampleClass(
val type: Int,
@JsonString val rawJson: String,
)
data class ExampleClass(val type: Int, @JsonString val rawJson: String)

@Retention(RUNTIME)
@JsonQualifier
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ kotlinCompileTesting = { module = "dev.zacsweers.kctfork:core", version.ref = "k
kotlinCompileTesting-ksp = { module = "dev.zacsweers.kctfork:ksp", version.ref ="kotlinCompileTesting" }
truth = "com.google.truth:truth:1.4.5"
googleJavaFormat = "com.google.googlejavaformat:google-java-format:1.32.0"
ktlint = "com.pinterest.ktlint:ktlint-cli:1.3.1"
ktlint = "com.pinterest.ktlint:ktlint-cli:1.4.1"
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,12 @@ public class AdapterGenerator(
.initializer("null")
.build()

public fun prepare(generateProguardRules: Boolean, typeHook: (TypeSpec) -> TypeSpec = { it }): PreparedAdapter {
public fun prepare(
generateProguardRules: Boolean,
typeHook: (TypeSpec) -> TypeSpec = {
it
},
): PreparedAdapter {
val reservedSimpleNames = mutableSetOf<String>()
for (property in nonTransientProperties) {
// Allocate names for simple property types first to avoid collisions
Expand Down Expand Up @@ -234,7 +239,11 @@ public class AdapterGenerator(
result.superclass(jsonAdapterTypeName)

if (typeVariables.isNotEmpty()) {
result.addTypeVariables(typeVariables.map { it.stripTypeVarVariance(typeVariableResolver) as TypeVariableName })
result.addTypeVariables(
typeVariables.map {
it.stripTypeVarVariance(typeVariableResolver) as TypeVariableName
},
)
// require(types.size == 1) {
// "TypeVariable mismatch: Expecting 1 type(s) for generic type variables [T], but received ${types.size} with values $types"
// }
Expand Down Expand Up @@ -709,7 +718,10 @@ public class AdapterGenerator(
}
}

private fun FunSpec.Builder.addMissingPropertyCheck(property: PropertyGenerator, readerParam: ParameterSpec) {
private fun FunSpec.Builder.addMissingPropertyCheck(
property: PropertyGenerator,
readerParam: ParameterSpec,
) {
val missingPropertyBlock =
CodeBlock.of(
"%M(%S, %S, %N)",
Expand Down Expand Up @@ -772,16 +784,14 @@ private sealed class FromJsonComponent {

abstract val type: TypeName

data class ParameterOnly(
override val parameter: TargetParameter,
) : FromJsonComponent(),
data class ParameterOnly(override val parameter: TargetParameter) :
FromJsonComponent(),
ParameterComponent {
override val type: TypeName = parameter.type
}

data class PropertyOnly(
override val property: PropertyGenerator,
) : FromJsonComponent(),
data class PropertyOnly(override val property: PropertyGenerator) :
FromJsonComponent(),
PropertyComponent {
override val type: TypeName = property.target.type
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ public data class DelegateKey(
"At${it.typeName.rawType().simpleName}"
}
val adapterName = nameAllocator.newName(
"${type.toVariableName().replaceFirstChar { it.lowercase(Locale.US) }}${qualifierNames}Adapter",
"${type.toVariableName().replaceFirstChar {
it.lowercase(Locale.US)
}}${qualifierNames}Adapter",
this,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public class PropertyGenerator(
* This is used to indicate that presence should be checked first before possible assigning null
* to an absent value
*/
public val hasLocalIsPresentName: Boolean = !isTransient && hasDefault && !hasConstructorParameter && delegateKey.nullable
public val hasLocalIsPresentName: Boolean =
!isTransient && hasDefault && !hasConstructorParameter && delegateKey.nullable
public val hasConstructorDefault: Boolean = hasDefault && hasConstructorParameter

internal fun allocateNames(nameAllocator: NameAllocator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,20 @@ internal fun TypeName.findRawType(): ClassName? {
}
}

internal fun TypeName.defaultPrimitiveValue(): CodeBlock =
when (this) {
BOOLEAN -> CodeBlock.of("false")
CHAR -> CodeBlock.of("0.toChar()")
BYTE -> CodeBlock.of("0.toByte()")
SHORT -> CodeBlock.of("0.toShort()")
INT -> CodeBlock.of("0")
FLOAT -> CodeBlock.of("0f")
LONG -> CodeBlock.of("0L")
DOUBLE -> CodeBlock.of("0.0")
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
else -> CodeBlock.of("null")
}
internal fun TypeName.defaultPrimitiveValue(): CodeBlock = when (this) {
BOOLEAN -> CodeBlock.of("false")
CHAR -> CodeBlock.of("0.toChar()")
BYTE -> CodeBlock.of("0.toByte()")
SHORT -> CodeBlock.of("0.toShort()")
INT -> CodeBlock.of("0")
FLOAT -> CodeBlock.of("0f")
LONG -> CodeBlock.of("0L")
DOUBLE -> CodeBlock.of("0.0")
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException(
"Parameter with void, Unit, or Nothing type is illegal",
)
else -> CodeBlock.of("null")
}

@OptIn(DelicateKotlinPoetApi::class)
internal fun TypeName.asTypeBlock(): CodeBlock {
Expand Down Expand Up @@ -128,19 +129,25 @@ internal fun TypeName.asTypeBlock(): CodeBlock {
}
}

UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException(
"Parameter with void, Unit, or Nothing type is illegal",
)

else -> CodeBlock.of("%T::class.java", copy(nullable = false))
}
}

else -> throw UnsupportedOperationException("Parameter with type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, or type variables are allowed.")
else -> throw UnsupportedOperationException(
"Parameter with type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, or type variables are allowed.",
)
}
}

internal fun KModifier.checkIsVisibility() {
require(ordinal <= ordinal) {
"Visibility must be one of ${(0..ordinal).joinToString { KModifier.values()[it].name }}. Is $name"
"Visibility must be one of ${(0..ordinal).joinToString {
KModifier.values()[it].name
}}. Is $name"
}
}

Expand All @@ -156,7 +163,9 @@ internal fun TypeName.stripTypeVarVariance(resolver: TypeVariableResolver): Type

is WildcardTypeName -> deepCopy { it.stripTypeVarVariance(resolver) }

else -> throw UnsupportedOperationException("Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.")
else -> throw UnsupportedOperationException(
"Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.",
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ internal fun TypeName.unwrapTypeAlias(): TypeName {
unwrapTypeAliasInternal() ?: deepCopy(TypeName::unwrapTypeAlias)
}

Dynamic -> throw UnsupportedOperationException("Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.")
Dynamic -> throw UnsupportedOperationException(
"Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ internal class AppliedType private constructor(
) {

/** Returns all super classes of this, recursively. Only [CLASS] is used as we can't really use other types. */
fun superclasses(
resolver: Resolver,
): LinkedHashSet<AppliedType> {
fun superclasses(resolver: Resolver): LinkedHashSet<AppliedType> {
val result: LinkedHashSet<AppliedType> = LinkedHashSet()
result.add(this)
for (supertype in type.getAllSuperTypes()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ public class JsonClassSymbolProcessorProvider : SymbolProcessorProvider {
}
}

private class JsonClassSymbolProcessor(
environment: SymbolProcessorEnvironment,
) : SymbolProcessor {
private class JsonClassSymbolProcessor(environment: SymbolProcessorEnvironment) : SymbolProcessor {

private companion object {
val JSON_CLASS_NAME = JsonClass::class.qualifiedName!!
Expand All @@ -62,7 +60,8 @@ private class JsonClassSymbolProcessor(
"Invalid option value for $OPTION_GENERATED. Found $it, allowable values are ${POSSIBLE_GENERATED_NAMES.keys}."
}
}
private val generateProguardRules = environment.options[OPTION_GENERATE_PROGUARD_RULES]?.toBooleanStrictOrNull() ?: true
private val generateProguardRules =
environment.options[OPTION_GENERATE_PROGUARD_RULES]?.toBooleanStrictOrNull() ?: true

override fun process(resolver: Resolver): List<KSAnnotated> {
val generatedAnnotation = generatedOption?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ internal fun KSAnnotation.toAnnotationSpec(resolver: Resolver): AnnotationSpec {
return builder.build()
}

private fun addValueToBlock(value: Any, resolver: Resolver, member: CodeBlock.Builder, annotationContext: KSClassDeclaration? = null) {
private fun addValueToBlock(
value: Any,
resolver: Resolver,
member: CodeBlock.Builder,
annotationContext: KSClassDeclaration? = null,
) {
when (value) {
is List<*> -> {
// Array type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,12 @@ import com.squareup.moshi.kotlin.codegen.api.TargetType
import com.squareup.moshi.kotlin.codegen.api.unwrapTypeAlias

/** Returns a target type for [type] or null if it cannot be used with code gen. */
internal fun targetType(
type: KSDeclaration,
resolver: Resolver,
logger: KSPLogger,
): TargetType? {
internal fun targetType(type: KSDeclaration, resolver: Resolver, logger: KSPLogger): TargetType? {
if (type !is KSClassDeclaration) {
logger.error("@JsonClass can't be applied to ${type.qualifiedName?.asString()}: must be a Kotlin class", type)
logger.error(
"@JsonClass can't be applied to ${type.qualifiedName?.asString()}: must be a Kotlin class",
type,
)
return null
}
logger.check(type.classKind != ClassKind.ENUM_CLASS, type) {
Expand Down Expand Up @@ -117,7 +116,9 @@ internal fun targetType(
"""
@JsonClass can't be applied to $type: supertype $superclass is not a Kotlin type.
Origin=${classDecl.origin}
Annotations=${classDecl.annotations.joinToString(prefix = "[", postfix = "]") { it.shortName.getShortName() }}
Annotations=${classDecl.annotations.joinToString(prefix = "[", postfix = "]") {
it.shortName.getShortName()
}}
""".trimIndent(),
type,
)
Expand Down Expand Up @@ -269,8 +270,10 @@ private fun KSPropertyDeclaration.toPropertySpec(
addAnnotations(
[email protected]
.mapNotNull {
if ((it.annotationType.resolve().unwrapTypeAlias().declaration as KSClassDeclaration).isJsonQualifier
) {
val isJsonQualifier =
(it.annotationType.resolve().unwrapTypeAlias().declaration as KSClassDeclaration)
.isJsonQualifier
if (isJsonQualifier) {
it.toAnnotationSpec(resolver)
} else {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,19 @@ import kotlin.reflect.KClass
* Copied experimental utilities from KSP.
*/

internal fun <T : Annotation> KSAnnotated.getAnnotationsByType(annotationKClass: KClass<T>): Sequence<T> {
internal fun <T : Annotation> KSAnnotated.getAnnotationsByType(
annotationKClass: KClass<T>,
): Sequence<T> {
return this.annotations.filter {
it.shortName.getShortName() == annotationKClass.simpleName &&
it.annotationType.resolve().declaration
.qualifiedName?.asString() == annotationKClass.qualifiedName
}.map { it.toAnnotation(annotationKClass.java) }
}

internal fun <T : Annotation> KSAnnotated.isAnnotationPresent(annotationKClass: KClass<T>): Boolean =
getAnnotationsByType(annotationKClass).firstOrNull() != null
internal fun <T : Annotation> KSAnnotated.isAnnotationPresent(
annotationKClass: KClass<T>,
): Boolean = getAnnotationsByType(annotationKClass).firstOrNull() != null

@Suppress("UNCHECKED_CAST")
private fun <T : Annotation> KSAnnotation.toAnnotation(annotationClass: Class<T>): T {
Expand Down Expand Up @@ -81,7 +84,9 @@ private fun KSAnnotation.createInvocationHandler(clazz: Class<*>): InvocationHan
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")
throw IllegalStateException(
"unhandled value type, please file a bug at https://github.com/google/ksp/issues/new",
)
}
}
method.returnType.isEnum -> {
Expand Down Expand Up @@ -135,9 +140,7 @@ private fun KSAnnotation.createInvocationHandler(clazz: Class<*>): InvocationHan
}

@Suppress("UNCHECKED_CAST")
private fun KSAnnotation.asAnnotation(
annotationInterface: Class<*>,
): Any {
private fun KSAnnotation.asAnnotation(annotationInterface: Class<*>): Any {
return Proxy.newProxyInstance(
this.javaClass.classLoader,
arrayOf(annotationInterface),
Expand All @@ -149,15 +152,25 @@ private fun KSAnnotation.asAnnotation(
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>).asClasses(proxyClass).toTypedArray()

"java.lang.String" -> (this as List<String>).toTypedArray()

else -> { // arrays of enums or annotations
when {
method.returnType.componentType.isEnum -> {
Expand All @@ -168,7 +181,9 @@ private fun List<*>.asArray(method: Method, proxyClass: Class<*>) =
(result as KSAnnotation).asAnnotation(method.returnType.componentType)
}
}
else -> throw IllegalStateException("Unable to process type ${method.returnType.componentType.name}")
else -> throw IllegalStateException(
"Unable to process type ${method.returnType.componentType.name}",
)
}
}
}
Expand All @@ -190,6 +205,7 @@ private fun <T> Any.asEnum(returnType: Class<T>): T =
returnType.getDeclaredMethod("valueOf", String::class.java)
.invoke(
null,

if (this is KSType) {
this.declaration.simpleName.getShortName()
} else if (this is KSClassDeclaration) {
Expand All @@ -210,10 +226,12 @@ private fun Any.asFloat(): Float = if (this is Int) this.toFloat() else this as
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)
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)
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)
Expand All @@ -227,7 +245,8 @@ private fun List<KSType>.asClasses(proxyClass: Class<*>) = try {
throw KSTypesNotPresentException(this, e)
}

private fun Any.asArray(method: Method, proxyClass: Class<*>) = listOf(this).asArray(method, proxyClass)
private fun Any.asArray(method: Method, proxyClass: Class<*>) =
listOf(this).asArray(method, proxyClass)

private fun KSDeclaration.toJavaClassName(): String {
val nameDelimiter = '.'
Expand Down
Loading