private static Condition<PsiElement> findFieldUsages(
      final PsiField psiField,
      final List<UsageInfo> usages,
      final PsiElement[] allElementsToDelete) {
    final Condition<PsiElement> isInsideDeleted = getUsageInsideDeletedFilter(allElementsToDelete);
    ReferencesSearch.search(psiField)
        .forEach(
            reference -> {
              if (!isInsideDeleted.value(reference.getElement())) {
                final PsiElement element = reference.getElement();
                final PsiElement parent = element.getParent();
                if (parent instanceof PsiAssignmentExpression
                    && element == ((PsiAssignmentExpression) parent).getLExpression()) {
                  usages.add(
                      new SafeDeleteFieldWriteReference(
                          (PsiAssignmentExpression) parent, psiField));
                } else {
                  TextRange range = reference.getRangeInElement();
                  usages.add(
                      new SafeDeleteReferenceJavaDeleteUsageInfo(
                          reference.getElement(),
                          psiField,
                          range.getStartOffset(),
                          range.getEndOffset(),
                          false,
                          PsiTreeUtil.getParentOfType(element, PsiImportStaticStatement.class)
                              != null));
                }
              }

              return true;
            });

    return isInsideDeleted;
  }
  private static void findClassUsages(
      final PsiClass psiClass,
      final PsiElement[] allElementsToDelete,
      final List<UsageInfo> usages) {
    final boolean justPrivates = containsOnlyPrivates(psiClass);
    final String qualifiedName = psiClass.getQualifiedName();
    final boolean annotationType = psiClass.isAnnotationType() && qualifiedName != null;

    ReferencesSearch.search(psiClass)
        .forEach(
            reference -> {
              final PsiElement element = reference.getElement();

              if (!isInside(element, allElementsToDelete)) {
                PsiElement parent = element.getParent();
                if (parent instanceof PsiReferenceList) {
                  final PsiElement pparent = parent.getParent();
                  if (pparent instanceof PsiClass
                      && element instanceof PsiJavaCodeReferenceElement) {
                    final PsiClass inheritor = (PsiClass) pparent;
                    // If psiClass contains only private members, then it is safe to remove it and
                    // change inheritor's extends/implements accordingly
                    if (justPrivates) {
                      if (parent.equals(inheritor.getExtendsList())
                          || parent.equals(inheritor.getImplementsList())) {
                        usages.add(
                            new SafeDeleteExtendsClassUsageInfo(
                                (PsiJavaCodeReferenceElement) element, psiClass, inheritor));
                        return true;
                      }
                    }
                  }
                }
                LOG.assertTrue(element.getTextRange() != null);
                final PsiFile containingFile = psiClass.getContainingFile();
                boolean sameFileWithSingleClass = false;
                if (containingFile instanceof PsiClassOwner) {
                  final PsiClass[] classes = ((PsiClassOwner) containingFile).getClasses();
                  sameFileWithSingleClass =
                      classes.length == 1
                          && classes[0] == psiClass
                          && element.getContainingFile() == containingFile;
                }

                final boolean safeDelete = sameFileWithSingleClass || isInNonStaticImport(element);
                if (annotationType && parent instanceof PsiAnnotation) {
                  usages.add(
                      new SafeDeleteAnnotation((PsiAnnotation) parent, psiClass, safeDelete));
                } else {
                  usages.add(
                      new SafeDeleteReferenceJavaDeleteUsageInfo(element, psiClass, safeDelete));
                }
              }
              return true;
            });
  }
  public Collection<PsiElement> getAdditionalElementsToDelete(
      @NotNull final PsiElement element,
      @NotNull final Collection<PsiElement> allElementsToDelete,
      final boolean askUser) {
    if (element instanceof PsiField) {
      PsiField field = (PsiField) element;
      final Project project = element.getProject();
      String propertyName =
          JavaCodeStyleManager.getInstance(project)
              .variableNameToPropertyName(field.getName(), VariableKind.FIELD);

      PsiClass aClass = field.getContainingClass();
      if (aClass != null) {
        boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
        PsiMethod[] getters =
            GetterSetterPrototypeProvider.findGetters(aClass, propertyName, isStatic);
        if (getters != null) {
          final List<PsiMethod> validGetters = new ArrayList<>(1);
          for (PsiMethod getter : getters) {
            if (!allElementsToDelete.contains(getter) && (getter != null && getter.isPhysical())) {
              validGetters.add(getter);
            }
          }
          getters =
              validGetters.isEmpty()
                  ? null
                  : validGetters.toArray(new PsiMethod[validGetters.size()]);
        }

        PsiMethod setter = PropertyUtil.findPropertySetter(aClass, propertyName, isStatic, false);
        if (allElementsToDelete.contains(setter) || setter != null && !setter.isPhysical())
          setter = null;
        if (askUser && (getters != null || setter != null)) {
          final String message =
              RefactoringMessageUtil.getGetterSetterMessage(
                  field.getName(),
                  RefactoringBundle.message("delete.title"),
                  getters != null ? getters[0] : null,
                  setter);
          if (!ApplicationManager.getApplication().isUnitTestMode()
              && Messages.showYesNoDialog(
                      project,
                      message,
                      RefactoringBundle.message("safe.delete.title"),
                      Messages.getQuestionIcon())
                  != Messages.YES) {
            getters = null;
            setter = null;
          }
        }
        List<PsiElement> elements = new ArrayList<>();
        if (setter != null) elements.add(setter);
        if (getters != null) Collections.addAll(elements, getters);
        return elements;
      }
    }
    return null;
  }
 @Nullable
 private static PsiMethod getOverridingConstructorOfSuperCall(final PsiElement element) {
   if (element instanceof PsiReferenceExpression && "super".equals(element.getText())) {
     PsiElement parent = element.getParent();
     if (parent instanceof PsiMethodCallExpression) {
       parent = parent.getParent();
       if (parent instanceof PsiExpressionStatement) {
         parent = parent.getParent();
         if (parent instanceof PsiCodeBlock) {
           parent = parent.getParent();
           if (parent instanceof PsiMethod && ((PsiMethod) parent).isConstructor()) {
             return (PsiMethod) parent;
           }
         }
       }
     }
   }
   return null;
 }
  @Nullable
  @Override
  public Collection<? extends PsiElement> getElementsToSearch(
      @NotNull PsiElement element,
      @Nullable Module module,
      @NotNull Collection<PsiElement> allElementsToDelete) {
    Project project = element.getProject();
    if (element instanceof PsiPackage && module != null) {
      final PsiDirectory[] directories =
          ((PsiPackage) element).getDirectories(module.getModuleScope());
      if (directories.length == 0) return null;
      return Arrays.asList(directories);
    } else if (element instanceof PsiMethod) {
      final PsiMethod[] methods =
          SuperMethodWarningUtil.checkSuperMethods(
              (PsiMethod) element,
              RefactoringBundle.message("to.delete.with.usage.search"),
              allElementsToDelete);
      if (methods.length == 0) return null;
      final ArrayList<PsiMethod> psiMethods = new ArrayList<>(Arrays.asList(methods));
      psiMethods.add((PsiMethod) element);
      return psiMethods;
    } else if (element instanceof PsiParameter
        && ((PsiParameter) element).getDeclarationScope() instanceof PsiMethod) {
      PsiMethod method = (PsiMethod) ((PsiParameter) element).getDeclarationScope();
      final Set<PsiParameter> parametersToDelete = new HashSet<>();
      parametersToDelete.add((PsiParameter) element);
      final int parameterIndex =
          method.getParameterList().getParameterIndex((PsiParameter) element);
      final List<PsiMethod> superMethods =
          new ArrayList<>(Arrays.asList(method.findDeepestSuperMethods()));
      if (superMethods.isEmpty()) {
        superMethods.add(method);
      }
      for (PsiMethod superMethod : superMethods) {
        parametersToDelete.add(superMethod.getParameterList().getParameters()[parameterIndex]);
        OverridingMethodsSearch.search(superMethod)
            .forEach(
                overrider -> {
                  parametersToDelete.add(
                      overrider.getParameterList().getParameters()[parameterIndex]);
                  return true;
                });
      }

      if (parametersToDelete.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode()) {
        String message =
            RefactoringBundle.message(
                "0.is.a.part.of.method.hierarchy.do.you.want.to.delete.multiple.parameters",
                UsageViewUtil.getLongName(method));
        int result =
            Messages.showYesNoCancelDialog(
                project, message, SafeDeleteHandler.REFACTORING_NAME, Messages.getQuestionIcon());
        if (result == Messages.CANCEL) return null;
        if (result == Messages.NO) return Collections.singletonList(element);
      }
      return parametersToDelete;
    } else if (element instanceof PsiTypeParameter) {
      final PsiTypeParameterListOwner owner = ((PsiTypeParameter) element).getOwner();
      if (owner instanceof PsiMethod && !owner.hasModifierProperty(PsiModifier.STATIC)) {
        final PsiTypeParameterList typeParameterList = owner.getTypeParameterList();
        if (typeParameterList != null) {
          final int index = typeParameterList.getTypeParameterIndex((PsiTypeParameter) element);
          if (index >= 0) {
            final ArrayList<PsiTypeParameter> overriders = new ArrayList<>();
            overriders.add((PsiTypeParameter) element);
            OverridingMethodsSearch.search((PsiMethod) owner)
                .forEach(
                    overrider -> {
                      final PsiTypeParameter[] typeParameters = overrider.getTypeParameters();
                      if (index < typeParameters.length) {
                        overriders.add(typeParameters[index]);
                      }
                      return true;
                    });
            if (overriders.size() > 1) {
              String message =
                  RefactoringBundle.message(
                      "0.is.a.part.of.method.hierarchy.do.you.want.to.delete.multiple.type.parameters",
                      UsageViewUtil.getLongName(owner));
              int result =
                  ApplicationManager.getApplication().isUnitTestMode()
                      ? Messages.YES
                      : Messages.showYesNoCancelDialog(
                          project,
                          message,
                          SafeDeleteHandler.REFACTORING_NAME,
                          Messages.getQuestionIcon());
              if (result == Messages.CANCEL) return null;
              if (result == Messages.YES) return overriders;
            }
          }
        }
      }
    }

    return Collections.singletonList(element);
  }
  private static void findParameterUsages(
      final PsiParameter parameter, final List<UsageInfo> usages) {
    final PsiMethod method = (PsiMethod) parameter.getDeclarationScope();
    final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
    // search for refs to current method only, do not search for refs to overriding methods, they'll
    // be searched separately
    ReferencesSearch.search(method)
        .forEach(
            reference -> {
              PsiElement element = reference.getElement();
              if (element != null) {
                final JavaSafeDeleteDelegate safeDeleteDelegate =
                    JavaSafeDeleteDelegate.EP.forLanguage(element.getLanguage());
                if (safeDeleteDelegate != null) {
                  safeDeleteDelegate.createUsageInfoForParameter(
                      reference, usages, parameter, method);
                }
                if (!parameter.isVarArgs()
                    && !RefactoringChangeUtil.isSuperMethodCall(element.getParent())) {
                  final PsiParameter paramInCaller =
                      SafeDeleteJavaCallerChooser.isTheOnlyOneParameterUsage(
                          element.getParent(), parameterIndex, method);
                  if (paramInCaller != null) {
                    final PsiMethod callerMethod = (PsiMethod) paramInCaller.getDeclarationScope();
                    if (ApplicationManager.getApplication().isUnitTestMode()) {
                      usages.add(
                          new SafeDeleteParameterCallHierarchyUsageInfo(
                              callerMethod, paramInCaller, callerMethod));
                    } else {
                      usages.add(
                          new SafeDeleteParameterCallHierarchyUsageInfo(
                              method, parameter, callerMethod));
                    }
                  }
                }
              }
              return true;
            });

    ReferencesSearch.search(parameter)
        .forEach(
            reference -> {
              PsiElement element = reference.getElement();
              final PsiDocTag docTag = PsiTreeUtil.getParentOfType(element, PsiDocTag.class);
              if (docTag != null) {
                usages.add(new SafeDeleteReferenceJavaDeleteUsageInfo(docTag, parameter, true));
                return true;
              }

              boolean isSafeDelete = false;
              if (element.getParent().getParent() instanceof PsiMethodCallExpression) {
                PsiMethodCallExpression call =
                    (PsiMethodCallExpression) element.getParent().getParent();
                PsiReferenceExpression methodExpression = call.getMethodExpression();
                if (methodExpression.getText().equals(PsiKeyword.SUPER)) {
                  isSafeDelete = true;
                } else if (methodExpression.getQualifierExpression()
                    instanceof PsiSuperExpression) {
                  final PsiMethod superMethod = call.resolveMethod();
                  if (superMethod != null
                      && MethodSignatureUtil.isSuperMethod(superMethod, method)) {
                    isSafeDelete = true;
                  }
                }
              }

              usages.add(
                  new SafeDeleteReferenceJavaDeleteUsageInfo(element, parameter, isSafeDelete));
              return true;
            });

    findFunctionalExpressions(usages, method);
  }