diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt new file mode 100644 index 00000000000..fc8d9073c57 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt @@ -0,0 +1,274 @@ +package org.jetbrains.kotlin.idea.slicer + +import com.intellij.psi.PsiCall +import com.intellij.psi.PsiElement +import com.intellij.psi.search.LocalSearchScope +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.slicer.SliceUsage +import com.intellij.util.Processor +import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor +import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* +import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction +import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder +import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.* +import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar +import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar +import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference +import org.jetbrains.kotlin.idea.references.ReferenceAccess +import org.jetbrains.kotlin.idea.references.readWriteAccessWithFullExpression +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.parameterIndex +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.util.OperatorNameConventions + +class InflowSlicer( + element: KtExpression, + processor: Processor, + parentUsage: KotlinSliceUsage +) : Slicer(element, processor, parentUsage) { + private fun PsiElement.processHierarchyDownwardAndPass() { + processHierarchyDownward(parentUsage.scope.toSearchScope()) { passToProcessor() } + } + + private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true) + + private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) { + processVariableAccesses(accessSearchScope, + AccessKind.WRITE_WITH_OPTIONAL_READ + ) body@{ + val refElement = it.element ?: return@body + val refParent = refElement.parent + + val rhsValue = when { + refElement is KtExpression -> { + val (accessKind, accessExpression) = refElement.readWriteAccessWithFullExpression(true) + if (accessKind == ReferenceAccess.WRITE && accessExpression is KtBinaryExpression && accessExpression.operationToken == KtTokens.EQ) { + accessExpression.right + } else { + accessExpression + } + } + + refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(0) + + else -> null + } + rhsValue?.passToProcessorAsValue() + } + } + + private fun KtPropertyAccessor.processBackingFieldAssignments() { + forEachDescendantOfType body@{ + if (it.operationToken != KtTokens.EQ) return@body + val lhs = it.left?.let { expression -> KtPsiUtil.safeDeparenthesize(expression) } ?: return@body + val rhs = it.right ?: return@body + if (!lhs.isBackingFieldReference()) return@body + rhs.passToProcessor() + } + } + + private fun KtProperty.processPropertyAssignments() { + val analysisScope = parentUsage.scope.toSearchScope() + val accessSearchScope = if (isVar) analysisScope + else { + val containerScope = getStrictParentOfType()?.let { + LocalSearchScope( + it + ) + } ?: return + analysisScope.intersectWith(containerScope) + } + processAssignments(accessSearchScope) + } + + private fun KtProperty.processProperty() { + val bindingContext by lazy { analyzeWithContent() } + + if (hasDelegateExpression()) { + val getter = (unsafeResolveToDescriptor() as VariableDescriptorWithAccessors).getter + val delegateGetterResolvedCall = getter?.let { bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it] } + delegateGetterResolvedCall?.resultingDescriptor?.originalSource?.getPsi()?.passToProcessor() + return + } + + initializer?.passToProcessor() + + getter?.processFunction() + + val isDefaultGetter = getter?.bodyExpression == null + val isDefaultSetter = setter?.bodyExpression == null + if (isDefaultGetter) { + if (isDefaultSetter) { + processPropertyAssignments() + } else { + setter!!.processBackingFieldAssignments() + } + } + } + + private fun KtParameter.processParameter(includeOverriders: Boolean) { + if (!canProcess()) return + + val function = ownerFunction ?: return + + if (function is KtPropertyAccessor && function.isSetter) { + function.property.processPropertyAssignments() + return + } + + if (function is KtNamedFunction + && function.name == OperatorNameConventions.SET_VALUE.asString() + && function.hasModifier(KtTokens.OPERATOR_KEYWORD) + ) { + + ReferencesSearch.search(function, parentUsage.scope.toSearchScope()) + .filterIsInstance() + .forEach { (it.element.parent as? KtProperty)?.processPropertyAssignments() } + } + + val parameterDescriptor = resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return + + (function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope(), includeOverriders) body@{ + val refElement = it.element ?: return@body + val refParent = refElement.parent + + val argumentExpression = when { + refElement is KtExpression -> { + val callElement = refElement.getParentOfTypeAndBranch { calleeExpression } ?: return@body + val resolvedCall = callElement.resolveToCall() ?: return@body + val callParameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[parameterDescriptor.index] + val resolvedArgument = resolvedCall.valueArguments[callParameterDescriptor] ?: return@body + when (resolvedArgument) { + is DefaultValueArgument -> defaultValue + is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression() + else -> null + } + } + + refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(this@processParameter.parameterIndex()) + + else -> null + } + argumentExpression?.passToProcessorAsValue() + } + + if (valOrVarKeyword.toValVar() == KotlinValVar.Var) { + processAssignments(parentUsage.scope.toSearchScope()) + } + } + + private fun KtDeclarationWithBody.processFunction() { + val bodyExpression = bodyExpression ?: return + val pseudocode = pseudocodeCache[bodyExpression] ?: return + pseudocode.traverse(TraversalOrder.FORWARD) { instr -> + if (instr is ReturnValueInstruction && instr.subroutine == this) { + (instr.returnExpressionIfAny?.returnedExpression ?: instr.element as? KtExpression)?.passToProcessorAsValue() + } + } + } + + private fun Instruction.passInputsToProcessor() { + inputValues.forEach { + if (it.createdAt != null) { + it.element?.passToProcessorAsValue() + } + } + } + + private fun KtExpression.isBackingFieldReference(): Boolean { + return this is KtSimpleNameExpression && + getReferencedName() == SyntheticFieldDescriptor.NAME.asString() && + resolveToCall()?.resultingDescriptor is SyntheticFieldDescriptor + } + + private fun KtExpression.processExpression() { + val lambda = when (this) { + is KtLambdaExpression -> functionLiteral + is KtNamedFunction -> if (name == null) this else null + else -> null + } + if (lambda != null) { + if (parentUsage.lambdaLevel > 0) { + lambda.passToProcessor(parentUsage.lambdaLevel - 1) + } + return + } + + val pseudocode = pseudocodeCache[this] ?: return + val expressionValue = pseudocode.getElementValue(this) ?: return + when (val createdAt = expressionValue.createdAt) { + is ReadValueInstruction -> { + if (createdAt.target == AccessTarget.BlackBox) { + val originalElement = expressionValue.element as? KtExpression ?: return + if (originalElement != this) { + originalElement.processExpression() + } + return + } + val accessedDescriptor = createdAt.target.accessedDescriptor ?: return + val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return + if (accessedDescriptor is SyntheticFieldDescriptor) { + val property = accessedDeclaration as? KtProperty ?: return + if (accessedDescriptor.propertyDescriptor.setter?.isDefault != false) { + property.processPropertyAssignments() + } else { + property.setter?.processBackingFieldAssignments() + } + return + } + accessedDeclaration.processHierarchyDownwardAndPass() + } + + is MergeInstruction -> createdAt.passInputsToProcessor() + + is MagicInstruction -> when (createdAt.kind) { + MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> createdAt.passInputsToProcessor() + MagicKind.BOUND_CALLABLE_REFERENCE, MagicKind.UNBOUND_CALLABLE_REFERENCE -> { + val callableRefExpr = expressionValue.element as? KtCallableReferenceExpression + ?: return + val referencedDescriptor = analyze()[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return + val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi() ?: return + referencedDeclaration.passToProcessor(parentUsage.lambdaLevel - 1) + } + else -> return + } + + is CallInstruction -> { + val resolvedCall = createdAt.resolvedCall + val resultingDescriptor = resolvedCall.resultingDescriptor + if (resultingDescriptor is FunctionInvokeDescriptor) { + (resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression?.passToProcessorAsValue(parentUsage.lambdaLevel + 1) + } else { + resultingDescriptor.originalSource.getPsi()?.processHierarchyDownwardAndPass() + } + } + } + } + + override fun processChildren() { + if (parentUsage.forcedExpressionMode) return element.processExpression() + + when (element) { + is KtProperty -> element.processProperty() + // for parameter, we include overriders only when the feature is invoked on parameter itself + is KtParameter -> element.processParameter(includeOverriders = parentUsage.parent == null) + is KtDeclarationWithBody -> element.processFunction() + else -> element.processExpression() + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt new file mode 100644 index 00000000000..c4ec4884b1e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt @@ -0,0 +1,163 @@ +package org.jetbrains.kotlin.idea.slicer + +import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.light.LightMemberReference +import com.intellij.slicer.SliceUsage +import com.intellij.util.Processor +import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue +import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* +import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch +import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.resolve.source.getPsi + +class OutflowSlicer( + element: KtExpression, + processor: Processor, + parentUsage: KotlinSliceUsage +) : Slicer(element, processor, parentUsage) { + private fun KtDeclaration.processVariable() { + processHierarchyUpward(parentUsage.scope) { + if (this is KtParameter && !canProcess()) return@processHierarchyUpward + + val withDereferences = parentUsage.params.showInstanceDereferences + val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY + (this as? KtDeclaration)?.processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{ + val refElement = it.element + if (refElement !is KtExpression) { + refElement?.passToProcessor() + return@body + } + + val refExpression = KtPsiUtil.safeDeparenthesize(refElement) + if (withDereferences) { + refExpression.processDereferences() + } + if (!withDereferences || KotlinReadWriteAccessDetector.INSTANCE.getExpressionAccess(refExpression) == ReadWriteAccessDetector.Access.Read) { + refExpression.passToProcessor() + } + } + } + } + + private fun PsiElement.getCallElementForExactCallee(): PsiElement? { + if (this is KtArrayAccessExpression) return this + + val operationRefExpr = getNonStrictParentOfType() + if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression + + val parentCall = getParentOfTypeAndBranch { calleeExpression } ?: return null + val callee = parentCall.calleeExpression?.let { KtPsiUtil.safeDeparenthesize(it) } + if (callee == this || callee is KtConstructorCalleeExpression && callee.isAncestor(this, true)) return parentCall + + return null + } + + private fun PsiElement.getCallableReferenceForExactCallee(): KtCallableReferenceExpression? { + val callableRef = getParentOfTypeAndBranch { callableReference } ?: return null + val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference) + return if (callee == this) callableRef else null + } + + private fun KtFunction.processFunction() { + if (this is KtConstructor<*> || this is KtNamedFunction && name != null) { + processCalls(parentUsage.scope.toSearchScope(), includeOverriders = false) { + when (val refElement = it.element) { + null -> (it.reference as? LightMemberReference)?.element?.passToProcessor() + is KtExpression -> { + refElement.getCallElementForExactCallee()?.passToProcessor() + refElement.getCallableReferenceForExactCallee()?.passToProcessor(parentUsage.lambdaLevel + 1) + } + else -> refElement.passToProcessor() + } + } + return + } + + val funExpression = when (this) { + is KtFunctionLiteral -> parent as? KtLambdaExpression + is KtNamedFunction -> this + else -> null + } ?: return + (funExpression as PsiElement).passToProcessor(parentUsage.lambdaLevel + 1, true) + } + + private fun processDereferenceIsNeeded( + expression: KtExpression, + pseudoValue: PseudoValue, + instr: InstructionWithReceivers + ) { + if (!parentUsage.params.showInstanceDereferences) return + + val receiver = instr.receiverValues[pseudoValue] + val resolvedCall = when (instr) { + is CallInstruction -> instr.resolvedCall + is ReadValueInstruction -> (instr.target as? AccessTarget.Call)?.resolvedCall + else -> null + } ?: return + + if (receiver != null && resolvedCall.dispatchReceiver == receiver) { + processor.process( + KotlinSliceDereferenceUsage( + expression, + parentUsage, + parentUsage.lambdaLevel + ) + ) + } + } + + private fun KtExpression.processPseudocodeUsages(processor: (PseudoValue, Instruction) -> Unit) { + val pseudocode = pseudocodeCache[this] ?: return + val pseudoValue = pseudocode.getElementValue(this) ?: return + pseudocode.getUsages(pseudoValue).forEach { processor(pseudoValue, it) } + } + + private fun KtExpression.processDereferences() { + processPseudocodeUsages { pseudoValue, instr -> + when (instr) { + is ReadValueInstruction -> processDereferenceIsNeeded(this, pseudoValue, instr) + is CallInstruction -> processDereferenceIsNeeded(this, pseudoValue, instr) + } + } + } + + private fun KtExpression.processExpression() { + processPseudocodeUsages { pseudoValue, instr -> + when (instr) { + is WriteValueInstruction -> instr.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor() + is CallInstruction -> { + if (parentUsage.lambdaLevel > 0 && instr.receiverValues[pseudoValue] != null) { + instr.element.passToProcessor(parentUsage.lambdaLevel - 1) + } else { + instr.arguments[pseudoValue]?.originalSource?.getPsi()?.passToProcessor() + } + } + is ReturnValueInstruction -> instr.subroutine.passToProcessor() + is MagicInstruction -> when (instr.kind) { + MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> instr.outputValue.element?.passToProcessor() + else -> { + } + } + } + } + } + + override fun processChildren() { + if (parentUsage.forcedExpressionMode) return element.processExpression() + + when (element) { + is KtProperty, is KtParameter -> (element as KtDeclaration).processVariable() + is KtFunction -> element.processFunction() + is KtPropertyAccessor -> if (element.isGetter) { + element.property.processVariable() + } + else -> element.processExpression() + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index 0cce5619533..c40c91262b7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -6,103 +6,34 @@ package org.jetbrains.kotlin.idea.slicer import com.intellij.analysis.AnalysisScope -import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access -import com.intellij.psi.PsiCall import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod -import com.intellij.psi.impl.light.LightMemberReference -import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.SearchScope -import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.slicer.JavaSliceUsage import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.asJava.namedUnwrappedElement -import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor -import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode -import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction -import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* -import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction -import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder -import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors -import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor -import org.jetbrains.kotlin.idea.caches.resolve.* +import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions 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.processAllUsages -import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar -import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar -import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference -import org.jetbrains.kotlin.idea.references.ReferenceAccess -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.idea.search.ideaExtensions.KotlinReadWriteAccessDetector -import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.psi.psiUtil.contains import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument -import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.source.getPsi -import org.jetbrains.kotlin.util.OperatorNameConventions import java.util.* -private fun PsiElement.processHierarchyDownward(scope: SearchScope, processor: PsiElement.() -> Unit) { - processor() - HierarchySearchRequest(this, scope).searchOverriders().forEach { - it.namedUnwrappedElement?.processor() - } -} - -private fun KtDeclaration.processHierarchyUpward(scope: AnalysisScope, processor: PsiElement.() -> Unit) { - processor() - val descriptor = unsafeResolveToDescriptor() as? CallableMemberDescriptor ?: return - DescriptorUtils - .getAllOverriddenDescriptors(descriptor) - .asSequence() - .mapNotNull { it.originalSource.getPsi() } - .filter { scope.contains(it) } - .toList() - .forEach(processor) -} - -private enum class AccessKind { - READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE -} - -private fun KtDeclaration.processVariableAccesses( - scope: SearchScope, - kind: AccessKind, - processor: (UsageInfo) -> Unit -) { - processAllExactUsages( - KotlinPropertyFindUsagesOptions(project).apply { - 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 - isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE - isSearchForTextOccurrences = false - isSkipImportStatements = true - searchScope = scope.intersectWith(useScope) - }, - processor - ) -} - -private fun KtParameter.canProcess() = !isVarArg - abstract class Slicer( protected val element: KtExpression, protected val processor: SliceUsageProcessor, @@ -168,385 +99,56 @@ abstract class Slicer( } } } -} -class InflowSlicer( - element: KtExpression, - processor: SliceUsageProcessor, - parentUsage: KotlinSliceUsage -) : Slicer(element, processor, parentUsage) { - private fun PsiElement.processHierarchyDownwardAndPass() { - processHierarchyDownward(parentUsage.scope.toSearchScope()) { passToProcessor() } - } - - private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true) - - private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) { - processVariableAccesses(accessSearchScope, AccessKind.WRITE_WITH_OPTIONAL_READ) body@{ - val refElement = it.element ?: return@body - val refParent = refElement.parent - - val rhsValue = when { - refElement is KtExpression -> { - val (accessKind, accessExpression) = refElement.readWriteAccessWithFullExpression(true) - if (accessKind == ReferenceAccess.WRITE && accessExpression is KtBinaryExpression && accessExpression.operationToken == KtTokens.EQ) { - accessExpression.right - } else { - accessExpression - } - } - - refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(0) - - else -> null - } - rhsValue?.passToProcessorAsValue() + protected fun PsiElement.processHierarchyDownward(scope: SearchScope, processor: PsiElement.() -> Unit) { + processor() + HierarchySearchRequest(this, scope).searchOverriders().forEach { + it.namedUnwrappedElement?.processor() } } - private fun KtPropertyAccessor.processBackingFieldAssignments() { - forEachDescendantOfType body@{ - if (it.operationToken != KtTokens.EQ) return@body - val lhs = it.left?.let { expression -> KtPsiUtil.safeDeparenthesize(expression) } ?: return@body - val rhs = it.right ?: return@body - if (!lhs.isBackingFieldReference()) return@body - rhs.passToProcessor() - } + protected fun KtDeclaration.processHierarchyUpward(scope: AnalysisScope, processor: PsiElement.() -> Unit) { + processor() + val descriptor = unsafeResolveToDescriptor() as? CallableMemberDescriptor ?: return + DescriptorUtils + .getAllOverriddenDescriptors(descriptor) + .asSequence() + .mapNotNull { it.originalSource.getPsi() } + .filter { scope.contains(it) } + .toList() + .forEach(processor) } - private fun KtProperty.processPropertyAssignments() { - val analysisScope = parentUsage.scope.toSearchScope() - val accessSearchScope = if (isVar) analysisScope - else { - val containerScope = getStrictParentOfType()?.let { LocalSearchScope(it) } ?: return - analysisScope.intersectWith(containerScope) - } - processAssignments(accessSearchScope) + protected enum class AccessKind { + READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE } - private fun KtProperty.processProperty() { - val bindingContext by lazy { analyzeWithContent() } - - if (hasDelegateExpression()) { - val getter = (unsafeResolveToDescriptor() as VariableDescriptorWithAccessors).getter - val delegateGetterResolvedCall = getter?.let { bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it] } - delegateGetterResolvedCall?.resultingDescriptor?.originalSource?.getPsi()?.passToProcessor() - return - } - - initializer?.passToProcessor() - - getter?.processFunction() - - val isDefaultGetter = getter?.bodyExpression == null - val isDefaultSetter = setter?.bodyExpression == null - if (isDefaultGetter) { - if (isDefaultSetter) { - processPropertyAssignments() - } else { - setter!!.processBackingFieldAssignments() - } - } - } - - private fun KtParameter.processParameter(includeOverriders: Boolean) { - if (!canProcess()) return - - val function = ownerFunction ?: return - - if (function is KtPropertyAccessor && function.isSetter) { - function.property.processPropertyAssignments() - return - } - - if (function is KtNamedFunction - && function.name == OperatorNameConventions.SET_VALUE.asString() - && function.hasModifier(KtTokens.OPERATOR_KEYWORD) - ) { - - ReferencesSearch - .search(function, parentUsage.scope.toSearchScope()) - .filterIsInstance() - .forEach { (it.element.parent as? KtProperty)?.processPropertyAssignments() } - } - - val parameterDescriptor = resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return - - (function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope(), includeOverriders) body@{ - val refElement = it.element ?: return@body - val refParent = refElement.parent - - val argumentExpression = when { - refElement is KtExpression -> { - val callElement = refElement.getParentOfTypeAndBranch { calleeExpression } ?: return@body - val resolvedCall = callElement.resolveToCall() ?: return@body - val callParameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[parameterDescriptor.index] - val resolvedArgument = resolvedCall.valueArguments[callParameterDescriptor] ?: return@body - when (resolvedArgument) { - is DefaultValueArgument -> defaultValue - is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression() - else -> null - } - } - - refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(this@processParameter.parameterIndex()) - - else -> null - } - argumentExpression?.passToProcessorAsValue() - } - - if (valOrVarKeyword.toValVar() == KotlinValVar.Var) { - processAssignments(parentUsage.scope.toSearchScope()) - } - } - - private fun KtDeclarationWithBody.processFunction() { - val bodyExpression = bodyExpression ?: return - val pseudocode = pseudocodeCache[bodyExpression] ?: return - pseudocode.traverse(TraversalOrder.FORWARD) { instr -> - if (instr is ReturnValueInstruction && instr.subroutine == this) { - (instr.returnExpressionIfAny?.returnedExpression ?: instr.element as? KtExpression)?.passToProcessorAsValue() - } - } - } - - private fun Instruction.passInputsToProcessor() { - inputValues.forEach { - if (it.createdAt != null) { - it.element?.passToProcessorAsValue() - } - } - } - - private fun KtExpression.isBackingFieldReference(): Boolean { - return this is KtSimpleNameExpression && - getReferencedName() == SyntheticFieldDescriptor.NAME.asString() && - resolveToCall()?.resultingDescriptor is SyntheticFieldDescriptor - } - - private fun KtExpression.processExpression() { - val lambda = when (this) { - is KtLambdaExpression -> functionLiteral - is KtNamedFunction -> if (name == null) this else null - else -> null - } - if (lambda != null) { - if (parentUsage.lambdaLevel > 0) { - lambda.passToProcessor(parentUsage.lambdaLevel - 1) - } - return - } - - val pseudocode = pseudocodeCache[this] ?: return - val expressionValue = pseudocode.getElementValue(this) ?: return - when (val createdAt = expressionValue.createdAt) { - is ReadValueInstruction -> { - if (createdAt.target == AccessTarget.BlackBox) { - val originalElement = expressionValue.element as? KtExpression ?: return - if (originalElement != this) { - originalElement.processExpression() - } - return - } - val accessedDescriptor = createdAt.target.accessedDescriptor ?: return - val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return - if (accessedDescriptor is SyntheticFieldDescriptor) { - val property = accessedDeclaration as? KtProperty ?: return - if (accessedDescriptor.propertyDescriptor.setter?.isDefault != false) { - property.processPropertyAssignments() - } else { - property.setter?.processBackingFieldAssignments() - } - return - } - accessedDeclaration.processHierarchyDownwardAndPass() - } - - is MergeInstruction -> createdAt.passInputsToProcessor() - - is MagicInstruction -> when (createdAt.kind) { - MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> createdAt.passInputsToProcessor() - MagicKind.BOUND_CALLABLE_REFERENCE, MagicKind.UNBOUND_CALLABLE_REFERENCE -> { - val callableRefExpr = expressionValue.element as? KtCallableReferenceExpression ?: return - val referencedDescriptor = analyze()[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return - val referencedDeclaration = - (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi() ?: return - referencedDeclaration.passToProcessor(parentUsage.lambdaLevel - 1) - } - else -> return - } - - is CallInstruction -> { - val resolvedCall = createdAt.resolvedCall - val resultingDescriptor = resolvedCall.resultingDescriptor - if (resultingDescriptor is FunctionInvokeDescriptor) { - (resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression?.passToProcessorAsValue(parentUsage.lambdaLevel + 1) - } else { - resultingDescriptor.originalSource.getPsi()?.processHierarchyDownwardAndPass() - } - } - } - } - - override fun processChildren() { - if (parentUsage.forcedExpressionMode) return element.processExpression() - - when (element) { - is KtProperty -> element.processProperty() - // for parameter, we include overriders only when the feature is invoked on parameter itself - is KtParameter -> element.processParameter(includeOverriders = parentUsage.parent == null) - is KtDeclarationWithBody -> element.processFunction() - else -> element.processExpression() - } - } -} - -class OutflowSlicer( - element: KtExpression, - processor: SliceUsageProcessor, - parentUsage: KotlinSliceUsage -) : Slicer(element, processor, parentUsage) { - private fun KtDeclaration.processVariable() { - processHierarchyUpward(parentUsage.scope) { - if (this is KtParameter && !canProcess()) return@processHierarchyUpward - - val withDereferences = parentUsage.params.showInstanceDereferences - val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY - (this as? KtDeclaration)?.processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{ - val refElement = it.element - if (refElement !is KtExpression) { - refElement?.passToProcessor() - return@body - } - - val refExpression = KtPsiUtil.safeDeparenthesize(refElement) - if (withDereferences) { - refExpression.processDereferences() - } - if (!withDereferences || KotlinReadWriteAccessDetector.INSTANCE.getExpressionAccess(refExpression) == Access.Read) { - refExpression.passToProcessor() - } - } - } - } - - private fun PsiElement.getCallElementForExactCallee(): PsiElement? { - if (this is KtArrayAccessExpression) return this - - val operationRefExpr = getNonStrictParentOfType() - if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression - - val parentCall = getParentOfTypeAndBranch { calleeExpression } ?: return null - val callee = parentCall.calleeExpression?.let { KtPsiUtil.safeDeparenthesize(it) } - if (callee == this || callee is KtConstructorCalleeExpression && callee.isAncestor(this, true)) return parentCall - - return null - } - - private fun PsiElement.getCallableReferenceForExactCallee(): KtCallableReferenceExpression? { - val callableRef = getParentOfTypeAndBranch { callableReference } ?: return null - val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference) - return if (callee == this) callableRef else null - } - - private fun KtFunction.processFunction() { - if (this is KtConstructor<*> || this is KtNamedFunction && name != null) { - processCalls(parentUsage.scope.toSearchScope(), includeOverriders = false) { - when (val refElement = it.element) { - null -> (it.reference as? LightMemberReference)?.element?.passToProcessor() - is KtExpression -> { - refElement.getCallElementForExactCallee()?.passToProcessor() - refElement.getCallableReferenceForExactCallee()?.passToProcessor(parentUsage.lambdaLevel + 1) - } - else -> refElement.passToProcessor() - } - } - return - } - - val funExpression = when (this) { - is KtFunctionLiteral -> parent as? KtLambdaExpression - is KtNamedFunction -> this - else -> null - } ?: return - (funExpression as PsiElement).passToProcessor(parentUsage.lambdaLevel + 1, true) - } - - private fun processDereferenceIsNeeded( - expression: KtExpression, - pseudoValue: PseudoValue, - instr: InstructionWithReceivers + protected fun KtDeclaration.processVariableAccesses( + scope: SearchScope, + kind: AccessKind, + processor: (UsageInfo) -> Unit ) { - if (!parentUsage.params.showInstanceDereferences) return - - val receiver = instr.receiverValues[pseudoValue] - val resolvedCall = when (instr) { - is CallInstruction -> instr.resolvedCall - is ReadValueInstruction -> (instr.target as? AccessTarget.Call)?.resolvedCall - else -> null - } ?: return - - if (receiver != null && resolvedCall.dispatchReceiver == receiver) { - processor.process(KotlinSliceDereferenceUsage(expression, parentUsage, parentUsage.lambdaLevel)) - } + processAllExactUsages( + KotlinPropertyFindUsagesOptions(project).apply { + 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 + isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE + isSearchForTextOccurrences = false + isSkipImportStatements = true + searchScope = scope.intersectWith(useScope) + }, + processor + ) } - private fun KtExpression.processPseudocodeUsages(processor: (PseudoValue, Instruction) -> Unit) { - val pseudocode = pseudocodeCache[this] ?: return - val pseudoValue = pseudocode.getElementValue(this) ?: return - pseudocode.getUsages(pseudoValue).forEach { processor(pseudoValue, it) } - } + protected fun KtParameter.canProcess() = !isVarArg - private fun KtExpression.processDereferences() { - processPseudocodeUsages { pseudoValue, instr -> - when (instr) { - is ReadValueInstruction -> processDereferenceIsNeeded(this, pseudoValue, instr) - is CallInstruction -> processDereferenceIsNeeded(this, pseudoValue, instr) + protected val DeclarationDescriptorWithSource.originalSource: SourceElement + get() { + var descriptor = this + while (descriptor.original != descriptor) { + descriptor = descriptor.original } + return descriptor.source } - } - - private fun KtExpression.processExpression() { - processPseudocodeUsages { pseudoValue, instr -> - when (instr) { - is WriteValueInstruction -> instr.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor() - is CallInstruction -> { - if (parentUsage.lambdaLevel > 0 && instr.receiverValues[pseudoValue] != null) { - instr.element.passToProcessor(parentUsage.lambdaLevel - 1) - } else { - instr.arguments[pseudoValue]?.originalSource?.getPsi()?.passToProcessor() - } - } - is ReturnValueInstruction -> instr.subroutine.passToProcessor() - is MagicInstruction -> when (instr.kind) { - MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> instr.outputValue.element?.passToProcessor() - else -> { - } - } - } - } - } - - override fun processChildren() { - if (parentUsage.forcedExpressionMode) return element.processExpression() - - when (element) { - is KtProperty, is KtParameter -> (element as KtDeclaration).processVariable() - is KtFunction -> element.processFunction() - is KtPropertyAccessor -> if (element.isGetter) { - element.property.processVariable() - } - else -> element.processExpression() - } - } } - -private val DeclarationDescriptorWithSource.originalSource: SourceElement - get() { - var descriptor = this - while (descriptor.original != descriptor) { - descriptor = descriptor.original - } - return descriptor.source - }