Refactoring - converting extensions to normal functions

This commit is contained in:
Valentin Kipyatkov
2020-02-10 10:55:53 +02:00
parent b1cd56ba91
commit ff39880677
3 changed files with 87 additions and 83 deletions
@@ -7,6 +7,7 @@ import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.slicer.SliceUsage import com.intellij.slicer.SliceUsage
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
@@ -22,6 +23,8 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
import org.jetbrains.kotlin.idea.references.ReferenceAccess import org.jetbrains.kotlin.idea.references.ReferenceAccess
import org.jetbrains.kotlin.idea.references.readWriteAccessWithFullExpression import org.jetbrains.kotlin.idea.references.readWriteAccessWithFullExpression
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
@@ -45,10 +48,17 @@ class InflowSlicer(
processHierarchyDownward(parentUsage.scope.toSearchScope()) { passToProcessor() } processHierarchyDownward(parentUsage.scope.toSearchScope()) { passToProcessor() }
} }
private fun PsiElement.processHierarchyDownward(scope: SearchScope, processor: PsiElement.() -> Unit) {
processor()
HierarchySearchRequest(this, scope).searchOverriders().forEach {
it.namedUnwrappedElement?.processor()
}
}
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true) private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) { private fun processAssignments(variableDeclaration: KtCallableDeclaration, accessSearchScope: SearchScope) {
processVariableAccesses(accessSearchScope, AccessKind.WRITE_WITH_OPTIONAL_READ) body@{ processVariableAccesses(variableDeclaration, accessSearchScope, AccessKind.WRITE_WITH_OPTIONAL_READ) body@{
val refElement = it.element ?: return@body val refElement = it.element ?: return@body
val refParent = refElement.parent val refParent = refElement.parent
@@ -82,47 +92,44 @@ class InflowSlicer(
private fun KtProperty.processPropertyAssignments() { private fun KtProperty.processPropertyAssignments() {
val analysisScope = parentUsage.scope.toSearchScope() val analysisScope = parentUsage.scope.toSearchScope()
val accessSearchScope = if (isVar) analysisScope val accessSearchScope = if (isVar) {
else { analysisScope
val containerScope = getStrictParentOfType<KtDeclaration>()?.let { } else {
LocalSearchScope( val containerScope = getStrictParentOfType<KtDeclaration>()?.let { LocalSearchScope(it) } ?: return
it
)
} ?: return
analysisScope.intersectWith(containerScope) analysisScope.intersectWith(containerScope)
} }
processAssignments(accessSearchScope) processAssignments(this, accessSearchScope)
} }
private fun KtProperty.processProperty() { private fun processProperty(property: KtProperty) {
val bindingContext by lazy { analyzeWithContent() } val bindingContext by lazy { property.analyzeWithContent() }
if (hasDelegateExpression()) { if (property.hasDelegateExpression()) {
val getter = (unsafeResolveToDescriptor() as VariableDescriptorWithAccessors).getter val getter = (property.unsafeResolveToDescriptor() as VariableDescriptorWithAccessors).getter
val delegateGetterResolvedCall = getter?.let { bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it] } val delegateGetterResolvedCall = getter?.let { bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it] }
delegateGetterResolvedCall?.resultingDescriptor?.originalSource?.getPsi()?.passToProcessor() delegateGetterResolvedCall?.resultingDescriptor?.originalSource?.getPsi()?.passToProcessor()
return return
} }
initializer?.passToProcessor() property.initializer?.passToProcessor()
getter?.processFunction() property.getter?.processBody()
val isDefaultGetter = getter?.bodyExpression == null val isDefaultGetter = property.getter?.bodyExpression == null
val isDefaultSetter = setter?.bodyExpression == null val isDefaultSetter = property.setter?.bodyExpression == null
if (isDefaultGetter) { if (isDefaultGetter) {
if (isDefaultSetter) { if (isDefaultSetter) {
processPropertyAssignments() property.processPropertyAssignments()
} else { } else {
setter!!.processBackingFieldAssignments() property.setter!!.processBackingFieldAssignments()
} }
} }
} }
private fun KtParameter.processParameter(includeOverriders: Boolean) { private fun processParameter(parameter: KtParameter, includeOverriders: Boolean) {
if (!canProcess()) return if (!parameter.canProcess()) return
val function = ownerFunction ?: return val function = parameter.ownerFunction ?: return
if (function is KtPropertyAccessor && function.isSetter) { if (function is KtPropertyAccessor && function.isSetter) {
function.property.processPropertyAssignments() function.property.processPropertyAssignments()
@@ -139,7 +146,7 @@ class InflowSlicer(
.forEach { (it.element.parent as? KtProperty)?.processPropertyAssignments() } .forEach { (it.element.parent as? KtProperty)?.processPropertyAssignments() }
} }
val parameterDescriptor = resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return val parameterDescriptor = parameter.resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return
(function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope(), includeOverriders) body@{ (function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope(), includeOverriders) body@{
val refElement = it.element ?: return@body val refElement = it.element ?: return@body
@@ -152,25 +159,25 @@ class InflowSlicer(
val callParameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[parameterDescriptor.index] val callParameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[parameterDescriptor.index]
val resolvedArgument = resolvedCall.valueArguments[callParameterDescriptor] ?: return@body val resolvedArgument = resolvedCall.valueArguments[callParameterDescriptor] ?: return@body
when (resolvedArgument) { when (resolvedArgument) {
is DefaultValueArgument -> defaultValue is DefaultValueArgument -> parameter.defaultValue
is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression() is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression()
else -> null else -> null
} }
} }
refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(this@processParameter.parameterIndex()) refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(parameter.parameterIndex())
else -> null else -> null
} }
argumentExpression?.passToProcessorAsValue() argumentExpression?.passToProcessorAsValue()
} }
if (valOrVarKeyword.toValVar() == KotlinValVar.Var) { if (parameter.valOrVarKeyword.toValVar() == KotlinValVar.Var) {
processAssignments(parentUsage.scope.toSearchScope()) processAssignments(parameter, parentUsage.scope.toSearchScope())
} }
} }
private fun KtDeclarationWithBody.processFunction() { private fun KtDeclarationWithBody.processBody() {
val bodyExpression = bodyExpression ?: return val bodyExpression = bodyExpression ?: return
val pseudocode = pseudocodeCache[bodyExpression] ?: return val pseudocode = pseudocodeCache[bodyExpression] ?: return
pseudocode.traverse(TraversalOrder.FORWARD) { instr -> pseudocode.traverse(TraversalOrder.FORWARD) { instr ->
@@ -194,10 +201,10 @@ class InflowSlicer(
resolveToCall()?.resultingDescriptor is SyntheticFieldDescriptor resolveToCall()?.resultingDescriptor is SyntheticFieldDescriptor
} }
private fun KtExpression.processExpression() { private fun processExpression(expression: KtExpression) {
val lambda = when (this) { val lambda = when (expression) {
is KtLambdaExpression -> functionLiteral is KtLambdaExpression -> expression.functionLiteral
is KtNamedFunction -> if (name == null) this else null is KtNamedFunction -> if (expression.name == null) expression else null
else -> null else -> null
} }
if (lambda != null) { if (lambda != null) {
@@ -207,14 +214,14 @@ class InflowSlicer(
return return
} }
val pseudocode = pseudocodeCache[this] ?: return val pseudocode = pseudocodeCache[expression] ?: return
val expressionValue = pseudocode.getElementValue(this) ?: return val expressionValue = pseudocode.getElementValue(expression) ?: return
when (val createdAt = expressionValue.createdAt) { when (val createdAt = expressionValue.createdAt) {
is ReadValueInstruction -> { is ReadValueInstruction -> {
if (createdAt.target == AccessTarget.BlackBox) { if (createdAt.target == AccessTarget.BlackBox) {
val originalElement = expressionValue.element as? KtExpression ?: return val originalElement = expressionValue.element as? KtExpression ?: return
if (originalElement != this) { if (originalElement != expression) {
originalElement.processExpression() processExpression(originalElement)
} }
return return
} }
@@ -239,7 +246,7 @@ class InflowSlicer(
MagicKind.BOUND_CALLABLE_REFERENCE, MagicKind.UNBOUND_CALLABLE_REFERENCE -> { MagicKind.BOUND_CALLABLE_REFERENCE, MagicKind.UNBOUND_CALLABLE_REFERENCE -> {
val callableRefExpr = expressionValue.element as? KtCallableReferenceExpression val callableRefExpr = expressionValue.element as? KtCallableReferenceExpression
?: return ?: return
val referencedDescriptor = analyze()[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return val referencedDescriptor = expression.analyze()[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return
val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi() ?: return val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi() ?: return
referencedDeclaration.passToProcessor(parentUsage.lambdaLevel - 1) referencedDeclaration.passToProcessor(parentUsage.lambdaLevel - 1)
} }
@@ -259,14 +266,14 @@ class InflowSlicer(
} }
override fun processChildren() { override fun processChildren() {
if (parentUsage.forcedExpressionMode) return element.processExpression() if (parentUsage.forcedExpressionMode) return processExpression(element)
when (element) { when (element) {
is KtProperty -> element.processProperty() is KtProperty -> processProperty(element)
// for parameter, we include overriders only when the feature is invoked on parameter itself // for parameter, we include overriders only when the feature is invoked on parameter itself
is KtParameter -> element.processParameter(includeOverriders = parentUsage.parent == null) is KtParameter -> processParameter(parameter = element, includeOverriders = parentUsage.parent == null)
is KtDeclarationWithBody -> element.processFunction() is KtDeclarationWithBody -> element.processBody()
else -> element.processExpression() else -> processExpression(element)
} }
} }
} }
@@ -21,12 +21,17 @@ class OutflowSlicer(
processor: Processor<SliceUsage>, processor: Processor<SliceUsage>,
parentUsage: KotlinSliceUsage parentUsage: KotlinSliceUsage
) : Slicer(element, processor, parentUsage) { ) : Slicer(element, processor, parentUsage) {
private fun KtCallableDeclaration.processVariable() {
if (this is KtParameter && !canProcess()) return private fun processVariable(variable: KtCallableDeclaration) {
if (variable is KtParameter && !variable.canProcess()) return
val withDereferences = parentUsage.params.showInstanceDereferences val withDereferences = parentUsage.params.showInstanceDereferences
val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY
processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{ processVariableAccesses(
variable,
parentUsage.scope.toSearchScope(),
accessKind
) body@{
val refElement = it.element val refElement = it.element
if (refElement !is KtExpression) { if (refElement !is KtExpression) {
refElement?.passToProcessor() refElement?.passToProcessor()
@@ -62,14 +67,14 @@ class OutflowSlicer(
return if (callee == this) callableRef else null return if (callee == this) callableRef else null
} }
private fun KtFunction.processFunction() { private fun processFunction(function: KtFunction) {
if (this is KtConstructor<*> || this is KtNamedFunction && name != null) { if (function is KtConstructor<*> || function is KtNamedFunction && function.name != null) {
processCalls(parentUsage.scope.toSearchScope(), includeOverriders = false) { function.processCalls(this.parentUsage.scope.toSearchScope(), includeOverriders = false) {
when (val refElement = it.element) { when (val refElement = it.element) {
null -> (it.reference as? LightMemberReference)?.element?.passToProcessor() null -> (it.reference as? LightMemberReference)?.element?.passToProcessor()
is KtExpression -> { is KtExpression -> {
refElement.getCallElementForExactCallee()?.passToProcessor() refElement.getCallElementForExactCallee()?.passToProcessor()
refElement.getCallableReferenceForExactCallee()?.passToProcessor(parentUsage.lambdaLevel + 1) refElement.getCallableReferenceForExactCallee()?.passToProcessor(this.parentUsage.lambdaLevel + 1)
} }
else -> refElement.passToProcessor() else -> refElement.passToProcessor()
} }
@@ -77,9 +82,9 @@ class OutflowSlicer(
return return
} }
val funExpression = when (this) { val funExpression = when (function) {
is KtFunctionLiteral -> parent as? KtLambdaExpression is KtFunctionLiteral -> function.parent as? KtLambdaExpression
is KtNamedFunction -> this is KtNamedFunction -> function
else -> null else -> null
} ?: return } ?: return
(funExpression as PsiElement).passToProcessor(parentUsage.lambdaLevel + 1, true) (funExpression as PsiElement).passToProcessor(parentUsage.lambdaLevel + 1, true)
@@ -125,13 +130,13 @@ class OutflowSlicer(
} }
} }
private fun KtExpression.processExpression() { private fun processExpression(expression: KtExpression) {
processPseudocodeUsages { pseudoValue, instr -> expression.processPseudocodeUsages { pseudoValue, instr ->
when (instr) { when (instr) {
is WriteValueInstruction -> instr.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor() is WriteValueInstruction -> instr.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor()
is CallInstruction -> { is CallInstruction -> {
if (parentUsage.lambdaLevel > 0 && instr.receiverValues[pseudoValue] != null) { if (this.parentUsage.lambdaLevel > 0 && instr.receiverValues[pseudoValue] != null) {
instr.element.passToProcessor(parentUsage.lambdaLevel - 1) instr.element.passToProcessor(this.parentUsage.lambdaLevel - 1)
} else { } else {
instr.arguments[pseudoValue]?.originalSource?.getPsi()?.passToProcessor() instr.arguments[pseudoValue]?.originalSource?.getPsi()?.passToProcessor()
} }
@@ -147,16 +152,16 @@ class OutflowSlicer(
} }
override fun processChildren() { override fun processChildren() {
if (parentUsage.forcedExpressionMode) return element.processExpression() if (parentUsage.forcedExpressionMode) return processExpression(element)
when (element) { when (element) {
is KtProperty -> element.processVariable() is KtProperty -> processVariable(element)
is KtParameter -> element.processVariable() is KtParameter -> processVariable(element)
is KtFunction -> element.processFunction() is KtFunction -> processFunction(element)
is KtPropertyAccessor -> if (element.isGetter) { is KtPropertyAccessor -> if (element.isGetter) {
element.property.processVariable() processVariable(element.property)
} }
else -> element.processExpression() else -> processExpression(element)
} }
} }
} }
@@ -9,8 +9,9 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.slicer.JavaSliceUsage import com.intellij.slicer.JavaSliceUsage
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.namedUnwrappedElement import com.intellij.util.Processor
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
@@ -22,11 +23,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
import org.jetbrains.kotlin.idea.findUsages.processAllUsages import org.jetbrains.kotlin.idea.findUsages.processAllUsages
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.contains import org.jetbrains.kotlin.psi.psiUtil.contains
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -35,7 +33,7 @@ import java.util.*
abstract class Slicer( abstract class Slicer(
protected val element: KtExpression, protected val element: KtExpression,
protected val processor: SliceUsageProcessor, protected val processor: Processor<SliceUsage>,
protected val parentUsage: KotlinSliceUsage protected val parentUsage: KotlinSliceUsage
) { ) {
protected class PseudocodeCache { protected class PseudocodeCache {
@@ -99,39 +97,33 @@ abstract class Slicer(
} }
} }
protected fun PsiElement.processHierarchyDownward(scope: SearchScope, processor: PsiElement.() -> Unit) {
processor()
HierarchySearchRequest(this, scope).searchOverriders().forEach {
it.namedUnwrappedElement?.processor()
}
}
protected enum class AccessKind { protected enum class AccessKind {
READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE
} }
protected fun KtDeclaration.processVariableAccesses( protected fun processVariableAccesses(
declaration: KtCallableDeclaration,
scope: SearchScope, scope: SearchScope,
kind: AccessKind, kind: AccessKind,
usageProcessor: (UsageInfo) -> Unit usageProcessor: (UsageInfo) -> Unit
) { ) {
val allDeclarations = mutableListOf(this) val allDeclarations = mutableListOf(declaration)
val descriptor = unsafeResolveToDescriptor() val descriptor = declaration.unsafeResolveToDescriptor()
if (descriptor is CallableMemberDescriptor) { if (descriptor is CallableMemberDescriptor) {
DescriptorUtils.getAllOverriddenDeclarations(descriptor).mapNotNullTo(allDeclarations) { DescriptorUtils.getAllOverriddenDeclarations(descriptor).mapNotNullTo(allDeclarations) {
it.originalSource.getPsi() as? KtDeclaration it.originalSource.getPsi() as? KtCallableDeclaration
} }
} }
for (declaration in allDeclarations) { for (aDeclaration in allDeclarations) {
declaration.processAllExactUsages( aDeclaration.processAllExactUsages(
KotlinPropertyFindUsagesOptions(project).apply { KotlinPropertyFindUsagesOptions(aDeclaration.project).apply {
isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE
isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
isSearchForTextOccurrences = false isSearchForTextOccurrences = false
isSkipImportStatements = true isSkipImportStatements = true
searchScope = scope.intersectWith(declaration.useScope) searchScope = scope.intersectWith(aDeclaration.useScope)
}, },
usageProcessor usageProcessor
) )