public static @NonNls @NotNull String findOutInvalidationReason(@NotNull PsiElement root) {
    if (root == PsiUtilCore.NULL_PSI_ELEMENT) {
      return "NULL_PSI_ELEMENT";
    }

    PsiElement lastParent = root;
    PsiElement element = root instanceof PsiFile ? root : root.getParent();
    if (element == null) {
      @NonNls String m = "parent is null";
      if (root instanceof StubBasedPsiElement) {
        StubElement<?> stub = ((StubBasedPsiElement<?>)root).getStub();
        while (stub != null) {
          //noinspection StringConcatenationInLoop
          m += "\n  each stub=" + stub;
          if (stub instanceof PsiFileStub) {
            m += "; fileStub.psi=" + stub.getPsi() + "; reason=" + ((PsiFileStub<?>)stub).getInvalidationReason();
          }
          stub = stub.getParentStub();
        }
      }
      return m;
    }

    String hierarchy = "";
    while (element != null && !(element instanceof PsiFile)) {
      //noinspection StringConcatenationInLoop
      hierarchy += (hierarchy.isEmpty() ? "" : ", ") + element.getClass();
      lastParent = element;
      element = element.getParent();
    }
    PsiFile psiFile = (PsiFile)element;
    if (psiFile == null) {
      PsiElement context = lastParent.getContext();
      return "containing file is null; hierarchy=" + hierarchy +
             ", context=" + context +
             ", contextFile=" + JBIterable.generate(context, PsiElement::getParent).find(e -> e instanceof PsiFile);
    }

    FileViewProvider provider = psiFile.getViewProvider();
    VirtualFile vFile = provider.getVirtualFile();
    if (!vFile.isValid()) {
      return vFile + " is invalid";
    }
    if (!provider.isPhysical()) {
      PsiElement context = psiFile.getContext();
      if (context != null && !context.isValid()) {
        return "invalid context: " + findOutInvalidationReason(context);
      }
    }

    PsiFile original = psiFile.getOriginalFile();
    if (original != psiFile && !original.isValid()) {
      return "invalid original: " + findOutInvalidationReason(original);
    }

    PsiManager manager = psiFile.getManager();
    if (manager.getProject().isDisposed()) {
      return "project is disposed: " + manager.getProject();
    }

    Language language = psiFile.getLanguage();
    if (language != provider.getBaseLanguage()) {
      return "File language:" + language + " != Provider base language:" + provider.getBaseLanguage();
    }

    FileViewProvider p = manager.findCachedViewProvider(vFile);
    if (provider != p) {
      return "different providers: " + provider + "(" + id(provider) + "); " + p + "(" + id(p) + ")";
    }

    if (!provider.isPhysical()) {
      return "non-physical provider: " + provider; // "dummy" file?
    }

    return "psi is outdated";
  }
public static @NonNls @NotNull String findOutInvalidationReason(@NotNull PsiElement root)