diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt index 6ee4059e5c2ef..df86958eb40e1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.slicer import com.intellij.psi.PsiCall import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.SearchScope import com.intellij.psi.search.searches.ReferencesSearch @@ -33,7 +34,6 @@ import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchReques import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders import org.jetbrains.kotlin.idea.util.actualsForExpected import org.jetbrains.kotlin.idea.util.isExpectDeclaration -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType @@ -129,25 +129,7 @@ class InflowSlicer( val parameterDescriptor = parameter.resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return if (function is KtFunction) { - val sliceTransformer = ArgumentExpressionTransformer(parameterDescriptor) - - when { - function is KtFunctionLiteral -> { - processFunctionLiteralCalls(function, sliceTransformer) - } - - function is KtNamedFunction && function.name == null -> { - processAnonymousFunctionCalls(function, sliceTransformer) - } - - else -> { - processCalls(function, analysisScope, includeOverriders) { usageInfo -> - usageInfo.element?.let { - sliceTransformer.extractArgumentExpression(it)?.passToProcessorAsValue() - } - } - } - } + processCalls(function, includeOverriders, ArgumentSliceProducer(parameterDescriptor)) } if (parameter.valOrVarKeyword.toValVar() == KotlinValVar.Var) { @@ -156,33 +138,7 @@ class InflowSlicer( } private fun processExtensionReceiver(declaration: KtCallableDeclaration, includeOverriders: Boolean) { - fun processReceiver(refElement: PsiElement) { - when (refElement) { - is KtExpression -> { - val resolvedCall = refElement.resolveToCall() ?: return - when (val receiver = resolvedCall.extensionReceiver) { - is ExpressionReceiver -> { - receiver.expression.passToProcessorAsValue() - } - - is ImplicitReceiver -> { - val callableDeclaration = (receiver.declarationDescriptor as? CallableDescriptor)?.originalSource?.getPsi() - (callableDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessor() - } - } - } - - else -> { - (refElement.parent as? PsiCall)?.argumentList?.expressions?.getOrNull(0)?.passToProcessorAsValue() - } - } - } - - processCalls(declaration, analysisScope, includeOverriders) { usageInfo -> - usageInfo.element?.let { - processReceiver(it) - } - } + processCalls(declaration, includeOverriders, ReceiverSliceProducer) } private fun processExpression(expression: KtExpression) { @@ -234,8 +190,8 @@ class InflowSlicer( val anonymousFunction = accessedDescriptor.containingDeclaration as? AnonymousFunctionDescriptor if (anonymousFunction != null && accessedDescriptor.name.asString() == "it") { val functionLiteral = anonymousFunction.source.getPsi() as KtFunctionLiteral - val sliceTransformer = ArgumentExpressionTransformer(anonymousFunction.valueParameters.first()) - processFunctionLiteralCalls(functionLiteral, sliceTransformer) + val sliceTransformer = ArgumentSliceProducer(anonymousFunction.valueParameters.first()) + processCalls(functionLiteral, false, sliceTransformer) } } else { accessedDeclaration.passDeclarationToProcessorWithOverriders() @@ -366,23 +322,27 @@ class InflowSlicer( } @Suppress("DataClassPrivateConstructor") // we have modifier data to get equals&hashCode only - private data class ArgumentExpressionTransformer private constructor( + private data class ArgumentSliceProducer private constructor( private val parameterIndex: Int, private val isExtension: Boolean - ) : KotlinSliceUsageTransformer + ) : SliceProducer { constructor(parameterDescriptor: ValueParameterDescriptor) : this( parameterDescriptor.index, parameterDescriptor.containingDeclaration.isExtension ) - override fun transform(usage: KotlinSliceUsage): SliceUsage? { - val element = usage.element ?: return null - val argumentExpression = extractArgumentExpression(element) ?: return null - return KotlinSliceUsage(argumentExpression, usage.parent, usage.behaviour?.originalBehaviour, forcedExpressionMode = true) + override fun produce( + usage: UsageInfo, + behaviour: KotlinSliceUsage.SpecialBehaviour?, + parent: SliceUsage + ): Collection? { + val element = usage.element ?: return emptyList() + val argumentExpression = extractArgumentExpression(element) ?: return emptyList() + return listOf(KotlinSliceUsage(argumentExpression, parent, behaviour, forcedExpressionMode = true)) } - fun extractArgumentExpression(refElement: PsiElement): PsiElement? { + private fun extractArgumentExpression(refElement: PsiElement): PsiElement? { val refParent = refElement.parent return when { refElement is KtExpression -> { @@ -404,8 +364,47 @@ class InflowSlicer( refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(parameterIndex + (if (isExtension) 1 else 0)) + refElement is PsiMethod -> refElement.parameterList.parameters.getOrNull(parameterIndex + (if (isExtension) 1 else 0)) + else -> null } } } + + private object ReceiverSliceProducer : SliceProducer { + override fun produce( + usage: UsageInfo, + behaviour: KotlinSliceUsage.SpecialBehaviour?, + parent: SliceUsage + ): Collection? { + val refElement = usage.element ?: return emptyList() + when (refElement) { + is KtExpression -> { + val resolvedCall = refElement.resolveToCall() ?: return emptyList() + when (val receiver = resolvedCall.extensionReceiver) { + is ExpressionReceiver -> { + return listOf(KotlinSliceUsage(receiver.expression, parent, behaviour, true)) + } + + is ImplicitReceiver -> { + val callableDeclaration = (receiver.declarationDescriptor as? CallableDescriptor)?.originalSource?.getPsi() + val receiverTypeReference = (callableDeclaration as? KtCallableDeclaration)?.receiverTypeReference + ?: return emptyList() + return listOf(KotlinSliceUsage(receiverTypeReference, parent, behaviour, false)) + } + + else -> return emptyList() + } + } + + else -> { + val argument = (refElement.parent as? PsiCall)?.argumentList?.expressions?.getOrNull(0) ?: return emptyList() + return listOf(KotlinSliceUsage(argument, parent, behaviour, true)) + } + } + } + + override fun equals(other: Any?) = other === this + override fun hashCode() = 0 + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt index 26e09caa1d8b9..d45fe7bd2872f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt @@ -11,17 +11,9 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor import org.jetbrains.kotlin.psi.Call import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver - -interface KotlinSliceUsageTransformer { - fun transform(usage: KotlinSliceUsage): SliceUsage? - - override fun equals(other: Any?): Boolean - override fun hashCode(): Int -} data class LambdaCallsBehaviour( - val usageTransformer: KotlinSliceUsageTransformer, + val sliceProducer: SliceProducer, override val originalBehaviour: KotlinSliceUsage.SpecialBehaviour? ) : KotlinSliceUsage.SpecialBehaviour { @@ -32,7 +24,7 @@ data class LambdaCallsBehaviour( val sliceElement = sliceUsage.element ?: return true val resolvedCall = (sliceElement as? KtElement)?.resolveToCall() if (resolvedCall?.call?.callType == Call.CallType.INVOKE) { - return usageTransformer.transform(sliceUsage)?.let { uniqueProcessor.process(it) } ?: true + return sliceProducer.produceAndProcess(sliceUsage, originalBehaviour, parent, uniqueProcessor) } } return uniqueProcessor.process(sliceUsage) diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt index df5369addd0a2..1b435016c6f50 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt @@ -9,6 +9,7 @@ import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.psi.impl.light.LightMemberReference +import com.intellij.slicer.SliceUsage import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction @@ -150,17 +151,7 @@ class OutflowSlicer( private fun processFunction(function: KtFunction) { if (function is KtConstructor<*> || function is KtNamedFunction && function.name != null) { - processCalls(function, analysisScope, includeOverriders = false) { usageInfo -> - when (val refElement = usageInfo.element) { - null -> (usageInfo.reference as? LightMemberReference)?.element?.passToProcessor() - is KtExpression -> { - refElement.getCallElementForExactCallee()?.passToProcessor() - refElement.getCallableReferenceForExactCallee() - ?.passToProcessor(LambdaResultOutflowBehaviour(behaviour)) - } - else -> refElement.passToProcessor() - } - } + processCalls(function, includeOverriders = false, sliceProducer = CallSliceProducer) return } @@ -249,25 +240,6 @@ class OutflowSlicer( return true } - 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, strict = 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 processDereferenceIfNeeded( expression: KtExpression, pseudoValue: PseudoValue, @@ -301,4 +273,54 @@ class OutflowSlicer( } } } + + private object CallSliceProducer : SliceProducer { + override fun produce( + usage: UsageInfo, + behaviour: KotlinSliceUsage.SpecialBehaviour?, + parent: SliceUsage + ): Collection? { + when (val refElement = usage.element) { + null -> { + val element = (usage.reference as? LightMemberReference)?.element ?: return emptyList() + return listOf(KotlinSliceUsage(element, parent, behaviour, false)) + } + + is KtExpression -> { + return mutableListOf().apply { + refElement.getCallElementForExactCallee() + ?.let { this += KotlinSliceUsage(it, parent, behaviour, false) } + refElement.getCallableReferenceForExactCallee() + ?.let { this += KotlinSliceUsage(it, parent, LambdaResultOutflowBehaviour(behaviour), false) } + } + } + + else -> { + return null // unknown type of usage - return null to process it "as is" + } + } + } + + override fun equals(other: Any?) = other === this + override fun hashCode() = 0 + + 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, strict = 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 + } + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt new file mode 100644 index 0000000000000..bbe7a09326371 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.slicer + +import com.intellij.slicer.SliceUsage +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor + +interface SliceProducer { + fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection? + + override fun equals(other: Any?): Boolean + override fun hashCode(): Int +} + +fun SliceProducer.produceAndProcess( + sliceUsage: SliceUsage, + behaviour: KotlinSliceUsage.SpecialBehaviour?, + parentUsage: SliceUsage, + processor: SliceUsageProcessor +): Boolean { + val result = produce(sliceUsage.usageInfo, behaviour, parentUsage) ?: listOf(sliceUsage) + for (usage in result) { + if (!processor.process(usage)) return false + } + return true +} \ 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 2ea2b986a8fa3..08dc6e9a144c9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -68,29 +68,18 @@ abstract class Slicer( passToProcessor(behaviour, forcedExpressionMode = true) } - protected fun processFunctionLiteralCalls( - functionLiteral: KtFunctionLiteral, - sliceTransformer: KotlinSliceUsageTransformer, - ) { - (functionLiteral.parent as KtLambdaExpression).passToProcessorAsValue(LambdaCallsBehaviour(sliceTransformer, behaviour)) - } - - protected fun processAnonymousFunctionCalls( - function: KtNamedFunction, - sliceTransformer: KotlinSliceUsageTransformer, - ) { - require(function.name == null) - function.passToProcessorAsValue(LambdaCallsBehaviour(sliceTransformer, behaviour)) - } - protected fun processCalls( callable: KtCallableDeclaration, - scope: SearchScope, includeOverriders: Boolean, - usageProcessor: (UsageInfo) -> Unit + sliceProducer: SliceProducer ) { if (callable is KtFunctionLiteral) { - //TODO + (callable.parent as KtLambdaExpression).passToProcessorAsValue(LambdaCallsBehaviour(sliceProducer, behaviour)) + return + } + + if (callable is KtFunction && callable.name == null) { + callable.passToProcessorAsValue(LambdaCallsBehaviour(sliceProducer, behaviour)) return } @@ -99,7 +88,7 @@ abstract class Slicer( KotlinFunctionFindUsagesOptions(project).apply { isSearchForTextOccurrences = false isSkipImportStatements = true - searchScope = scope + searchScope = analysisScope } } @@ -107,7 +96,7 @@ abstract class Slicer( KotlinPropertyFindUsagesOptions(project).apply { isSearchForTextOccurrences = false isSkipImportStatements = true - searchScope = scope + searchScope = analysisScope } } @@ -131,6 +120,11 @@ abstract class Slicer( val declaration = superDescriptor.originalSource.getPsi() ?: continue when (declaration) { is KtDeclaration -> { + val usageProcessor: (UsageInfo) -> Unit = processor@ { usageInfo -> + val element = usageInfo.element ?: return@processor + val sliceUsage = KotlinSliceUsage(element, parentUsage, behaviour, false) + sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor) + } if (includeOverriders) { declaration.processAllUsages(options, usageProcessor) } else { @@ -139,12 +133,13 @@ abstract class Slicer( } is PsiMethod -> { - // todo: work around the bug in JavaSliceProvider.transform() - processor.process(JavaSliceUsage.createRootUsage(declaration, parentUsage.params)) + val sliceUsage = JavaSliceUsage.createRootUsage(declaration, parentUsage.params) + sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor) } else -> { - declaration.passToProcessor() + val sliceUsage = KotlinSliceUsage(declaration, parentUsage, behaviour, false) + sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor) } } } @@ -189,12 +184,14 @@ abstract class Slicer( protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg - protected val DeclarationDescriptorWithSource.originalSource: SourceElement - get() { - var descriptor = this - while (descriptor.original != descriptor) { - descriptor = descriptor.original + protected companion object { + val DeclarationDescriptorWithSource.originalSource: SourceElement + get() { + var descriptor = this + while (descriptor.original != descriptor) { + descriptor = descriptor.original + } + return descriptor.source } - return descriptor.source - } + } } diff --git a/idea/testData/slicer/inflow/overrideFun.leafGroups.txt b/idea/testData/slicer/inflow/overrideFun.leafGroups.txt index 3da0399e37a9d..43eb929df214a 100644 --- a/idea/testData/slicer/inflow/overrideFun.leafGroups.txt +++ b/idea/testData/slicer/inflow/overrideFun.leafGroups.txt @@ -1,7 +1,8 @@ -2 void foo(int p); +22 i.foo(3) 9 val v = p 8 override fun foo(p: Int) { -2 void foo(int p); +2 void foo(int p); +22 i.foo(3) 13 foo(1) 9 val v = p diff --git a/idea/testData/slicer/inflow/overrideFun.results.txt b/idea/testData/slicer/inflow/overrideFun.results.txt index 1b02e1cfcf4c9..e670a570517df 100644 --- a/idea/testData/slicer/inflow/overrideFun.results.txt +++ b/idea/testData/slicer/inflow/overrideFun.results.txt @@ -1,5 +1,7 @@ 9 val v = p 8 override fun foo(p: Int) { -2 void foo(int p); +2 void foo(int p); +13 foo(1) +22 i.foo(3) 13 foo(1) 18 i.foo(2)