private static synchronized void findReferencedPsi(@NotNull Object root, @NotNull Key<?> key, @NotNull UserDataHolder toIgnore) {
    Predicate<Object> shouldExamineValue = value -> {
      if (value == toIgnore) return false;
      if (value instanceof ASTNode) {
        value = ((ASTNode)value).getPsi();
        if (value == toIgnore) return false;
      }
      if (value instanceof Project || value instanceof Module || value instanceof Application) return false;
      if (value instanceof PsiElement &&
          toIgnore instanceof PsiElement &&
          ((PsiElement)toIgnore).getContainingFile() != null &&
          isAncestor((PsiElement)value, (PsiElement)toIgnore)) {
        // allow to capture PSI parents, assuming that they stay valid at least as long as the element itself
        return false;
      }
      return true;
    };
    Map<Object, @NonNls String> roots = Collections.singletonMap(root, "CachedValueProvider " + key);
    DebugReflectionUtil.walkObjects(5, roots, PsiElement.class, shouldExamineValue, (__, backLink) -> {
      LOG.error("Provider '" + root + "' is retaining PSI, causing memory leaks and possible invalid element access.\n" + backLink);
      return false;
    });
  }
private static synchronized void findReferencedPsi(@NotNull Object root, @NotNull Key<?> key, @NotNull UserDataHolder toIgnore)