docs: private void analyzeApplication() throws InterruptedException {
          int passCount = 0;
          Profiler profiler = bugReporter.getProjectStats().getProfiler();
          profiler.start(this.getClass());
          AnalysisContext.currentXFactory().canonicalizeAll();
          try {
              boolean multiplePasses = executionPlan.getNumPasses() > 1;
              if (executionPlan.getNumPasses() == 0) {
                  throw new AssertionError("no analysis passes");
              }
              int[] classesPerPass = new int[executionPlan.getNumPasses()];
              classesPerPass[0] = referencedClassSet.size();
              for (int i = 0; i < classesPerPass.length; i++) {
                  classesPerPass[i] = i == 0 ? referencedClassSet.size() : appClassList.size();
              }
              progressReporter.predictPassCount(classesPerPass);
              XFactory factory = AnalysisContext.currentXFactory();
              Collection<ClassDescriptor> badClasses = new LinkedList<>();
              for (ClassDescriptor desc : referencedClassSet) {
                  try {
                      XClass info = Global.getAnalysisCache().getClassAnalysis(XClass.class, desc);
                      factory.intern(info);
                  } catch (CheckedAnalysisException e) {
                      AnalysisContext.logError("Couldn't get class info for " + desc, e);
                      badClasses.add(desc);
                  } catch (RuntimeException e) {
                      AnalysisContext.logError("Couldn't get class info for " + desc, e);
                      badClasses.add(desc);
                  }
              }
              if (!badClasses.isEmpty()) {
                  referencedClassSet = new LinkedHashSet<>(referencedClassSet);
                  referencedClassSet.removeAll(badClasses);
              }
  
              long startTime = System.currentTimeMillis();
              bugReporter.getProjectStats().setReferencedClasses(referencedClassSet.size());
              for (Iterator<AnalysisPass> passIterator = executionPlan.passIterator(); passIterator.hasNext();) {
                  AnalysisPass pass = passIterator.next();
                  // The first pass is generally a non-reporting pass which
                  // gathers information about referenced classes.
                  boolean isNonReportingFirstPass = multiplePasses && passCount == 0;
  
                  // Instantiate the detectors
                  Detector2[] detectorList = pass.instantiateDetector2sInPass(bugReporter);
  
                  // If there are multiple passes, then on the first pass,
                  // we apply detectors to all classes referenced by the
                  // application classes.
                  // On subsequent passes, we apply detector only to application
                  // classes.
                  Collection<ClassDescriptor> classCollection = (isNonReportingFirstPass) ? referencedClassSet : appClassList;
                  AnalysisContext.currentXFactory().canonicalizeAll();
                  if (PROGRESS || LIST_ORDER) {
                      System.out.printf("%6d : Pass %d: %d classes%n", (System.currentTimeMillis() - startTime)/1000, passCount,  classCollection.size());
                      if (DEBUG) {
                          XFactory.profile();
                      }
                  }
                  if (!isNonReportingFirstPass) {
                      OutEdges<ClassDescriptor> outEdges = e -> {
                          try {
                              XClass classNameAndInfo = Global.getAnalysisCache().getClassAnalysis(XClass.class, e);
                              return classNameAndInfo.getCalledClassDescriptors();
                          } catch (CheckedAnalysisException e2) {
                              AnalysisContext.logError("error while analyzing " + e.getClassName(), e2);
                              return Collections.emptyList();
  
                          }
                      };
  
                      classCollection = sortByCallGraph(classCollection, outEdges);
                  }
                  if (LIST_ORDER) {
                      System.out.println("Analysis order:");
                      for (ClassDescriptor c : classCollection) {
                          System.out.println("  " + c);
                      }
                  }
                  AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
                  currentAnalysisContext.updateDatabases(passCount);
  
                  progressReporter.startAnalysis(classCollection.size());
                  int count = 0;
                  Global.getAnalysisCache().purgeAllMethodAnalysis();
                  Global.getAnalysisCache().purgeClassAnalysis(FBClassReader.class);
                  for (ClassDescriptor classDescriptor : classCollection) {
                      long classStartNanoTime = 0;
                      if (PROGRESS) {
                          classStartNanoTime = System.nanoTime();
                          System.out.printf("%6d %d/%d  %d/%d %s%n", (System.currentTimeMillis() - startTime)/1000,
                                  passCount, executionPlan.getNumPasses(), count,
                                  classCollection.size(), classDescriptor);
                      }
                      count++;
  
                      // Check to see if class is excluded by the class screener.
                      // In general, we do not want to screen classes from the
                      // first pass, even if they would otherwise be excluded.
                      if ((SCREEN_FIRST_PASS_CLASSES || !isNonReportingFirstPass)
                              && !classScreener.matches(classDescriptor.toResourceName())) {
                          if (DEBUG) {
                              System.out.println("*** Excluded by class screener");
                          }
                          continue;
                      }
                      boolean isHuge = currentAnalysisContext.isTooBig(classDescriptor);
                      if (isHuge && currentAnalysisContext.isApplicationClass(classDescriptor)) {
                          bugReporter.reportBug(new BugInstance("SKIPPED_CLASS_TOO_BIG", Priorities.NORMAL_PRIORITY)
                          .addClass(classDescriptor));
                      }
                      currentClassName = ClassName.toDottedClassName(classDescriptor.getClassName());
                      notifyClassObservers(classDescriptor);
                      profiler.startContext(currentClassName);
                      currentAnalysisContext.setClassBeingAnalyzed(classDescriptor);
  
                      try {
                          for (Detector2 detector : detectorList) {
                              if (Thread.interrupted()) {
                                  throw new InterruptedException();
                              }
                              if (isHuge && !FirstPassDetector.class.isAssignableFrom(detector.getClass())) {
                                  continue;
                              }
                              if (DEBUG) {
                                  System.out.println("Applying " + detector.getDetectorClassName() + " to " + classDescriptor);
                                  // System.out.println("foo: " +
                                  // NonReportingDetector.class.isAssignableFrom(detector.getClass())
                                  // + ", bar: " + detector.getClass().getName());
                              }
                              try {
                                  profiler.start(detector.getClass());
                                  detector.visitClass(classDescriptor);
                              } catch (ClassFormatException e) {
                                  logRecoverableException(classDescriptor, detector, e);
                              } catch (MissingClassException e) {
                                  Global.getAnalysisCache().getErrorLogger().reportMissingClass(e.getClassDescriptor());
                              } catch (CheckedAnalysisException e) {
                                  logRecoverableException(classDescriptor, detector, e);
                              } catch (RuntimeException e) {
                                  logRecoverableException(classDescriptor, detector, e);
                              } finally {
                                  profiler.end(detector.getClass());
                              }
                          }
                      } finally {
  
                          progressReporter.finishClass();
                          profiler.endContext(currentClassName);
                          currentAnalysisContext.clearClassBeingAnalyzed();
                          if (PROGRESS) {
                              long usecs = (System.nanoTime() - classStartNanoTime)/1000;
                              if (usecs > 15000) {
                                  int classSize = currentAnalysisContext.getClassSize(classDescriptor);
                                  long speed = usecs /classSize;
                                  if (speed > 15) {
                                      System.out.printf("  %6d usecs/byte  %6d msec  %6d bytes  %d pass %s%n", speed, usecs/1000, classSize, passCount,
                                              classDescriptor);
                                  }
                              }
  
                          }
                      }
                  }
  
                  // Call finishPass on each detector
                  for (Detector2 detector : detectorList) {
                      detector.finishPass();
                  }
  
                  progressReporter.finishPerClassAnalysis();
  
                  passCount++;
              }
  
  
          } finally {
  
              bugReporter.finish();
              bugReporter.reportQueuedErrors();
              profiler.end(this.getClass());
              if (PROGRESS) {
                  System.out.println("Analysis completed");
              }
          }
  
      }