Why do fields not get annotated with @Metadata?

#1

As far as I know, right now only classes get annotated with @Metadata. This is limiting when dealing with annotation processing. For example, I’m writing an annotation processor that parses an annotation on fields and generates methods that need to know, among other things, the type of the field that was annotated. Unfortunately this is impossible (?), because information such as nullability is not available during annotation processing.

Are there any plans to include @Metadata in fields in order to describe their types, or otherwise provide this information (other than reflection since that’s limited to runtime)? Or is this already possible and I’m missing something?

#2

I guess the reason for adding the Metadata annotation to classes only is that also adding this info to each field, function, parameter, etc would just increase the size of the bytecode unnecessarily.
But can’t you just get the class from the field during annotation processing and that way access the Metadata annotation? After that you can simply use the metadata library to parse the annotation data and access the info about the field.
It’s probably not the most efficient way of solving this (and you might need to add some cashing) but it should definitely work. That said the version of this library is still 0.0.5 I believe so it’s in a very early stage of development. You might also want to follow KT-26602.

#3

That’s a very good question actually! I was doing something similar to parse class elements, but I didn’t know that the type of an element would also carry metadata. Thank you!

#4

@Wasabi375 Actually, I don’t think your suggestion is quite possible. I’ve done some more testing and it seems that Element objects that represent fields carry no @Metadata nor do their corresponding TypeMirrors. So for example it is possible to differentiate between

class <T : List<*>> AClass<T>

and

class <T : List<*>?> AClass<T>

by parsing their @Metadata, but not between

class <T : List<*>> AClass<T> {
  val myField: T = null!!
}

and

class <T : List<*>> AClass<T> {
  val myField: T? = null!!
}

because the Element for myField has no annotation and neither does its TypeMirror.

#5

The metadata for AClass should contain the information on whether myField is nullable or not. Sorry if I didn’t explain it correctly. The way to get the info for myField is by getting it’s parent class, in this case AClass and reading the metadata annotation there.

Not sure where exactly the metadata class is in nested classes. I guess it’s either on the nested class itself or on it’s parent. But as long as you don’t deal with nested classes, the metadata annotation should always be on the enclosing class.

fun Element.getMetadata(): KotlinClassMetadata {
    val annotation = element.getAnnotation(Metadata::class.java)
    if(annotation == null){
        return getMetadata(element.enclosingElement!!)
    }

    return annotation.let {
        KotlinClassHeader(it.kind, it.metadataVersion, it.bytecodeVersion,
                          it.data1, it.data2, it.extraString, it.packageName, it.extraInt)
    }.let {
        KotlinClassMetadata.read(it)!!
    }
}

After that you can use the visitor pattern to access the right field/method/parameter/etc. It’s not the easiest API to use, but the issue I linked earlier seems to indicate that it might be replaced with something nicer.

1 Like
#6

My bad, I couldn’t find the visitProperty method before. Thanks again!