Sasha Shpota
Sasha Shpota

Reputation: 10320

Kotlin Compiler Plugin: How to check if a property has an annotation?

I am creating a Kotlin compiler plugin in which I need to check if a property of a data class has an annotation:

data class User(
     @MyAnnotation
     val name: String
)

I override DelegatingClassBuilder.newField in the following way:

internal class MyClassBuilder(
    delegateBuilder: ClassBuilder
) : DelegatingClassBuilder(delegateBuilder) {
    override fun newField(
        origin: JvmDeclarationOrigin,
        access: Int,
        name: String,
        desc: String,
        signature: String?,
        value: Any?
    ): FieldVisitor {
        val visitor = super.newField(origin, access, name, desc, signature, value)
        val descriptor = origin.descriptor as? PropertyDescriptor ?: return visitor
        if (descriptor.annotations.hasAnnotation(FqName("com.example.MyAnnotation"))) {
            // I never get here
        }
        return visitor
    }
}

Problem: No matter if my property is annotated or not descriptor.annotations would not contain my annotation. If I change the annotations use target to anything else I still don't get the annotation.

At the same time, if I annotate a function and override newMethod, I can get annotations of of this function with pretty similar code.

Question: How to get annotations of a property in a data class?

Upvotes: 3

Views: 686

Answers (2)

Ítalo Oliveira
Ítalo Oliveira

Reputation: 1

Try this:

myObject::class
    .memberProperties
    .forEach {
        if (it.hasAnnotation<FooBar>()) {
             val annotation = it.findAnnotation<FooBar>() 
             // doYourStuff()
        }
            

and in the data class:

@property:FooBar
val myField: Any

The target @property grants the annotation won't end up over a getter but over the property itself, and makes it readable to the 'memberProperties' KClass property. Thereby iterate through the class properties, find the property, and check if it has the annotation by using the 'hasAnnotation<FooBar>()' KProperty1 method. In order to get the annotation object, use 'findAnnotation<FooBar>()' KProperty1 method.

Upvotes: 0

MrGewurz
MrGewurz

Reputation: 21

I guess my answer won't help you, but for anyone with a future problem. For field annotations without a Target the compiler will generate a function that has the annotation. So,

data class User(@MyAnnotation val name: String)

will compile to the following functions:

  • getName
  • getName$annotations
  • ...

As the name suggests, the annotation @MyAnnotation is located in getName$annotations.

You can avoid this by specifying a Target:

data class User(@field:MyAnnotation val name: String)

With the target, you can reference it directly via newField function; otherwise you have to access it via the newMethod function and extract the field. If you develop a plugin for end-user you probably should implement both methods, as the end-user might(not) add a target

Upvotes: 2

Related Questions