docs: private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType, final boolean strictWhitelist,
              final ClassType... classTypes) {
          Set<ClassInfo> directlyRelatedClasses = this.relatedClasses.get(relType);
          if (directlyRelatedClasses == null) {
              return NO_REACHABLE_CLASSES;
          } else {
              // Clone collection to prevent users modifying contents accidentally or intentionally
              directlyRelatedClasses = new LinkedHashSet<>(directlyRelatedClasses);
          }
          final Set<ClassInfo> reachableClasses = new LinkedHashSet<>(directlyRelatedClasses);
          if (relType == RelType.METHOD_ANNOTATIONS || relType == RelType.METHOD_PARAMETER_ANNOTATIONS
                  || relType == RelType.FIELD_ANNOTATIONS) {
              // For method and field annotations, need to change the RelType when finding meta-annotations
              for (final ClassInfo annotation : directlyRelatedClasses) {
                  reachableClasses.addAll(
                          annotation.filterClassInfo(RelType.CLASS_ANNOTATIONS, strictWhitelist).reachableClasses);
              }
          } else if (relType == RelType.CLASSES_WITH_METHOD_ANNOTATION
                  || relType == RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION
                  || relType == RelType.CLASSES_WITH_FIELD_ANNOTATION) {
              // If looking for meta-annotated methods or fields, need to find all meta-annotated annotations, then
              // look for the methods or fields that they annotate
              for (final ClassInfo subAnnotation : this.filterClassInfo(RelType.CLASSES_WITH_ANNOTATION,
                      strictWhitelist, ClassType.ANNOTATION).reachableClasses) {
                  final Set<ClassInfo> annotatedClasses = subAnnotation.relatedClasses.get(relType);
                  if (annotatedClasses != null) {
                      reachableClasses.addAll(annotatedClasses);
                  }
              }
          } else {
              // For other relationship types, the reachable type stays the same over the transitive closure. Find the
              // transitive closure, breaking cycles where necessary.
              final LinkedList<ClassInfo> queue = new LinkedList<>(directlyRelatedClasses);
              while (!queue.isEmpty()) {
                  final ClassInfo head = queue.removeFirst();
                  final Set<ClassInfo> headRelatedClasses = head.relatedClasses.get(relType);
                  if (headRelatedClasses != null) {
                      for (final ClassInfo directlyReachableFromHead : headRelatedClasses) {
                          // Don't get in cycle
                          if (reachableClasses.add(directlyReachableFromHead)) {
                              queue.add(directlyReachableFromHead);
                          }
                      }
                  }
              }
          }
          if (reachableClasses.isEmpty()) {
              return NO_REACHABLE_CLASSES;
          }
  
          if (relType == RelType.CLASS_ANNOTATIONS || relType == RelType.METHOD_ANNOTATIONS
                  || relType == RelType.METHOD_PARAMETER_ANNOTATIONS || relType == RelType.FIELD_ANNOTATIONS) {
              // Special case -- don't inherit java.lang.annotation.* meta-annotations as related meta-annotations
              // (but still return them as direct meta-annotations on annotation classes).
              Set<ClassInfo> reachableClassesToRemove = null;
              for (final ClassInfo reachableClassInfo : reachableClasses) {
                  // Remove all java.lang.annotation annotations that are not directly related to this class
                  if (reachableClassInfo.getName().startsWith("java.lang.annotation.")
                          && !directlyRelatedClasses.contains(reachableClassInfo)) {
                      if (reachableClassesToRemove == null) {
                          reachableClassesToRemove = new LinkedHashSet<>();
                      }
                      reachableClassesToRemove.add(reachableClassInfo);
                  }
              }
              if (reachableClassesToRemove != null) {
                  reachableClasses.removeAll(reachableClassesToRemove);
              }
          }
  
          return new ReachableAndDirectlyRelatedClasses(
                  filterClassInfo(reachableClasses, scanResult.scanSpec, strictWhitelist, classTypes),
                  filterClassInfo(directlyRelatedClasses, scanResult.scanSpec, strictWhitelist, classTypes));
  
      }