Skip to content

Commit

Permalink
random code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
F43nd1r committed Aug 23, 2024
1 parent c30ebb5 commit 5b20625
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Annotated, Type, Constructor, Parameter> {
private fun Set<Element>.mapToTypes() = filterIsInstance<TypeElement>().map { Type(it, it.toTypeSpec()) }

override fun getClassesWithAnnotation(annotation: KClass<out Annotation>): List<Type> =
roundEnv.getElementsAnnotatedWith(annotation.java).filterIsInstance<TypeElement>().map { Type(it, it.toTypeSpec()) }
roundEnv.getElementsAnnotatedWith(annotation.java).mapToTypes()

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

override fun Type.getClassKind(): ClassKind = when (typeSpec.kind) {
TypeSpec.Kind.CLASS -> {
Expand All @@ -50,14 +56,14 @@ class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, p
TypeSpec.Kind.INTERFACE -> ClassKind.INTERFACE
}

override fun Annotated.hasAnnotation(annotation: KClass<out Annotation>): Boolean = getAnnotation(annotation) != null

override fun <T : Annotation> Annotated.getAnnotationTypeProperty(annotation: KClass<T>, property: KProperty1<T, KClass<*>>): ClassName? = try {
getAnnotation(annotation)?.let(property)?.asClassName()
} catch (e: MirroredTypeException) {
(e.typeMirror.asTypeName() as? ClassName)
}?.mapToKotlin()

override fun Annotated.hasAnnotation(annotation: KClass<out Annotation>): Boolean = getAnnotation(annotation) != null

override fun <T : Annotation, V> Annotated.getAnnotationProperty(annotation: KClass<T>, property: KProperty1<T, V>): V? =
getAnnotation(annotation)?.let(property)

Expand All @@ -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
Expand All @@ -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<Type> =
(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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
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
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<KSAnnotated, KSClassDeclaration, KSFunctionDeclaration, KSValueParameter> {
private fun getClassesWithAnnotation(annotation: String): List<KSClassDeclaration> =
resolver.getSymbolsWithAnnotation(annotation).filterIsInstance<KSClassDeclaration>().toList()
Expand All @@ -29,13 +34,19 @@ class KspSourceInfoResolver(private val resolver: Resolver) : SourceInfoResolver

override fun KSClassDeclaration.getClassKind(): ClassKind = classKind

override fun KSAnnotated.hasAnnotation(annotation: KClass<out Annotation>): Boolean = findAnnotation(annotation) != null
override fun KSAnnotated.hasAnnotation(annotation: KClass<out Annotation>): Boolean = isAnnotationPresent(annotation)

override fun <T : Annotation> KSAnnotated.getAnnotationTypeProperty(annotation: KClass<T>, property: KProperty1<T, KClass<*>>): 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 <T : Annotation, V> KSAnnotated.getAnnotationProperty(annotation: KClass<T>, property: KProperty1<T, V>): 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)

Expand All @@ -49,14 +60,14 @@ class KspSourceInfoResolver(private val resolver: Resolver) : SourceInfoResolver

override fun KSFunctionDeclaration.getParameters(): List<KSValueParameter> = 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<KSClassDeclaration> =
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()

Expand Down
Original file line number Diff line number Diff line change
@@ -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 <T : Annotation> KSAnnotation.couldBe(annotation: KClass<T>): Boolean = shortName.asString() == annotation.simpleName


/**
* Heavy check with type resolution
*
* @return true if this is equal to annotation
*/
fun <T : Annotation> KSAnnotation.isEqualTo(annotation: KClass<T>) =
annotationType.resolve().declaration.qualifiedName?.asString() == annotation.java.name

fun <T : Annotation> KSAnnotated.findAnnotation(annotation: KClass<T>): 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()

0 comments on commit 5b20625

Please sign in to comment.