private static boolean canBePrivate(
      PsiMethod method,
      Collection<PsiReference> references,
      Collection<? extends PsiElement> deleted,
      final PsiElement[] allElementsToDelete) {
    final PsiClass containingClass = method.getContainingClass();
    if (containingClass == null) {
      return false;
    }

    PsiManager manager = method.getManager();
    final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
    final PsiElementFactory factory = facade.getElementFactory();
    final PsiModifierList privateModifierList;
    try {
      final PsiMethod newMethod = factory.createMethod("x3", PsiType.VOID);
      privateModifierList = newMethod.getModifierList();
      privateModifierList.setModifierProperty(PsiModifier.PRIVATE, true);
    } catch (IncorrectOperationException e) {
      LOG.assertTrue(false);
      return false;
    }
    for (PsiReference reference : references) {
      final PsiElement element = reference.getElement();
      if (!isInside(element, allElementsToDelete)
          && !isInside(element, deleted)
          && !facade
              .getResolveHelper()
              .isAccessible(method, privateModifierList, element, null, null)) {
        return false;
      }
    }
    return true;
  }
  @Nullable
  private static Condition<PsiElement> findMethodUsages(
      final PsiMethod psiMethod, final PsiElement[] allElementsToDelete, List<UsageInfo> usages) {
    final Collection<PsiReference> references = ReferencesSearch.search(psiMethod).findAll();

    if (psiMethod.isConstructor()) {
      return findConstructorUsages(psiMethod, references, usages, allElementsToDelete);
    }
    final PsiMethod[] overridingMethods =
        removeDeletedMethods(
            OverridingMethodsSearch.search(psiMethod).toArray(PsiMethod.EMPTY_ARRAY),
            allElementsToDelete);

    findFunctionalExpressions(usages, ArrayUtil.prepend(psiMethod, overridingMethods));

    final HashMap<PsiMethod, Collection<PsiReference>> methodToReferences = new HashMap<>();
    for (PsiMethod overridingMethod : overridingMethods) {
      final Collection<PsiReference> overridingReferences =
          ReferencesSearch.search(overridingMethod).findAll();
      methodToReferences.put(overridingMethod, overridingReferences);
    }
    final Set<PsiMethod> validOverriding =
        validateOverridingMethods(
            psiMethod,
            references,
            Arrays.asList(overridingMethods),
            methodToReferences,
            usages,
            allElementsToDelete);
    for (PsiReference reference : references) {
      final PsiElement element = reference.getElement();
      if (!isInside(element, allElementsToDelete) && !isInside(element, validOverriding)) {
        usages.add(
            new SafeDeleteReferenceJavaDeleteUsageInfo(
                element,
                psiMethod,
                PsiTreeUtil.getParentOfType(element, PsiImportStaticStatement.class) != null));
      }
    }

    final List<PsiMethod> calleesSafeToDelete =
        SafeDeleteJavaCalleeChooser.computeCalleesSafeToDelete(psiMethod);
    if (calleesSafeToDelete != null) {
      for (PsiMethod method : calleesSafeToDelete) {
        usages.add(new SafeDeleteMethodCalleeUsageInfo(method, psiMethod));
      }
    }

    return usage -> {
      if (usage instanceof PsiFile) return false;
      return isInside(usage, allElementsToDelete) || isInside(usage, validOverriding);
    };
  }
  @Nullable
  private static Condition<PsiElement> findConstructorUsages(
      PsiMethod constructor,
      Collection<PsiReference> originalReferences,
      List<UsageInfo> usages,
      final PsiElement[] allElementsToDelete) {
    HashMap<PsiMethod, Collection<PsiReference>> constructorsToRefs = new HashMap<>();
    HashSet<PsiMethod> newConstructors = new HashSet<>();
    if (isTheOnlyEmptyDefaultConstructor(constructor)) return null;

    newConstructors.add(constructor);
    constructorsToRefs.put(constructor, originalReferences);
    HashSet<PsiMethod> passConstructors = new HashSet<>();
    do {
      passConstructors.clear();
      for (PsiMethod method : newConstructors) {
        final Collection<PsiReference> references = constructorsToRefs.get(method);
        for (PsiReference reference : references) {
          PsiMethod overridingConstructor =
              getOverridingConstructorOfSuperCall(reference.getElement());
          if (overridingConstructor != null
              && !constructorsToRefs.containsKey(overridingConstructor)) {
            Collection<PsiReference> overridingConstructorReferences =
                ReferencesSearch.search(overridingConstructor).findAll();
            constructorsToRefs.put(overridingConstructor, overridingConstructorReferences);
            passConstructors.add(overridingConstructor);
          }
        }
      }
      newConstructors.clear();
      newConstructors.addAll(passConstructors);
    } while (!newConstructors.isEmpty());

    final Set<PsiMethod> validOverriding =
        validateOverridingMethods(
            constructor,
            originalReferences,
            constructorsToRefs.keySet(),
            constructorsToRefs,
            usages,
            allElementsToDelete);

    return usage -> {
      if (usage instanceof PsiFile) return false;
      return isInside(usage, allElementsToDelete) || isInside(usage, validOverriding);
    };
  }
  @Nullable
  public NonCodeUsageSearchInfo findUsages(
      @NotNull final PsiElement element,
      @NotNull final PsiElement[] allElementsToDelete,
      @NotNull final List<UsageInfo> usages) {
    Condition<PsiElement> insideDeletedCondition = getUsageInsideDeletedFilter(allElementsToDelete);
    if (element instanceof PsiClass) {
      findClassUsages((PsiClass) element, allElementsToDelete, usages);
      if (element instanceof PsiTypeParameter) {
        findTypeParameterExternalUsages((PsiTypeParameter) element, usages);
      }
    } else if (element instanceof PsiMethod) {
      insideDeletedCondition = findMethodUsages((PsiMethod) element, allElementsToDelete, usages);
    } else if (element instanceof PsiField) {
      insideDeletedCondition = findFieldUsages((PsiField) element, usages, allElementsToDelete);
    } else if (element instanceof PsiParameter) {
      LOG.assertTrue(((PsiParameter) element).getDeclarationScope() instanceof PsiMethod);
      findParameterUsages((PsiParameter) element, usages);
    } else if (element instanceof PsiLocalVariable) {
      for (PsiReference reference : ReferencesSearch.search(element)) {
        PsiReferenceExpression referencedElement = (PsiReferenceExpression) reference.getElement();
        final PsiStatement statement =
            PsiTreeUtil.getParentOfType(referencedElement, PsiStatement.class);

        boolean isSafeToDelete = PsiUtil.isAccessedForWriting(referencedElement);
        boolean hasSideEffects = false;
        if (PsiUtil.isOnAssignmentLeftHand(referencedElement)) {
          hasSideEffects =
              RemoveUnusedVariableUtil.checkSideEffects(
                  ((PsiAssignmentExpression) referencedElement.getParent()).getRExpression(),
                  ((PsiLocalVariable) element),
                  new ArrayList<>());
        }
        usages.add(
            new SafeDeleteReferenceJavaDeleteUsageInfo(
                statement, element, isSafeToDelete && !hasSideEffects));
      }
    }
    return new NonCodeUsageSearchInfo(insideDeletedCondition, element);
  }
  private static Set<PsiMethod> validateOverridingMethods(
      PsiMethod originalMethod,
      final Collection<PsiReference> originalReferences,
      Collection<PsiMethod> overridingMethods,
      HashMap<PsiMethod, Collection<PsiReference>> methodToReferences,
      List<UsageInfo> usages,
      final PsiElement[] allElementsToDelete) {
    Set<PsiMethod> validOverriding = new LinkedHashSet<>(overridingMethods);
    Set<PsiMethod> multipleInterfaceImplementations = new HashSet<>();
    boolean anyNewBadRefs;
    do {
      anyNewBadRefs = false;
      for (PsiMethod overridingMethod : overridingMethods) {
        if (validOverriding.contains(overridingMethod)) {
          final Collection<PsiReference> overridingReferences =
              methodToReferences.get(overridingMethod);
          boolean anyOverridingRefs = false;
          for (final PsiReference overridingReference : overridingReferences) {
            final PsiElement element = overridingReference.getElement();
            if (!isInside(element, allElementsToDelete) && !isInside(element, validOverriding)) {
              anyOverridingRefs = true;
              break;
            }
          }
          if (!anyOverridingRefs
              && isMultipleInterfacesImplementation(
                  overridingMethod, originalMethod, allElementsToDelete)) {
            anyOverridingRefs = true;
            multipleInterfaceImplementations.add(overridingMethod);
          }

          if (anyOverridingRefs) {
            validOverriding.remove(overridingMethod);
            anyNewBadRefs = true;

            for (PsiReference reference : originalReferences) {
              final PsiElement element = reference.getElement();
              if (!isInside(element, allElementsToDelete)
                  && !isInside(element, overridingMethods)) {
                usages.add(
                    new SafeDeleteReferenceJavaDeleteUsageInfo(element, originalMethod, false));
                validOverriding.clear();
              }
            }
          }
        }
      }
    } while (anyNewBadRefs && !validOverriding.isEmpty());

    for (PsiMethod method : validOverriding) {
      if (method != originalMethod) {

        usages.add(new SafeDeleteOverridingMethodUsageInfo(method, originalMethod));
      }
    }

    for (PsiMethod method : overridingMethods) {
      if (!validOverriding.contains(method)
          && !multipleInterfaceImplementations.contains(method)
          && canBePrivate(
              method, methodToReferences.get(method), validOverriding, allElementsToDelete)) {
        usages.add(new SafeDeletePrivatizeMethod(method, originalMethod));
      } else {
        usages.add(new SafeDeleteOverrideAnnotation(method, originalMethod));
      }
    }
    return validOverriding;
  }