@Override
  public <Key, Psi extends PsiElement> boolean processElements(@NotNull StubIndexKey<Key, Psi> indexKey,
                                                               @NotNull Key key,
                                                               @NotNull Project project,
                                                               @Nullable GlobalSearchScope scope,
                                                               @Nullable IdFilter idFilter,
                                                               @NotNull Class<Psi> requiredClass,
                                                               @NotNull Processor<? super Psi> processor) {
    var trace = lookupStubEntriesStarted(indexKey)
      .withProject(project);

    try {
      boolean dumb = DumbService.isDumb(project);
      if (dumb) {
        if (project instanceof LightEditCompatible) return false;
        DumbModeAccessType accessType = FileBasedIndex.getInstance().getCurrentDumbModeAccessType();
        if (accessType == DumbModeAccessType.RAW_INDEX_DATA_ACCEPTABLE) {
          throw new AssertionError("raw index data access is not available for StubIndex");
        }
      }
      Predicate<? super Psi> keyFilter = StubIndexKeyDescriptorCache.INSTANCE.getKeyPsiMatcher(indexKey, key);
      PairProcessor<VirtualFile, StubIdList> stubProcessor = (file, list) -> myStubProcessingHelper.processStubsInFile(
        project, file, list, keyFilter == null ? processor : o -> !keyFilter.test(o) || processor.process(o), scope, requiredClass,
        () -> "Looking for " + key + " in " + indexKey);

      Iterator<VirtualFile> singleFileInScope = FileBasedIndexEx.extractSingleFileOrEmpty(scope);
      Iterator<VirtualFile> fileStream;
      boolean shouldHaveKeys;

      if (singleFileInScope != null) {
        if (!(singleFileInScope.hasNext())) return true;
        FileBasedIndex.getInstance().ensureUpToDate(StubUpdatingIndex.INDEX_ID, project, scope);
        fileStream = singleFileInScope;
        trace.lookupResultSize(1);
        shouldHaveKeys = false;
      }
      else {
        IntSet fileIds = getContainingIds(indexKey, key, project, idFilter, scope);
        if (fileIds == null) {
          trace.lookupResultSize(0);
          return true;
        }
        else {
          trace.lookupResultSize(fileIds.size());
        }
        IntPredicate accessibleFileFilter = ((FileBasedIndexEx)FileBasedIndex.getInstance()).getAccessibleFileIdFilter(project);

        // already ensured up-to-date in getContainingIds() method
        IntIterator idIterator = fileIds.iterator();
        fileStream = StubIndexImplUtil.mapIdIterator(idIterator, accessibleFileFilter);
        shouldHaveKeys = true;
      }

      trace.stubTreesDeserializingStarted();

      try {
        while (fileStream.hasNext()) {
          VirtualFile file = fileStream.next();
          assert file != null;
          if (scope != null && !scope.contains(file)) {
            continue;
          }

          int id = ((VirtualFileWithId)file).getId();
          StubIdList list = myCachedStubIds.get(indexKey).getValue().computeIfAbsent(new KeyAndFileId<>(key, id), __ ->
            myStubProcessingHelper.retrieveStubIdList(indexKey, key, file, project, shouldHaveKeys)
          );
          if (list == null) {
            // stub index inconsistency
            continue;
          }
          if (!stubProcessor.process(file, list)) {
            return false;
          }
        }
      }
      catch (RuntimeException e) {
        trace.lookupFailed();
        final Throwable cause = FileBasedIndexEx.getCauseToRebuildIndex(e);
        if (cause != null) {
          forceRebuild(cause);
        }
        else {
          throw e;
        }
      }
      return true;
    }
    catch (Throwable t) {
      trace.lookupFailed();
      throw t;
    }
    finally {
      tryFixIndexesForProblemFiles(indexKey, key, project);
      //Not using try-with-resources because in case of exceptions are thrown, .close() needs to be called _after_ catch,
      //  so .lookupFailed() is invoked on a not-yet-closed trace -- but TWR does the opposite: first close resources, then
      //  do all catch/finally blocks
      trace.close();
    }
  }
@Override
  public <Key, Psi extends PsiElement> boolean processElements(@NotNull StubIndexKey<Key, Psi> indexKey,
                                                               @NotNull Key key,
                                                               @NotNull Project project,
                                                               @Nullable GlobalSearchScope scope,
                                                               @Nullable IdFilter idFilter,
                                                               @NotNull Class<Psi> requiredClass,
                                                               @NotNull Processor<? super Psi> processor)