Skip to content

Commit

Permalink
Update kotlinpoet to 1.18
Browse files Browse the repository at this point in the history
  • Loading branch information
F43nd1r committed Aug 23, 2024
1 parent a87cf80 commit 4a02f8b
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 111 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ dokka = "1.9.20"
jgitver = "0.10.0-rc03"
nexusPublish = "2.0.0"
ksp = "2.0.10-1.0.24"
kotlinPoet = "1.16.0"
kotlinPoet = "1.18.1"
kotlinBard = "0.6.0"
compileTesting = "0.5.1"
junit = "5.11.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
@file:OptIn(KotlinPoetMetadataPreview::class)

package com.faendir.kotlin.autodsl.kapt

import com.faendir.kotlin.autodsl.SourceInfoResolver
import com.faendir.kotlin.autodsl.nonnull
import com.faendir.kotlin.autodsl.toRawType
import com.faendir.kotlin.autodsl.withoutAnnotations
import com.google.devtools.ksp.symbol.ClassKind
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.LambdaTypeName
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import com.squareup.kotlinpoet.metadata.toKmClass
import kotlinx.metadata.KmClass
import kotlinx.metadata.KmConstructor
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.Modality
import kotlinx.metadata.Visibility
import kotlinx.metadata.declaresDefaultValue
import kotlinx.metadata.isSecondary
import kotlinx.metadata.kind
import kotlinx.metadata.modality
import kotlinx.metadata.visibility
import com.squareup.kotlinpoet.metadata.classinspectors.ElementsClassInspector
import com.squareup.kotlinpoet.metadata.specs.toTypeSpec
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.ElementKind
Expand All @@ -38,19 +31,23 @@ import kotlin.reflect.KProperty1
class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, private val roundEnv: RoundEnvironment) :
SourceInfoResolver<Annotated, Type, Constructor, Parameter> {
override fun getClassesWithAnnotation(annotation: KClass<out Annotation>): List<Type> =
roundEnv.getElementsAnnotatedWith(annotation.java).filterIsInstance<TypeElement>().map { Type(it) }
roundEnv.getElementsAnnotatedWith(annotation.java).filterIsInstance<TypeElement>().map { Type(it, it.toTypeSpec()) }

override fun getClassesWithAnnotation(annotation: Type): List<Type> =
roundEnv.getElementsAnnotatedWith(annotation.element).filterIsInstance<TypeElement>().map { Type(it) }

override fun Type.getClassKind(): ClassKind = when (kmClass.kind) {
kotlinx.metadata.ClassKind.CLASS -> ClassKind.CLASS
kotlinx.metadata.ClassKind.INTERFACE -> ClassKind.INTERFACE
kotlinx.metadata.ClassKind.ENUM_CLASS -> ClassKind.ENUM_CLASS
kotlinx.metadata.ClassKind.ENUM_ENTRY -> ClassKind.ENUM_ENTRY
kotlinx.metadata.ClassKind.ANNOTATION_CLASS -> ClassKind.ANNOTATION_CLASS
kotlinx.metadata.ClassKind.OBJECT -> ClassKind.OBJECT
kotlinx.metadata.ClassKind.COMPANION_OBJECT -> ClassKind.OBJECT
roundEnv.getElementsAnnotatedWith(annotation.element).filterIsInstance<TypeElement>().map { Type(it, it.toTypeSpec()) }

override fun Type.getClassKind(): ClassKind = when (typeSpec.kind) {
TypeSpec.Kind.CLASS -> {
when {
typeSpec.modifiers.contains(KModifier.ENUM) -> ClassKind.ENUM_CLASS
typeSpec.modifiers.contains(KModifier.ANNOTATION) -> ClassKind.ANNOTATION_CLASS
element.kind == ElementKind.ENUM_CONSTANT -> ClassKind.ENUM_ENTRY
else -> ClassKind.CLASS
}
}

TypeSpec.Kind.OBJECT -> ClassKind.OBJECT
TypeSpec.Kind.INTERFACE -> ClassKind.INTERFACE
}

override fun <T : Annotation> Annotated.getAnnotationTypeProperty(annotation: KClass<T>, property: KProperty1<T, KClass<*>>): ClassName? = try {
Expand All @@ -64,82 +61,94 @@ class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, p
override fun <T : Annotation, V> Annotated.getAnnotationProperty(annotation: KClass<T>, property: KProperty1<T, V>): V? =
getAnnotation(annotation)?.let(property)

override fun Type.isAbstract(): Boolean = kmClass.modality == Modality.ABSTRACT
override fun Type.isAbstract(): Boolean = typeSpec.modifiers.contains(KModifier.ABSTRACT)

override fun Type.getConstructors(): List<Constructor> {
val constructorElements = element.enclosedElements.filterIsInstance<ExecutableElement>().filter { it.kind == ElementKind.CONSTRUCTOR }.toMutableList()
return kmClass.constructors.associateWith { kmConstructor ->
constructorElements.first { element ->
element.parameters.size == kmConstructor.valueParameters.size && (element.parameters zip kmConstructor.valueParameters).all { (e, k) ->
return (listOfNotNull(typeSpec.primaryConstructor?.let { it to true }) + typeSpec.funSpecs.filter { it.isConstructor }
.map { it to false }).map { (constructorSpec, isPrimary) ->
val element = constructorElements.first { element ->
element.parameters.size == constructorSpec.parameters.size && (element.parameters zip constructorSpec.parameters).all { (e, k) ->
val eType = e.asType().asTypeName().mapToKotlin()
val kType = k.type.asTypeName().nonnull
val kType = k.type.nonnull
if (eType is ParameterizedTypeName && kType is ParameterizedTypeName) {
//Invariant kotlin parameters are variant in java, just check erased type
eType.rawType == kType.rawType
} else if (eType is ParameterizedTypeName && kType is LambdaTypeName) {
eType.typeArguments.map { it.toRawType() } == listOfNotNull(kType.receiver) + kType.parameters.map { it.type } + kType.returnType
eType.typeArguments.map { it.toRawType() } == listOfNotNull(kType.receiver) + kType.parameters.map { it.type.withoutAnnotations() } + kType.returnType
} else {
eType == kType
}
}
}
}.map { (kmConstructor, element) -> Constructor(element, kmConstructor) }
Constructor(element, constructorSpec, isPrimary)
}
}

override fun Constructor.isAccessible(): Boolean = kmConstructor.visibility in listOf(Visibility.PUBLIC, Visibility.INTERNAL)
override fun Constructor.isAccessible(): Boolean = KModifier.PRIVATE !in constructorSpec.modifiers && KModifier.PROTECTED !in constructorSpec.modifiers

override fun Type.getPrimaryConstructor(): Constructor? = getConstructors().find { !it.kmConstructor.isSecondary }
override fun Type.getPrimaryConstructor(): Constructor? = getConstructors().find { it.isPrimary }

override fun Constructor.isValid(): Boolean = true

override fun Constructor.getParameters(): List<Parameter> =
(element.parameters zip kmConstructor.valueParameters).map { (element, kmValueParameter) -> Parameter(element, kmValueParameter) }
(element.parameters zip constructorSpec.parameters).map { (element, parameterSpec) ->
Parameter(
element,
parameterSpec.toBuilder(type = parameterSpec.type.withoutAnnotations()).build()
)
}

override fun Type.asClassName(): ClassName = kmClass.asClassName()
override fun Type.asClassName(): ClassName = element.asClassName()

@OptIn(KotlinPoetMetadataPreview::class)
override fun Parameter.getTypeDeclaration(): Type? {
val element = processingEnv.typeUtils.asElement(element.asType()) as? TypeElement
val kmType = try {
element?.toKmClass()
val typeSpec = try {
element?.toTypeSpec()
} catch (e: IllegalStateException) {
null
}
return if (element != null && kmType != null) Type(element, kmType) else null
return if (element != null && typeSpec != null) Type(element, typeSpec) else null
}

override fun Parameter.getTypeArguments(): List<Type> =
(element.asType() as DeclaredType).typeArguments.mapNotNull {
try {
Type(processingEnv.typeUtils.asElement(it) as TypeElement)
val typeElement = processingEnv.typeUtils.asElement(it) as TypeElement
Type(typeElement, typeElement.toTypeSpec())
} catch (e: Exception) {
null
}
}

override fun Parameter.getTypeName(): TypeName = kmValueParameter.type.asTypeName()
override fun Parameter.getTypeName(): TypeName = parameterSpec.type

override fun Parameter.getName(): String = kmValueParameter.name
override fun Parameter.getName(): String = parameterSpec.name

override fun Parameter.hasDefault(): Boolean = kmValueParameter.declaresDefaultValue
override fun Parameter.hasDefault(): Boolean = parameterSpec.defaultValue != null

override fun Parameter.getDoc(): String? = processingEnv.elementUtils.getDocComment(element)

private val classInspector = ElementsClassInspector.create(lenient = true, processingEnv.elementUtils, processingEnv.typeUtils)
private fun TypeElement.toTypeSpec() = toTypeSpec(lenient = true, classInspector)
}

interface Annotated {
fun <T : Annotation> getAnnotation(annotation: KClass<T>): T?
}

class Type(internal val element: TypeElement, internal val kmClass: KmClass) : Annotated {
constructor(element: TypeElement) : this(element, element.toKmClass())

class Type(internal val element: TypeElement, internal val typeSpec: TypeSpec) : Annotated {
override fun <T : Annotation> getAnnotation(annotation: KClass<T>): T? = element.getAnnotation(annotation.java)

override fun toString(): String {
return element.toString()
}
}

class Constructor(internal val element: ExecutableElement, internal val kmConstructor: KmConstructor) : Annotated {
class Constructor(internal val element: ExecutableElement, internal val constructorSpec: FunSpec, internal val isPrimary: Boolean) : Annotated {
override fun <T : Annotation> getAnnotation(annotation: KClass<T>): T? = element.getAnnotation(annotation.java)
}

class Parameter(internal val element: VariableElement, internal val kmValueParameter: KmValueParameter) : Annotated {
class Parameter(internal val element: VariableElement, internal val parameterSpec: ParameterSpec) : Annotated {
override fun <T : Annotation> getAnnotation(annotation: KClass<T>): T? = element.getAnnotation(annotation.java)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.faendir.kotlin.autodsl
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.LambdaTypeName
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.WildcardTypeName
import com.squareup.kotlinpoet.asClassName
Expand All @@ -19,6 +20,30 @@ fun TypeName.toRawType(): ClassName = when (this) {
else -> throw IllegalArgumentException("Unsupported conversion to raw type from $this")
}

fun TypeName.withoutAnnotations(): TypeName = when (this) {
is ParameterizedTypeName -> {
val typeArguments = typeArguments.map { it.withoutAnnotations() }
if (typeArguments == this.typeArguments) this else rawType.parameterizedBy(*typeArguments.toTypedArray())
}
is WildcardTypeName -> {
if(inTypes.size == 1) {
val inType = inTypes.first().withoutAnnotations()
if (inType == this.inTypes.first()) this else WildcardTypeName.consumerOf(inType)
} else if(outTypes.size == 1) {
val outType = outTypes.first().withoutAnnotations()
if (outType == this.outTypes.first()) this else WildcardTypeName.producerOf(outType)
} else this
}
is LambdaTypeName -> {
val receiver = receiver?.withoutAnnotations()
val parameters = parameters.map { it.toBuilder(type = it.type.withoutAnnotations()).build() }
val returnType = returnType.withoutAnnotations()
if (receiver == this.receiver && parameters == this.parameters && returnType == this.returnType) this else LambdaTypeName.get(receiver, parameters, returnType)
}
is ClassName -> this.copy(annotations = emptyList())
else -> this
}

fun ClassName.withBuilderSuffix() = ClassName(packageName, "${simpleName}Builder")

fun TypeName.withBuilderSuffix() = toRawType().withBuilderSuffix()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fun compile(

fun compileKsp(
@Language("kotlin") source: String,
@Language("kotlin") eval: String = "fun test() { }",
@Language("kotlin") eval: String,
expect: KotlinCompilation.ExitCode = KotlinCompilation.ExitCode.OK
): List<File> {
val compilation = KotlinCompilation().apply {
Expand All @@ -66,7 +66,7 @@ fun compileKsp(

fun compileKapt(
@Language("kotlin") source: String,
@Language("kotlin") eval: String = "fun test() { }",
@Language("kotlin") eval: String,
expect: KotlinCompilation.ExitCode = KotlinCompilation.ExitCode.OK
): List<File> {
val result = KotlinCompilation().apply {
Expand Down

0 comments on commit 4a02f8b

Please sign in to comment.