The same intellij API is not found during compilation, but can be invoked at runtime

Answered

There is the following code:

package com.example

import com.intellij.psi.PsiElement
import com.intellij.psi.util.childrenOfType
import org.jetbrains.kotlin.psi.KtDeclaration

class Foo {

fun bar(element: PsiElement) {
element.childrenOfType<KtDeclaration>()
}
}

It is not about practical usefullness.

The point is, it uses com.intellij.psi.util.childrenOfType.

I have the following build.gradle.kts code:

val pathToAndroidStudioDolpin = ... // we get it somehow
intellij {
localPath.set(pathToAndroidStudioDolpin)
...
}
tasks {
runIde {
ideDir.set(pathToAndroidStudioDolpin)
}
}

By "android studio Dolphin" I mean stable release, namely Intellij Idea 2021.3.3, according to the following table: https://plugins.jetbrains.com/docs/intellij/android-studio-releases-list.html

If I try to compile this code, it fails with the following error:

e: /com/example/Foo.kt: (4, 30): Unresolved reference: childrenOfType

Next, I change configuration so that it is assembled with android studio Electric Eel (= Intellij Idea 2022.1.4), but is still launched on android studio Dolphin:

val pathToAndroidStudioDolpin = ... // we get it somehow
val pathToAndroidStudioElectricEel = ... // we get it somehow
intellij {
localPath.set(pathToAndroidStudioElectricEel)
...
}
tasks {
runIde {
ideDir.set(pathToAndroidStudioDolpin)
}
}

In this case it is compiled successfully and is launched using AS Dolphin as development instance.

Next, I invoke Foo#bar (I`m not listing client code here, since it is not important). And... it runs successfully. Like com.intellij.psi.util.childrenOfType is actually present in AS Dolphin. But why it is not found during compilation?

In my mind, any specific API (for example, com.intellij.psi.util.childrenOfType) is either present in concrete IDE version or not.

If it is present, then both compilation and runtime calls are successful.

If that API is not present in IDE, then both compilation and runtime calls fail.

Why is it impossible to compile the code above with AS Dolphin, but at the same time it is possible to inkove the same API at AS Dolphin`s runtime?

0
3 comments

Where does this method resolve to exactly? com/intellij/psi/util/psiTreeUtil.kt it was added in `053a408acdc91f5a4566839c594756f5c834b044` which seems to be available only since 2022.1 release.

0

This method is com.intellij.psi.util.childrenOfType.

I see it was added in 053a408acdc91f5a4566839c594756f5c834b044. And only 221+ releases contain this commit.

However, ./gradlew runPluginVerifier says, there are no compatibility problems with Android Studio Dolphin (AI-213.7172.25.2113.9014738).

I understand it is very strange.

In code it is as follows:

import com.intellij.psi.util.childrenOfType
...
val klass: KtClassOrObject = ...
val enumConstants = klass.body?.childrenOfType<KtEnumEntry>()
...
0

childrenOfType() is declared as follows:

inline fun <reified T : PsiElement> PsiElement.childrenOfType(): List<T> = 
PsiTreeUtil.getChildrenOfTypeAsList(this, T::class.java)

Taking into account, that it is inline function, its body is inlined into call site. Therefore, bytecode does not contain direct childrenOfType() invocations. Instead, it contains PsiTreeUtil#getChildrenOfTypeAsList(), which certainly exists in Android Studio Dolphin (2021.3.1).

Looking into kotlin bytecode of my class, I see no childrenOfType() invocations, but instead there is a call to PsiTreeUtil#getChildrenOfTypeAsList():

L6
    LINENUMBER 146 L6
    ALOAD 4
    LDC Lorg/jetbrains/kotlin/psi/KtEnumEntry;.class
    INVOKESTATIC com/intellij/psi/util/PsiTreeUtil.getChildrenOfTypeAsList (Lcom/intellij/psi/PsiElement;Ljava/lang/Class;)Ljava/util/List;

It looks like AS Dolphin has no problems with calling childrenOfType because there is no actual invocation of childrenOfType in bytecode - getChildrenOfTypeAsList is called instead.

It looks like inline keyword was the reason why it cannot compile, but can run on the same IDE.

0

Please sign in to leave a comment.