diff --git a/processor/src/main/kotlin/com/faendir/kotlin/autodsl/kapt/KaptSourceInfoResolver.kt b/processor/src/main/kotlin/com/faendir/kotlin/autodsl/kapt/KaptSourceInfoResolver.kt index 127d48f..64bc8c6 100644 --- a/processor/src/main/kotlin/com/faendir/kotlin/autodsl/kapt/KaptSourceInfoResolver.kt +++ b/processor/src/main/kotlin/com/faendir/kotlin/autodsl/kapt/KaptSourceInfoResolver.kt @@ -6,6 +6,7 @@ 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.DelicateKotlinPoetApi import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.LambdaTypeName @@ -19,22 +20,27 @@ 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.Element import javax.lang.model.element.ElementKind import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement import javax.lang.model.element.VariableElement import javax.lang.model.type.DeclaredType import javax.lang.model.type.MirroredTypeException +import javax.lang.model.type.TypeMirror import kotlin.reflect.KClass import kotlin.reflect.KProperty1 +@OptIn(DelicateKotlinPoetApi::class) class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, private val roundEnv: RoundEnvironment) : SourceInfoResolver { + private fun Set.mapToTypes() = filterIsInstance().map { Type(it, it.toTypeSpec()) } + override fun getClassesWithAnnotation(annotation: KClass): List = - roundEnv.getElementsAnnotatedWith(annotation.java).filterIsInstance().map { Type(it, it.toTypeSpec()) } + roundEnv.getElementsAnnotatedWith(annotation.java).mapToTypes() override fun getClassesWithAnnotation(annotation: Type): List = - roundEnv.getElementsAnnotatedWith(annotation.element).filterIsInstance().map { Type(it, it.toTypeSpec()) } + roundEnv.getElementsAnnotatedWith(annotation.element).mapToTypes() override fun Type.getClassKind(): ClassKind = when (typeSpec.kind) { TypeSpec.Kind.CLASS -> { @@ -50,14 +56,14 @@ class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, p TypeSpec.Kind.INTERFACE -> ClassKind.INTERFACE } + override fun Annotated.hasAnnotation(annotation: KClass): Boolean = getAnnotation(annotation) != null + override fun Annotated.getAnnotationTypeProperty(annotation: KClass, property: KProperty1>): ClassName? = try { getAnnotation(annotation)?.let(property)?.asClassName() } catch (e: MirroredTypeException) { (e.typeMirror.asTypeName() as? ClassName) }?.mapToKotlin() - override fun Annotated.hasAnnotation(annotation: KClass): Boolean = getAnnotation(annotation) != null - override fun Annotated.getAnnotationProperty(annotation: KClass, property: KProperty1): V? = getAnnotation(annotation)?.let(property) @@ -75,6 +81,7 @@ class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, p //Invariant kotlin parameters are variant in java, just check erased type eType.rawType == kType.rawType } else if (eType is ParameterizedTypeName && kType is LambdaTypeName) { + // Lambdas are kotlin.FunctionX types in java eType.typeArguments.map { it.toRawType() } == listOfNotNull(kType.receiver) + kType.parameters.map { it.type.withoutAnnotations() } + kType.returnType } else { eType == kType @@ -101,25 +108,18 @@ class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, p override fun Type.asClassName(): ClassName = element.asClassName() - override fun Parameter.getTypeDeclaration(): Type? { - val element = processingEnv.typeUtils.asElement(element.asType()) as? TypeElement - val typeSpec = try { - element?.toTypeSpec() - } catch (e: IllegalStateException) { + private fun TypeMirror.toType(): Type? = (processingEnv.typeUtils.asElement(this) as? TypeElement)?.let { element -> + try { + Type(element, element.toTypeSpec()) + } catch (e: Exception) { null } - return if (element != null && typeSpec != null) Type(element, typeSpec) else null } + override fun Parameter.getTypeDeclaration(): Type? = element.asType().toType() + override fun Parameter.getTypeArguments(): List = - (element.asType() as DeclaredType).typeArguments.mapNotNull { - try { - val typeElement = processingEnv.typeUtils.asElement(it) as TypeElement - Type(typeElement, typeElement.toTypeSpec()) - } catch (e: Exception) { - null - } - } + (element.asType() as DeclaredType).typeArguments.mapNotNull { it.toType() } override fun Parameter.getTypeName(): TypeName = parameterSpec.type diff --git a/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/KspSourceInfoResolver.kt b/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/KspSourceInfoResolver.kt index 80f1a83..dc4824a 100644 --- a/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/KspSourceInfoResolver.kt +++ b/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/KspSourceInfoResolver.kt @@ -1,6 +1,10 @@ package com.faendir.kotlin.autodsl.ksp import com.faendir.kotlin.autodsl.SourceInfoResolver +import com.google.devtools.ksp.KSTypeNotPresentException +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getAnnotationsByType +import com.google.devtools.ksp.isAnnotationPresent import com.google.devtools.ksp.isInternal import com.google.devtools.ksp.isPublic import com.google.devtools.ksp.processing.Resolver @@ -8,17 +12,18 @@ import com.google.devtools.ksp.symbol.ClassKind import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.symbol.KSFunctionDeclaration -import com.google.devtools.ksp.symbol.KSType import com.google.devtools.ksp.symbol.KSValueParameter import com.google.devtools.ksp.symbol.Modifier import com.google.devtools.ksp.validate import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.asClassName +import com.squareup.kotlinpoet.ksp.toClassName import kotlin.reflect.KClass import kotlin.reflect.KProperty1 -import com.faendir.kotlin.autodsl.ksp.asClassName as asClassNameUtil import com.google.devtools.ksp.getConstructors as superGetConstructors +@OptIn(KspExperimental::class) class KspSourceInfoResolver(private val resolver: Resolver) : SourceInfoResolver { private fun getClassesWithAnnotation(annotation: String): List = resolver.getSymbolsWithAnnotation(annotation).filterIsInstance().toList() @@ -29,13 +34,19 @@ class KspSourceInfoResolver(private val resolver: Resolver) : SourceInfoResolver override fun KSClassDeclaration.getClassKind(): ClassKind = classKind - override fun KSAnnotated.hasAnnotation(annotation: KClass): Boolean = findAnnotation(annotation) != null + override fun KSAnnotated.hasAnnotation(annotation: KClass): Boolean = isAnnotationPresent(annotation) override fun KSAnnotated.getAnnotationTypeProperty(annotation: KClass, property: KProperty1>): ClassName? = - (findAnnotation(annotation)?.arguments?.firstOrNull { it.name?.asString() == property.name }?.value as? KSType?)?.asClassNameUtil() + getAnnotationsByType(annotation).firstOrNull()?.let { annotationValue -> + try { + property.get(annotationValue).asClassName() + } catch (e: KSTypeNotPresentException) { + e.ksType.toClassName() + } + } override fun KSAnnotated.getAnnotationProperty(annotation: KClass, property: KProperty1): V? = - findAnnotation(annotation)?.arguments?.firstOrNull { it.name?.asString() == property.name }?.value as? V? + getAnnotationsByType(annotation).firstOrNull()?.let { property.get(it) } override fun KSClassDeclaration.isAbstract(): Boolean = modifiers.contains(Modifier.ABSTRACT) @@ -49,14 +60,14 @@ class KspSourceInfoResolver(private val resolver: Resolver) : SourceInfoResolver override fun KSFunctionDeclaration.getParameters(): List = parameters - override fun KSClassDeclaration.asClassName(): ClassName = asClassNameUtil() + override fun KSClassDeclaration.asClassName(): ClassName = toClassName() override fun KSValueParameter.getTypeDeclaration(): KSClassDeclaration? = type.resolve().declaration as? KSClassDeclaration override fun KSValueParameter.getTypeArguments(): List = type.resolve().arguments.mapNotNull { it.type?.resolve()?.declaration as? KSClassDeclaration } - override fun KSValueParameter.getTypeName(): TypeName = type.asTypeName() + override fun KSValueParameter.getTypeName(): TypeName = type.resolve().asTypeName() override fun KSValueParameter.getName(): String = name!!.asString() diff --git a/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/ksputils.kt b/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/ksputils.kt index 09794f7..028bfb7 100644 --- a/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/ksputils.kt +++ b/processor/src/main/kotlin/com/faendir/kotlin/autodsl/ksp/ksputils.kt @@ -1,63 +1,19 @@ package com.faendir.kotlin.autodsl.ksp -import com.google.devtools.ksp.symbol.KSAnnotated -import com.google.devtools.ksp.symbol.KSAnnotation -import com.google.devtools.ksp.symbol.KSDeclaration import com.google.devtools.ksp.symbol.KSType -import com.google.devtools.ksp.symbol.KSTypeReference -import com.google.devtools.ksp.symbol.Variance -import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.LambdaTypeName -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import com.squareup.kotlinpoet.STAR +import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.TypeName -import com.squareup.kotlinpoet.WildcardTypeName -import kotlin.reflect.KClass +import com.squareup.kotlinpoet.ksp.toTypeName -/** - * Light check without type resolution. A positive result does not guarantee equality - * - * @return false if this is not equal to annotation - */ -fun KSAnnotation.couldBe(annotation: KClass): Boolean = shortName.asString() == annotation.simpleName - - -/** - * Heavy check with type resolution - * - * @return true if this is equal to annotation - */ -fun KSAnnotation.isEqualTo(annotation: KClass) = - annotationType.resolve().declaration.qualifiedName?.asString() == annotation.java.name - -fun KSAnnotated.findAnnotation(annotation: KClass): KSAnnotation? { - return annotations.filter { it.couldBe(annotation) }.firstOrNull { it.isEqualTo(annotation) } -} - -fun KSTypeReference.asTypeName() = resolve().asTypeName() fun KSType.asTypeName(): TypeName { - var name: TypeName = asClassName() - if (declaration.qualifiedName?.asString()?.matches(Regex("kotlin\\.Function\\d+")) == true) { - val arguments = arguments.mapNotNullTo(mutableListOf()) { it.type?.asTypeName() } - val receiver = if (annotations.any { it.couldBe(ExtensionFunctionType::class) }) arguments.removeFirst() else null + var name: TypeName = toTypeName() + if (name is ParameterizedTypeName && name.rawType.canonicalName.matches(Regex("kotlin\\.Function\\d+"))) { + val arguments = name.typeArguments.toMutableList() + val receiver = if (annotations.any { it.shortName.asString() == ExtensionFunctionType::class.simpleName }) arguments.removeFirst() else null val returnType = arguments.removeLast() name = LambdaTypeName.get(receiver = receiver, returnType = returnType, parameters = arguments.toTypedArray()) - } else if (arguments.isNotEmpty()) { - name = (name as ClassName).parameterizedBy(arguments.map { - when (it.variance) { - Variance.STAR -> STAR - Variance.INVARIANT -> it.type!!.asTypeName() - Variance.COVARIANT -> WildcardTypeName.producerOf(it.type!!.asTypeName()) - Variance.CONTRAVARIANT -> WildcardTypeName.consumerOf(it.type!!.asTypeName()) - } - }) } - return if (isMarkedNullable) name.copy(nullable = true) else name + return name } - -fun KSDeclaration.asClassName() = - ClassName(packageName.asString(), generateSequence({ this }, { it.parentDeclaration }).map { it.simpleName.asString() }.toList().reversed()) - -fun KSType.asClassName(): ClassName = declaration.asClassName() -