(Kotlin PSI) Check if class is a subclass of a particular class, that works for JVM and Common platforms?
I'm trying to find out if a KtLightClass is a subclass of org.spekframework.spek2.Spek. The code below works if the KtLightClass is a class from a JVM platform module.
fun isSpekSubclass(element: KtLightClass?): Boolean {
if (element != null) {
val superClass = element.superClass
if (superClass != null && superClass is KtLightClass) {
val fqName = superClass.getKotlinFqName()
return if (fqName != null && SPEK_CLASSES.contains(fqName.toString())) {
true
} else {
isSpekSubclass(superClass)
}
}
}
return false
}
It breaks if KtLightClass is a class from a common platform module. For example, say we have a class MySpek: Spek({ ... }). If the class is declared in a JVM platform module then hierarchy seen by the code is MySpek -> org.spekframework.spek2.Spek but when it's declared in a common platform module then the hierarchy is MySpek -> java.lang.Object. Is this expected?
Please sign in to leave a comment.
Sorry I didn't get that we are talking about resolved elements, I thought it is about elements in sources we analyze. If you want to work with resolved element you should work via "descriptors". for your case it could look something like:
You should use `org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall` instead of `resolve` on main reference to get proper descriptor.
Looks like `KtLightClass` will properly work only for JVM-platforms. You could try using `KtClassOrObject` with `getSuperTypeList` instead.
How would I resolve the actual KtClassOrObject from getSuperTypeList? For example given the hierarchy class MySpec: SomeCustSpek({ ...}), then class SomeCustomSpek(root: Root.() -> Unit): Spek(root). How would I know that MySpec is a subclass of org.spekframework.spek2.Spek? I need to compare it to the qualified name, not just Spek.
It could be impossible to get `KtClassOrObject` from `getSuperTypeList` but you could rely on `KtTypeReference` and resolve it to a full name. I am sorry I cant provide an exact code right now, but you could refer KotlinRedundantOverrideInspection
I've actually created an issue in the Kotlin bug tracker for your initial problem. But employing `KtClassOrObject` should work anyway.
Thanks fixed the problem without relying on KtLightClass. Now same problem with KtLightMethod, is there an alternative method that I can use? I need to check for annotations on that method.
You could use KtFunction. But it is not very clear how you actually get Light-elements? If you are writing an inspection you'll get Kotlin source elements (KtClassOrObject, KtFunction etc) and to get Light-elements you need to make additional conversions. What did lead you to Light-elements?
From a KtNamedFunction I use toLightMethods, which works on a JVM module but not a common one.
You should better operate `KtNamedFunction`, and get annotations via `getAnnotationEntries`. LightMethods were designed to provide in-IDE interop with Java, so yes, they could be not fully supported in non-JVM projects, and also despite "Light" in name could bring some performance overheads
If I remember correctly getAnnotationEntries only gives you a list of annotations but the actual properties of the annotation are undefined.@Synonym(type = SynonymType.Group) for example, will only say the method has the @Synonym annotation but not the value of type.
Here's a link to what I currently have: https://github.com/spekframework/spek/blob/2.x/spek-ide-plugin/intellij-common/src/main/kotlin/org/spekframework/intellij/util.kt.
Annotation entry for Kotlin - is a call expression, so `getValueArguments` should work to get passed arguments
Unfortunately getValueArguments doesn't work either.
It is very strange. Could you please provide more details about what do you call and what you expect to get?
Essentially I'm trying to extract annotations on some DSL extensions. If it has those annotations, the plugin will add a gutter icon for it (clicking it will run the test at that specific point).
The plugin traverses the PSI tree of the test class and tries to find usage of DSL methods (methods with @Synonym and optionally @Descriptions). The following code extracts the annotations:
This code works with a jvm module but not a common one. Interestingly when I try the plugin against the spek repo (where the actual source of the DSL methods is) everything magically works.
As we discussed earlier Light-classes will not work in "Common" modules (it is a bug). But through getAnnotationEntries everything should work
I can only get the annotation name, what I need the value arguments as well.
^ it doesn't even work with JVM modules.
Probably it is really better to send me a whole project and point me to the problematic line. because I still didn't get what is wrong with `getValueArguments` on annotationEntry
As I've said this is what I currently have: https://github.com/spekframework/spek/blob/2.x/spek-ide-plugin/intellij-common/src/main/kotlin/org/spekframework/intellij/util.kt#L159. function.annotationEntries will give you the annotations, but whenever I try to query for the annotation parameters it doesn't give me anything (using getValueArguments).
Thanks Nicolay! This works, but for some reason it doesn't resolve annotation parameters that are lists on a jvm module - everything works with a common module.