How well does the UAST system support custom language development?

Answered

I'm developing a custom language plugin that needs to support parsing elements in KtClass, PsiClass, and the current processing logic is this:

  1. Get the fully qualified class name
  2. use JavaPsiFacade#findClass to get PsiClass instance
  3. According to the language of the PsiClass instance, decide whether to get the KtClass instance according to the PsiClass instance: PsiTreeUtil#findChildOfType(PsiClass -> VirtualFile -> PsiFile, KtClass::class.java)
  4. Also based on the language of the PsiClass instance, decide to use PsiField or KtProperty to resolve the child elements of the instance, and then implement all the logic supported by the custom language, such as reference resolution, completion hints, syntax highlighting, renaming, structure view, and so on

Then I found something called 'UAST', which is a common abstraction for all JVM languages, and it looks like there are definitions for UClass, UField, and so on, which could theoretically simplify my implementation considerably, or at least eliminate the need to handle the same concepts with different logic depending on the language type.
For now, if I can use UAST to implement the following logic, then I can simplify my logic chain from the second step onwards to 'UClass#getFields'.

  1. does UAST support getting UClass instances as fully qualified names? If not, can we only use JavaPsiFacade#findClass to get PsiClass instances and then convert them to UClass?
  2. what are the current ways to get UAST instances? Is there only one way to get a Psi instance first, and then convert it to a UAST instance?
  3. Can UAST implement all the logic in custom language development and thus use UAST instead of my handling of Psi, Kt elements?
0
3 comments

Hi,

First, did you get familiar with https://plugins.jetbrains.com/docs/intellij/uast.html?

Regarding your questions:

  1. You can find a class with JavaPsiFacade (it should also find Kotlin classes as a light implementation of PsiClass), and then convert it to UAST: https://plugins.jetbrains.com/docs/intellij/uast.html#psi-to-uast-conversion
  2. Usually, you convert PsiElement to UElement. See the link above.
  3. It depends on what you mean by “all the logic in custom language development”.
    In general, I suggest trying it out, and if UAST is insufficient in some cases, you can also fall back to Java/Kotlin-specific code provided via your custom extension point and Java/Kotlin-specific implementations. See https://github.com/JetBrains/intellij-community/blob/243/plugins/devkit/devkit-core/src/inspections/IncorrectCancellationExceptionHandlingInspection.kt#L278-L298 as an example.
    You can also report an issue or request a feature at http://youtrack.jetbrains.com/issues/IJPL with
    - Subsystem: Core. Platform API
    - Tag UAST.
0

I did read that page ahead of time, which is why I had those questions. It's just that I was working on a feature when I asked the question before, and couldn't interrupt to open a new attempt, but now I can try it out!

However, I'll try to refine those questions with my own attempts, because there may be other people who have the same problem, and I've had questions like “Is this the best solution?”

My custom language are designed to be associated with PsiMethod/KtProperty in Java/Kotlin interfaces with specific annotations. To make a custom language look like a language, you need to implement at least the References and Resolve features.

Prior to that, from which version of idea was UAST available?

With this goal in mind, my attempts for UAST are as follows:

  1. dsfGetting the UClass is currently achieved by first getting the PsiClass instance via JavaPsiFacade and then calling com.intellij.psi.PsiElement?.toUElement(). I noticed that there is UastFacade, but it doesn't have a method like findUClass like JavaPsiFacade has findClass?

  2. Get UAnnotation, simply use uAnnotations or findAnnotation(fqName: kotlin.String)

  3. Getting PsiMethod/KtProperty instances, currently it's a matter of getting a PsiClass instance, then determining if the language is kotlin or not, then using specific logic for the specific language. In UClass, my assumption is that you also need to determine the language before deciding whether to call getFields or getMethods, but in reality, for the kotlin interface, getFields returns the empty list and getMethods returns the Java version?

    In Kotlin, the interface is defined as follows:

    Attempted results:

    After trying it out, I think this would be better here for now, what do you think? It's just that I'm considering using UAST with the original intention of no longer needing to judge the language, and this still doesn't satisfy me directly, it's still a bit of a hassle

  4. Getting the UAST version of the PsiPackage, but there doesn't seem to be anything like UPackage? After trying, there is no such thing either. So, UAST doesn't do a language-specific implementation for PsiPackage, just use PsiPackage directly?

PsiReferenceBase needs PsiElement, so after getting the target UElement, you still need to convert it to PsiElement and then build PsiReferenceBase, here you can use sourcePsi directly to get the original language element.

0

Hi,

  1. This is correct approach.
  2. I don't get if this is a question. Using both is OK for UAST (it is not OK to call PSI API on UAST objects).
  3. Unfortunately, UAST has some limitations. I'm not sure what you exactly want to achieve, but UClass.uDeclarations and filtering by type or flags might work for you too. You can also report new features as I mentioned.
  4. The only package-related place I see is UFile.packageName.

Sometimes it is helpful to use Dump UAST Tree action to understand UAST trees better.

0

Please sign in to leave a comment.