Skip to content

Commit

Permalink
Data flow to/from here: supported expected/actual declarations and ex…
Browse files Browse the repository at this point in the history
…tension receivers
  • Loading branch information
valentinkip committed Apr 17, 2020
1 parent bfa3fb0 commit 90188ef
Show file tree
Hide file tree
Showing 78 changed files with 1,702 additions and 799 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingT
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest
import org.jetbrains.kotlin.idea.script.AbstractScriptDefinitionsOrderTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerMultiplatformTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
Expand Down Expand Up @@ -872,7 +873,7 @@ fun main(args: Array<String>) {
}

testClass<AbstractSlicerTreeTest> {
model("slicer", singleClass = true)
model("slicer", excludeDirs = listOf("mpp"))
}

testClass<AbstractSlicerLeafGroupingTest> {
Expand All @@ -882,6 +883,10 @@ fun main(args: Array<String>) {
testClass<AbstractSlicerNullnessGroupingTest> {
model("slicer/inflow", singleClass = true)
}

testClass<AbstractSlicerMultiplatformTest> {
model("slicer/mpp", recursive = false, extension = null)
}
}

testGroup("idea/idea-fir/tests", "idea/testData") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import org.jetbrains.kotlin.idea.script.AbstractScriptDefinitionsOrderTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerMultiplatformTest
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
import org.jetbrains.kotlin.idea.stubs.AbstractMultiFileHighlightingTest
import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest
Expand Down Expand Up @@ -821,7 +822,7 @@ fun main(args: Array<String>) {
}

testClass<AbstractSlicerTreeTest> {
model("slicer", singleClass = true)
model("slicer", excludeDirs = listOf("mpp"))
}

testClass<AbstractSlicerLeafGroupingTest> {
Expand All @@ -831,6 +832,10 @@ fun main(args: Array<String>) {
testClass<AbstractSlicerNullnessGroupingTest> {
model("slicer/inflow", singleClass = true)
}

testClass<AbstractSlicerMultiplatformTest> {
model("slicer/mpp", recursive = false, extension = null)
}
}

testGroup("idea/scripting-support/test", "idea/scripting-support/testData") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import org.jetbrains.kotlin.idea.script.AbstractScriptDefinitionsOrderTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerMultiplatformTest
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
import org.jetbrains.kotlin.idea.stubs.AbstractMultiFileHighlightingTest
import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest
Expand Down Expand Up @@ -821,7 +822,7 @@ fun main(args: Array<String>) {
}

testClass<AbstractSlicerTreeTest> {
model("slicer", singleClass = true)
model("slicer", excludeDirs = listOf("mpp"))
}

testClass<AbstractSlicerLeafGroupingTest> {
Expand All @@ -831,6 +832,10 @@ fun main(args: Array<String>) {
testClass<AbstractSlicerNullnessGroupingTest> {
model("slicer/inflow", singleClass = true)
}

testClass<AbstractSlicerMultiplatformTest> {
model("slicer/mpp", recursive = false, extension = null)
}
}

testGroup("idea/scripting-support/test", "idea/scripting-support/testData") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import org.jetbrains.kotlin.idea.script.AbstractScriptDefinitionsOrderTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerMultiplatformTest
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
import org.jetbrains.kotlin.idea.stubs.AbstractMultiFileHighlightingTest
import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest
Expand Down Expand Up @@ -821,7 +822,7 @@ fun main(args: Array<String>) {
}

testClass<AbstractSlicerTreeTest> {
model("slicer", singleClass = true)
model("slicer", excludeDirs = listOf("mpp"))
}

testClass<AbstractSlicerLeafGroupingTest> {
Expand All @@ -831,6 +832,10 @@ fun main(args: Array<String>) {
testClass<AbstractSlicerNullnessGroupingTest> {
model("slicer/inflow", singleClass = true)
}

testClass<AbstractSlicerMultiplatformTest> {
model("slicer/mpp", recursive = false, extension = null)
}
}

testGroup("idea/scripting-support/test", "idea/scripting-support/testData") {
Expand Down
92 changes: 77 additions & 15 deletions idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ 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.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.*
Expand All @@ -30,6 +32,8 @@ 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.util.actualsForExpected
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
Expand All @@ -45,23 +49,34 @@ import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.OperatorNameConventions

class InflowSlicer(
element: KtExpression,
element: KtElement,
processor: SliceUsageProcessor,
parentUsage: KotlinSliceUsage
) : Slicer(element, processor, parentUsage) {

override fun processChildren() {
if (parentUsage.forcedExpressionMode) {
processExpression(element)
(element as? KtExpression)?.let { processExpression(it) }
return
}

when (element) {
is KtProperty -> processProperty(element)
// for parameter, we include overriders only when the feature is invoked on parameter itself

// include overriders only when invoked on the parameter declaration
is KtParameter -> processParameter(parameter = element, includeOverriders = parentUsage.parent == null)

is KtDeclarationWithBody -> element.processBody()
else -> processExpression(element)

is KtTypeReference -> {
val parent = element.parent
require(parent is KtCallableDeclaration)
require(element == parent.receiverTypeReference)
// include overriders only when invoked on receiver type in the declaration
processExtensionReceiver(parent, includeOverriders = parentUsage.parent == null)
}

is KtExpression -> processExpression(element)
}
}

Expand Down Expand Up @@ -135,8 +150,9 @@ class InflowSlicer(
}

fun processCall(usageInfo: UsageInfo) {
val refElement = usageInfo.element ?: return
extractArgumentExpression(refElement)?.passToProcessorAsValue()
usageInfo.element?.let {
extractArgumentExpression(it)?.passToProcessorAsValue()
}
}

processCalls(function, analysisScope, includeOverriders, ::processCall)
Expand All @@ -147,6 +163,31 @@ class InflowSlicer(
}
}

private fun processExtensionReceiver(declaration: KtCallableDeclaration, includeOverriders: Boolean) {
fun extractReceiverExpression(refElement: PsiElement): PsiElement? {
return when (refElement) {
is KtExpression -> {
val resolvedCall = refElement.resolveToCall() ?: return null
//TODO: implicit receiver
val expressionReceiver = resolvedCall.extensionReceiver as? ExpressionReceiver ?: return null
return expressionReceiver.expression
}

else -> {
(refElement.parent as? PsiCall)?.argumentList?.expressions?.getOrNull(0)
}
}
}

fun processCall(usageInfo: UsageInfo) {
usageInfo.element?.let {
extractReceiverExpression(it)?.passToProcessorAsValue()
}
}

processCalls(declaration, analysisScope, includeOverriders, ::processCall)
}

private fun processExpression(expression: KtExpression) {
val lambda = when (expression) {
is KtLambdaExpression -> expression.functionLiteral
Expand All @@ -171,18 +212,30 @@ class InflowSlicer(
}
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()
val accessedDeclaration = accessedDescriptor.originalSource.getPsi()
when (accessedDescriptor) {
is SyntheticFieldDescriptor -> {
val property = accessedDeclaration as? KtProperty ?: return
if (accessedDescriptor.propertyDescriptor.setter?.isDefault != false) {
property.processPropertyAssignments()
} else {
property.setter?.processBackingFieldAssignments()
}
}

is ReceiverParameterDescriptor -> {
val callable = accessedDescriptor.containingDeclaration as? CallableDescriptor ?: return
val callableDeclaration = callable.originalSource.getPsi() as? KtCallableDeclaration ?: return
//TODO: what about non-extensions?
callableDeclaration.receiverTypeReference?.passToProcessor()
}

else -> {
accessedDeclaration?.passDeclarationToProcessorWithOverriders()
}
return
}
accessedDeclaration.passDeclarationToProcessorWithOverriders()
}

is MergeInstruction -> createdAt.passInputsToProcessor()
Expand Down Expand Up @@ -287,8 +340,17 @@ class InflowSlicer(

private fun PsiElement.passDeclarationToProcessorWithOverriders() {
passToProcessor()

HierarchySearchRequest(this, analysisScope)
.searchOverriders()
.forEach { it.namedUnwrappedElement?.passToProcessor() }

if (this is KtCallableDeclaration && isExpectDeclaration()) {
resolveToDescriptorIfAny(BodyResolveMode.FULL)
?.actualsForExpected()
?.forEach {
(it as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()?.passToProcessor()
}
}
}
}
32 changes: 21 additions & 11 deletions idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.intellij.codeInsight.Nullability
import com.intellij.ide.util.treeView.AbstractTreeStructure
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.psi.PsiElement
import com.intellij.psi.util.parentOfType
import com.intellij.slicer.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
Expand All @@ -30,6 +31,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isPlainWithEscapes
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi2ir.deparenthesize
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isError
Expand Down Expand Up @@ -73,26 +75,34 @@ class KotlinSliceProvider : SliceLanguageSupportProvider, SliceUsageTransformer
return listOf(KotlinSliceUsage(usage.element, usage.parent, 0, false))
}

override fun getExpressionAtCaret(atCaret: PsiElement, dataFlowToThis: Boolean): KtExpression? {
val element =
atCaret.parentsWithSelf
.firstOrNull {
it is KtProperty ||
it is KtParameter ||
it is KtDeclarationWithBody ||
(it is KtClass && !it.hasExplicitPrimaryConstructor()) ||
(it is KtExpression && it !is KtDeclaration)
}
?.let { KtPsiUtil.safeDeparenthesize(it as KtExpression) } ?: return null
override fun getExpressionAtCaret(atCaret: PsiElement, dataFlowToThis: Boolean): KtElement? {
val element = atCaret.parentsWithSelf
.filterIsInstance<KtElement>()
.firstOrNull(::isSliceElement)
?.deparenthesize() ?: return null

if (dataFlowToThis) {
if (element is KtConstantExpression) return null
if (element is KtStringTemplateExpression && element.isPlainWithEscapes()) return null
if (element is KtClassLiteralExpression) return null
if (element is KtCallableReferenceExpression) return null
}

return element
}

private fun isSliceElement(element: KtElement): Boolean {
return when {
element is KtProperty -> true
element is KtParameter -> true
element is KtDeclarationWithBody -> true
element is KtClass && !element.hasExplicitPrimaryConstructor() -> true
element is KtExpression && element !is KtDeclaration && element.parentOfType<KtTypeReference>() == null -> true
element is KtTypeReference && element == (element.parent as? KtCallableDeclaration)?.receiverTypeReference -> true
else -> false
}
}

override fun getElementForDescription(element: PsiElement): PsiElement {
return (element as? KtSimpleNameExpression)?.mainReference?.resolve() ?: element
}
Expand Down
5 changes: 3 additions & 2 deletions idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.intellij.slicer.SliceAnalysisParams
import com.intellij.slicer.SliceUsage
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression

open class KotlinSliceUsage : SliceUsage {
Expand Down Expand Up @@ -58,10 +59,10 @@ open class KotlinSliceUsage : SliceUsage {
override fun canBeLeaf() = element != null && lambdaLevel == 0

public override fun processUsagesFlownDownTo(element: PsiElement, uniqueProcessor: SliceUsageProcessor) {
InflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren()
InflowSlicer(element as? KtElement ?: return, uniqueProcessor, this).processChildren()
}

public override fun processUsagesFlownFromThe(element: PsiElement, uniqueProcessor: SliceUsageProcessor) {
OutflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren()
OutflowSlicer(element as? KtElement ?: return, uniqueProcessor, this).processChildren()
}
}
Loading

0 comments on commit 90188ef

Please sign in to comment.