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.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
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
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.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.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.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.source.getPsi
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
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
@@ -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.isExpectDeclaration
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.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 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
}