Search code examples
kotlinannotation-processingkotlinpoet

How to get proper kotlin types from RoundEnvironment for custom Annotation processor?


Lets say my annotation processor's process function looks like below

override fun process(
        annotations: MutableSet<out TypeElement>,
        roundEnv: RoundEnvironment
    ): Boolean {
        try {
            roundEnv.getElementsAnnotatedWith(CustomAnnotation::class.java)
                .mapNotNull {
                    if (it.kind != ElementKind.INTERFACE) {
                        printError(
                            "Only interfaces can be annotated with " +
                                    MapperConfig::class.java.simpleName
                        )
                        null
                    } else {
                        it as TypeElement
                    }
                }.forEach {
                    processMapperConfigInterface(it, roundEnv)
                }
        } catch (ex: Exception) {
            messager.printError(ex.message!!)
        }
        return true
    }

roundEnv.getElementsAnnotatedWith returns me tthe java Elements without any kotlin type information, how do I use annotation processing to get proper kotlin type information instead?


Solution

  • I had the same problem and the only solution I could come up with, is to parse the element's metadata using the kotlinpoet-metadata API.

    IMPORTANT NOTE: kotlinpoet-metadata is still early in production and is itself based on the experimental kotlinx-metadata library, so things might break in the future. However, it is currently used in the stable version of the Moshi Kotlin code generator.

    First, make sure that you've added the following to your dependencies in your build.gradle:

    dependencies {
        implementation 'com.squareup:kotlinpoet:1.7.1'
        implementation 'com.squareup:kotlinpoet-metadata:1.7.1'`
    }
    

    1.7.1 is the latest KotlinPoet version as of today.

    You can get the Kotlin type information via a KmClass, constructed from your element's metadata. Here's an example on using KmClass and here's what you would need to change in your code:

    override fun process(
        annotations: MutableSet<out TypeElement>,
        roundEnv: RoundEnvironment
    ): Boolean {
        try {
            roundEnv.getElementsAnnotatedWith(CustomAnnotation::class.java)
                .mapNotNull {
                    if (it.kind != ElementKind.INTERFACE) {
                        printError(
                            "Only interfaces can be annotated with " +
                                    MapperConfig::class.java.simpleName
                        )
                        null
                    } else {
                        it as TypeElement
                    }
                }.forEach {
                    val typeMetadata = it.getAnnotation(Metadata::class.java) ?: return@forEach
                    var kmClass = typeMetadata.toImmutableKmClass()
                    // HERE: kmClass should contain all the Kotlin type information.
                }
        } catch (ex: Exception) {
            messager.printError(ex.message!!)
        }
        return true
    }