Moving declarations

This commit is contained in:
Valentin Kipyatkov
2020-04-11 12:46:30 +03:00
parent 52ff1c62dd
commit 6928700320
6 changed files with 205 additions and 171 deletions
@@ -0,0 +1,68 @@
/*
* 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.psi.PsiCall
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.source.getPsi
@Suppress("DataClassPrivateConstructor") // we have modifier data to get equals&hashCode only
data class ArgumentSliceProducer private constructor(
private val parameterIndex: Int,
private val isExtension: Boolean
) : SliceProducer
{
constructor(parameterDescriptor: ValueParameterDescriptor) : this(
parameterDescriptor.index,
parameterDescriptor.containingDeclaration.isExtension
)
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
val element = usage.element ?: return emptyList()
val argumentExpression = extractArgumentExpression(element) ?: return emptyList()
return listOf(KotlinSliceUsage(argumentExpression, parent, behaviour, forcedExpressionMode = true))
}
private fun extractArgumentExpression(refElement: PsiElement): PsiElement? {
val refParent = refElement.parent
return when {
refElement is KtExpression -> {
val callElement = refElement as? KtCallElement
?: refElement.getParentOfTypeAndBranch { calleeExpression }
?: return null
val resolvedCall = callElement.resolveToCall() ?: return null
val resultingDescriptor = resolvedCall.resultingDescriptor
val parameterIndexToUse = parameterIndex +
(if (isExtension && resultingDescriptor.extensionReceiverParameter == null) 1 else 0)
val parameterDescriptor = resultingDescriptor.valueParameters[parameterIndexToUse]
val resolvedArgument = resolvedCall.valueArguments[parameterDescriptor] ?: return null
when (resolvedArgument) {
is DefaultValueArgument -> (parameterDescriptor.source.getPsi() as? KtParameter)?.defaultValue
is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression()
else -> null
}
}
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
}
}
}
@@ -0,0 +1,61 @@
/*
* 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.psi.PsiElement
import com.intellij.psi.impl.light.LightMemberReference
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo
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
object CallSliceProducer : SliceProducer {
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
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<SliceUsage>().apply {
refElement.getCallElementForExactCallee()
?.let { this += KotlinSliceUsage(it, parent, behaviour, false) }
refElement.getCallableReferenceForExactCallee()
?.let { this += KotlinSliceUsage(it, parent, LambdaCallsBehaviour(SliceProducer.Trivial, 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<KtOperationReferenceExpression>()
if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression
val parentCall = getParentOfTypeAndBranch<KtCallElement> { 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<KtCallableReferenceExpression> { callableReference } ?: return null
val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference)
return if (callee == this) callableRef else null
}
}
@@ -7,11 +7,9 @@ package org.jetbrains.kotlin.idea.slicer
import com.intellij.psi.PsiCall import com.intellij.psi.PsiCall
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope 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.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
@@ -37,15 +35,10 @@ import org.jetbrains.kotlin.idea.util.isExpectDeclaration
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
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext 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.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -327,101 +320,4 @@ class InflowSlicer(
} }
} }
} }
}
@Suppress("DataClassPrivateConstructor") // we have modifier data to get equals&hashCode only
private data class ArgumentSliceProducer private constructor(
private val parameterIndex: Int,
private val isExtension: Boolean
) : SliceProducer
{
constructor(parameterDescriptor: ValueParameterDescriptor) : this(
parameterDescriptor.index,
parameterDescriptor.containingDeclaration.isExtension
)
override fun produce(
usage: UsageInfo,
behaviour: KotlinSliceUsage.SpecialBehaviour?,
parent: SliceUsage
): Collection<SliceUsage>? {
val element = usage.element ?: return emptyList()
val argumentExpression = extractArgumentExpression(element) ?: return emptyList()
return listOf(KotlinSliceUsage(argumentExpression, parent, behaviour, forcedExpressionMode = true))
}
private fun extractArgumentExpression(refElement: PsiElement): PsiElement? {
val refParent = refElement.parent
return when {
refElement is KtExpression -> {
val callElement = refElement as? KtCallElement
?: refElement.getParentOfTypeAndBranch { calleeExpression }
?: return null
val resolvedCall = callElement.resolveToCall() ?: return null
val resultingDescriptor = resolvedCall.resultingDescriptor
val parameterIndexToUse = parameterIndex +
(if (isExtension && resultingDescriptor.extensionReceiverParameter == null) 1 else 0)
val parameterDescriptor = resultingDescriptor.valueParameters[parameterIndexToUse]
val resolvedArgument = resolvedCall.valueArguments[parameterDescriptor] ?: return null
when (resolvedArgument) {
is DefaultValueArgument -> (parameterDescriptor.source.getPsi() as? KtParameter)?.defaultValue
is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression()
else -> null
}
}
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<SliceUsage>? {
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, forcedExpressionMode = true))
}
is ImplicitReceiver -> {
val callableDescriptor = receiver.declarationDescriptor as? CallableDescriptor ?: return emptyList()
when (val callableDeclaration = callableDescriptor.originalSource.getPsi()) {
is KtFunctionLiteral -> {
val newBehaviour = LambdaCallsBehaviour(ReceiverSliceProducer, behaviour)
return listOf(KotlinSliceUsage(callableDeclaration, parent, newBehaviour, forcedExpressionMode = true))
}
is KtCallableDeclaration -> {
val receiverTypeReference = callableDeclaration.receiverTypeReference ?: return emptyList()
return listOf(KotlinSliceUsage(receiverTypeReference, parent, behaviour, false))
}
else -> return emptyList()
}
}
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
}
}
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.idea.slicer package org.jetbrains.kotlin.idea.slicer
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.light.LightMemberReference
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
@@ -27,7 +24,8 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDete
import org.jetbrains.kotlin.idea.util.actualsForExpected import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.isExpectDeclaration import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
@@ -267,54 +265,4 @@ class OutflowSlicer(
} }
} }
} }
}
private object CallSliceProducer : SliceProducer {
override fun produce(
usage: UsageInfo,
behaviour: KotlinSliceUsage.SpecialBehaviour?,
parent: SliceUsage
): Collection<SliceUsage>? {
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<SliceUsage>().apply {
refElement.getCallElementForExactCallee()
?.let { this += KotlinSliceUsage(it, parent, behaviour, false) }
refElement.getCallableReferenceForExactCallee()
?.let { this += KotlinSliceUsage(it, parent, LambdaCallsBehaviour(SliceProducer.Trivial, 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<KtOperationReferenceExpression>()
if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression
val parentCall = getParentOfTypeAndBranch<KtCallElement> { 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<KtCallableReferenceExpression> { callableReference } ?: return null
val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference)
return if (callee == this) callableRef else null
}
}
}
@@ -0,0 +1,62 @@
/*
* 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.psi.PsiCall
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.source.getPsi
object ReceiverSliceProducer : SliceProducer {
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
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, forcedExpressionMode = true))
}
is ImplicitReceiver -> {
val callableDescriptor = receiver.declarationDescriptor as? CallableDescriptor
?: return emptyList()
when (val callableDeclaration = callableDescriptor.originalSource.getPsi()) {
is KtFunctionLiteral -> {
val newBehaviour = LambdaCallsBehaviour(ReceiverSliceProducer, behaviour)
return listOf(KotlinSliceUsage(callableDeclaration, parent, newBehaviour, forcedExpressionMode = true))
}
is KtCallableDeclaration -> {
val receiverTypeReference = callableDeclaration.receiverTypeReference ?: return emptyList()
return listOf(KotlinSliceUsage(receiverTypeReference, parent, behaviour, false))
}
else -> return emptyList()
}
}
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
}
@@ -178,15 +178,14 @@ abstract class Slicer(
} }
protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg
protected companion object {
val DeclarationDescriptorWithSource.originalSource: SourceElement
get() {
var descriptor = this
while (descriptor.original != descriptor) {
descriptor = descriptor.original
}
return descriptor.source
}
}
} }
val DeclarationDescriptorWithSource.originalSource: SourceElement
get() {
var descriptor = this
while (descriptor.original != descriptor) {
descriptor = descriptor.original
}
return descriptor.source
}