google / ksp

Kotlin Symbol Processing API
https://github.com/google/ksp
Apache License 2.0
2.87k stars 272 forks source link

Retrieve annotation enum property leads to java.lang.ClassCastException #429

Open Clement-Jean opened 3 years ago

Clement-Jean commented 3 years ago

Having the following enum:

enum class Priority {
   MIN,
   MEDIUM,
   MAX
}

and the following annotation:

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class FFProvider(val priority: Priority = Priority.MIN)

I'm trying to retrieve the value of that priority property with the following code:

private fun KSClassDeclaration.getPriority() = annotations.first {
   it.shortName.getShortName() == "FFProvider"
}.arguments.first {
   it.name?.getShortName() == "priority"
}.value as Priority

However, I get a java.lang.ClassCastException saying:

java.lang.ClassCastException: class com.google.devtools.ksp.symbol.impl.kotlin.KSTypeImpl cannot be cast to class XXX$Priority (com.google.devtools.ksp.symbol.impl.kotlin.KSTypeImpl is in unnamed module of loader java.net.URLClassLoader @5be45b81; XXX$Priority is in unnamed module of loader java.net.URLClassLoader @50071d6d)

Am I doing something wrong or is it a bug? No idea. If its the former please explain the problem to me (and potentially make the error more explicit).

Ps: works with Int type

Clement-Jean commented 3 years ago

Another question is how would you get the default value of the parameter in the Annotation class ?

mars885 commented 3 years ago

All my default values of the annotation class are set to null for some reason. In the quickstart guide, it says that KSP's analog of Java's Element.getAnnotation is not yet implemented. Has this already been implemented or am I missing something?

mars885 commented 3 years ago

@ting-yuan @neetopia Any thoughts on this?

neetopia commented 3 years ago

If the annotation class is declared in source code, or from a binary library that is compiled from Java source code, then default value should work. The remaining scenario where it is from a kotlin library is not working as a known issue we are tracking.

mars885 commented 3 years ago

If the annotation class is declared in source code, or from a binary library that is compiled from Java source code, then default value should work. The remaining scenario where it is from a kotlin library is not working as a known issue we are tracking.

Does that also apply to fetching arguments from the annotation?

For example:

annotation class BindType(
  val to: KClass<*> = Nothing::class,
  val installIn: Component = Component.NONE
) {

    enum class Component {

      NONE,

      SINGLETON,
      ACTIVITY_RETAINED,
      SERVICE,
      ACTIVITY,
      VIEW_MODEL,
      FRAGMENT,
      VIEW,
      VIEW_WITH_FRAGMENT,

      CUSTOM

    }

}

// then

@BindType(
  to = Testable::class,
  installIn = BindType.Component.VIEW
)
class Test : Testable

// then

fun getComponent(bindAnnotation: KSAnnotation): BindType.Component {
  return bindAnnotation.arguments
      .associate { it.name!!.asString() to it.value }
      .getValue("installIn") // returns KSTypeImpl, instead of BindType.Component
}

In javac, it is as simple as this:

fun getComponent(element: Element): BindType.Component {
  return element.getAnnotation(BindType::class.java).installIn
}

What is the equivalent of the above code in KSP? Thanks in advance.

ting-yuan commented 3 years ago

In Java's analogy, what's provided in KSP is closer to getAnnotationMirrors but not getAnnotation, because the annotation type may not be instantiable at compile time. We plan to implement getAnnotation in utils using reflection (e.g., java.lang.reflect.Proxy) but haven't have time to do so yet.

Ref: https://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html

Alankar0416 commented 2 years ago

Just checking did we manage to fix this. I want to get the enum value passed to one of the annotations. Similar to @mars885 it's returning KSTypeImpl

jithu-10 commented 8 months ago

Is there any solution avail now?. I have same type of problem, @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.SOURCE) annotation class GenerateDI( val moduleType: ModuleType, val superType: KClass<*> = Unit::class, )

@GenerateDI(ModuleType.ACTIVITY, BaseClass::class) class SampleClass : BaseClass

generateDIAnnotation?.arguments?.get(0)?.value.toString()

I used this line of code to get the param value as string. But is there any other way to type cast value?