Fix safe delete tests
Relevant intellij-community commit: b696a371747caa9a2be16c482df37e91936f86c6
This commit is contained in:
committed by
Nikolay Krasko
parent
230877c1a8
commit
a802ab5300
+157
-11
@@ -17,21 +17,19 @@
|
|||||||
package org.jetbrains.kotlin.idea.refactoring.safeDelete
|
package org.jetbrains.kotlin.idea.refactoring.safeDelete
|
||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Condition
|
import com.intellij.openapi.util.Condition
|
||||||
import com.intellij.openapi.util.Conditions
|
import com.intellij.openapi.util.Conditions
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.PsiMethod
|
|
||||||
import com.intellij.psi.PsiParameter
|
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import com.intellij.refactoring.safeDelete.ImportSearcher
|
||||||
import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor
|
import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor
|
||||||
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo
|
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo
|
||||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverrideAnnotation
|
import com.intellij.refactoring.safeDelete.usageInfo.*
|
||||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverridingMethodUsageInfo
|
|
||||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo
|
|
||||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo
|
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import com.intellij.util.Processor
|
||||||
import org.jetbrains.kotlin.asJava.*
|
import org.jetbrains.kotlin.asJava.*
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
@@ -45,8 +43,7 @@ import org.jetbrains.kotlin.lexer.JetTokens
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import java.util.ArrayList
|
import java.util.*
|
||||||
import java.util.Collections
|
|
||||||
|
|
||||||
public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
||||||
override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement()
|
override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement()
|
||||||
@@ -169,11 +166,21 @@ public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun findUsagesByCopiedJavaProcessor(element: PsiElement): NonCodeUsageSearchInfo {
|
||||||
|
if (element is PsiClass) {
|
||||||
|
ClassSearchCopiedFromJava.findClassUsages(element, allElementsToDelete, usages)
|
||||||
|
if (element is PsiTypeParameter) {
|
||||||
|
ClassSearchCopiedFromJava.findTypeParameterExternalUsages(element, usages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NonCodeUsageSearchInfo(JavaSafeDeleteProcessor.getUsageInsideDeletedFilter(allElementsToDelete), element)
|
||||||
|
}
|
||||||
|
|
||||||
return when (element) {
|
return when (element) {
|
||||||
is JetClassOrObject -> {
|
is JetClassOrObject -> {
|
||||||
element.toLightClass()?.let { klass ->
|
element.toLightClass()?.let { klass ->
|
||||||
findDelegationCallUsages(klass)
|
findDelegationCallUsages(klass)
|
||||||
findUsagesByJavaProcessor(klass, false)
|
findUsagesByCopiedJavaProcessor(klass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +214,8 @@ public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
|||||||
|
|
||||||
is JetTypeParameter -> {
|
is JetTypeParameter -> {
|
||||||
findTypeParameterUsages(element)
|
findTypeParameterUsages(element)
|
||||||
findUsagesByJavaProcessor(element)
|
element.toLightElements().forEach { findUsagesByCopiedJavaProcessor(it) }
|
||||||
|
getSearchInfo(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
is JetParameter ->
|
is JetParameter ->
|
||||||
@@ -326,4 +334,142 @@ public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
|||||||
else -> super.getElementsToSearch(element, module, allElementsToDelete)
|
else -> super.getElementsToSearch(element, module, allElementsToDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mostly copied from JavaSafeDeleteProcessor.findClassUsages. Temporary solution
|
||||||
|
private object ClassSearchCopiedFromJava {
|
||||||
|
private val LOG = Logger.getInstance("#" + javaClass<KotlinSafeDeleteProcessor>().canonicalName)
|
||||||
|
|
||||||
|
fun findClassUsages(psiClass: PsiClass, allElementsToDelete: Array<out PsiElement>, usages: MutableList<UsageInfo>) {
|
||||||
|
val justPrivates = containsOnlyPrivates(psiClass)
|
||||||
|
|
||||||
|
ReferencesSearch.search(psiClass).forEach(object : Processor<PsiReference> {
|
||||||
|
override fun process(reference: PsiReference): Boolean {
|
||||||
|
val element = reference.element
|
||||||
|
|
||||||
|
fun isInside(place: PsiElement, ancestors: Array<out PsiElement>): Boolean {
|
||||||
|
for (ancestor in ancestors) {
|
||||||
|
if (JavaSafeDeleteProcessor.isInside(place, ancestor)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isInNonStaticImport(element: PsiElement): Boolean {
|
||||||
|
return ImportSearcher.getImport(element, true) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isInside(element, allElementsToDelete)) {
|
||||||
|
val parent = element.parent
|
||||||
|
if (parent is PsiReferenceList) {
|
||||||
|
val pparent = parent.parent
|
||||||
|
if (pparent is PsiClass) {
|
||||||
|
/* If psiClass contains only private members, then it is safe to remove it
|
||||||
|
and change inheritor's extends/implements accordingly */
|
||||||
|
if (justPrivates && element is PsiJavaCodeReferenceElement) {
|
||||||
|
if (parent == pparent.extendsList || parent == pparent.implementsList) {
|
||||||
|
usages.add(SafeDeleteExtendsClassUsageInfo(element, psiClass, pparent))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.assertTrue(element.textRange != null)
|
||||||
|
|
||||||
|
val importDirective = getImportDirective(element, element)
|
||||||
|
if (importDirective != null) {
|
||||||
|
usages.add(SafeDeleteReferenceJavaDeleteUsageInfo(importDirective, psiClass, true))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val containingFile = psiClass.containingFile
|
||||||
|
val sameFileWithSingleClass = containingFile is PsiClassOwner
|
||||||
|
&& containingFile.classes.size() == 1
|
||||||
|
&& element.containingFile === containingFile
|
||||||
|
|
||||||
|
usages.add(SafeDeleteReferenceJavaDeleteUsageInfo(element, psiClass,
|
||||||
|
sameFileWithSingleClass || isInNonStaticImport(element)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findTypeParameterExternalUsages(typeParameter: PsiTypeParameter, usages: MutableCollection<UsageInfo>) {
|
||||||
|
val owner = typeParameter.owner
|
||||||
|
if (owner != null) {
|
||||||
|
val parameterList = owner.typeParameterList
|
||||||
|
if (parameterList != null) {
|
||||||
|
val paramsCount = parameterList.typeParameters.size()
|
||||||
|
val index = parameterList.getTypeParameterIndex(typeParameter)
|
||||||
|
|
||||||
|
ReferencesSearch.search(owner).forEach(object : Processor<PsiReference> {
|
||||||
|
override fun process(reference: PsiReference): Boolean {
|
||||||
|
if (reference is PsiJavaCodeReferenceElement) {
|
||||||
|
val parameterList = reference.parameterList
|
||||||
|
if (parameterList != null) {
|
||||||
|
val typeArgs = parameterList.typeParameterElements
|
||||||
|
if (typeArgs.size() > index) {
|
||||||
|
if (typeArgs.size() == 1 && paramsCount > 1 && typeArgs[0].type is PsiDiamondType) return true
|
||||||
|
usages.add(SafeDeleteReferenceJavaDeleteUsageInfo(typeArgs[index], typeParameter, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImportDirective(element: PsiElement?, original: PsiElement): JetImportDirective? = when (element) {
|
||||||
|
is JetDotQualifiedExpression -> getImportDirective(element.parent, original)
|
||||||
|
is JetNameReferenceExpression -> getImportDirective(element.parent, original)
|
||||||
|
is JetImportDirective -> {
|
||||||
|
val lastChild = element.importedReference
|
||||||
|
if (lastChild == original || (lastChild is JetDotQualifiedExpression && lastChild.selectorExpression == original)) {
|
||||||
|
element
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun containsOnlyPrivates(aClass: PsiClass): Boolean {
|
||||||
|
val fields = aClass.fields
|
||||||
|
for (field in fields) {
|
||||||
|
if (!field.hasModifierProperty(PsiModifier.PRIVATE)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val methods = aClass.methods
|
||||||
|
for (method in methods) {
|
||||||
|
if (!method.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||||
|
if (method.isConstructor) {
|
||||||
|
//skip non-private constructors with call to super only
|
||||||
|
val body = method.body
|
||||||
|
if (body != null) {
|
||||||
|
val statements = body.statements
|
||||||
|
if (statements.size() == 0) continue
|
||||||
|
if (statements.size() == 1 && statements[0] is PsiExpressionStatement) {
|
||||||
|
val expression = (statements[0] as PsiExpressionStatement).expression
|
||||||
|
if (expression is PsiMethodCallExpression) {
|
||||||
|
val methodExpression = expression.methodExpression
|
||||||
|
if (methodExpression.text == PsiKeyword.SUPER) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val inners = aClass.innerClasses
|
||||||
|
for (inner in inners) {
|
||||||
|
if (!inner.hasModifierProperty(PsiModifier.PRIVATE)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user