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; }