intellij-13intellij-pluginpsi

How to force IntelliJ plugin to parse a file's PsiReference resolutions?


I have an IntelliJ plugin that is walking PsiReference resolutions to a "distant" class, and having trouble because the resolve() method is returning null if and only if i have not navigated to the distant file in the editor during the current IntelliJ session. I suspect that IntelliJ needs to be prodded to parse the file in some sense (when you navigate to the file, you can see that IntelliJ kicks off some sort of parsing run as a lot of the code goes from being minimally highlighted to fully highlighted) but I don't know how to do that. I looked in PsiManager and PsiFile, where I found some refresh() and reloadFromDisk() methods, but I'm guessing these have a lot of side-effects I don't want and probably won't kick off the parsing run anyways. Details/clarification below.

The code looks something like this:

File 1, in which I invoke a plugin action on foo:

@AnnotationReferringToAnotherType(File2.class)
class File1 {
  String foo;
}

File 2, which the plugin must lookup in order to perform the action on foo:

class File2 {
  @File3
  void mustBeLookedUpToHandleAction() {}
}

File 3 must also be looked up:

@interface File3 {
  public enum SomeEnum {
    DEFAULT,
    SOMETHING_ELSE
  }

  SomeEnum value() default SomeEnum.DEFAULT;
}

I perform the action on foo, and then the logic finds the @AnnotationReferringToAnotherType at the top of the containing file (File 1) and resolve()s to the definition of File2. Then it looks for a method in File2 that is annotated with @File3. Since the @File3 annotation on mustBeLookedUpToHandleAction does not specify its value, the plugin must now look up the definition of @File3 in order to determine what the default value is. All of this works completely smoothly after freshly loading IntelliJ. The part that fails is the resolve() from the PsiReferenceExpression SomeEnum.DEFAULT to the actual definition of DEFAULT in SomeEnum. It consistently returns null, until i navigate, in the editor, to File 3, and after that it works every time for the remainder of the session. It seems clear that the references' resolutions are being parsed lazily, and if i can just find some way to kick off that parsing everything should be OK?

You may be thinking "why the hell is this logic so complicated?". Most of the logic is actually in a library i'm using - it's internal, so i may be able to get some changes made but I doubt i'll be able to make fundamental changes on account of this issue unless this issue turns out to be completely unmanageable.


Solution

  • Turns out this is a bug in IntelliJ: https://youtrack.jetbrains.com/issue/IDEA-133994

    the workaround is to call getText() on one of the annotation definition's PsiElements before calling resolve() - e.g. once you have the PsiAnnotationMethod for File3.value call getText() on it before calling getDefaultValue().resolve()

    deets:

    The definition for File3 is "stubbed", so only the core components of its psi tree are actually available. Whenever information is requested from the stub that the stub doesn't have (e.g. by calling getText), the file containing the stubbed psi tree will be fully parsed and the stub replaced with a complete psi tree (see https://confluence.jetbrains.com/display/IDEADEV/Indexing+and+PSI+Stubs+in+IntelliJ+IDEA). The particular implementation of PsiAnnotationMethod.getDefaultValue will return a dummy PsiAnnotationMemberValue if the annotation is stubbed, and that dummy element is apparently not knowledgeable to honor the resolve().