Code refactoring + better handling of incoming data flow to java super methods

This commit is contained in:
Valentin Kipyatkov
2020-04-10 23:51:46 +03:00
parent b5b0fd62a9
commit 1e39d4a63c
7 changed files with 172 additions and 129 deletions
@@ -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<SliceUsage>? {
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<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, 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
}
}
@@ -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)
@@ -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<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
}
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<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, 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<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,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<SliceUsage>?
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
}
@@ -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
}
}
}
+3 -2
View File
@@ -1,7 +1,8 @@
2 void <bold>foo(int p);</bold>
22 i.<bold>foo</bold>(3)
9 val v = <bold>p</bold>
8 override fun foo(<bold>p: Int</bold>) {
2 void <bold>foo(int p);</bold>
2 void foo(int <bold>p</bold>);
22 i.<bold>foo</bold>(3)
13 foo(<bold>1</bold>)
9 val v = <bold>p</bold>
+3 -1
View File
@@ -1,5 +1,7 @@
9 val v = <bold>p</bold>
8 override fun foo(<bold>p: Int</bold>) {
2 void <bold>foo(int p);</bold>
2 void foo(int <bold>p</bold>);
13 <bold>foo</bold>(1)
22 i.<bold>foo</bold>(3)
13 foo(<bold>1</bold>)
18 i.foo(<bold>2</bold>)