Tracking of receiver for incoming lambda's for outflow analysis + better handling of invoke and operator calls

This commit is contained in:
Valentin Kipyatkov
2020-04-12 20:39:06 +03:00
parent 8f01427534
commit 04d4f7444e
11 changed files with 262 additions and 95 deletions
@@ -140,19 +140,26 @@ class InflowSlicer(
}
val currentBehaviour = mode.currentBehaviour
if (lambda != null) {
if (currentBehaviour is LambdaResultInflowBehaviour) {
lambda.passToProcessor(mode.dropBehaviour())
}
else if (currentBehaviour is LambdaArgumentInflowBehaviour) {
val valueParameters = lambda.valueParameters
if (valueParameters.isEmpty() && lambda is KtFunctionLiteral) {
if (currentBehaviour.argumentIndex == 0) {
lambda.implicitItUsages().forEach {
it.passToProcessor(mode.dropBehaviour())
when (currentBehaviour) {
is LambdaResultInflowBehaviour -> {
lambda.passToProcessor(mode.dropBehaviour())
}
is LambdaArgumentInflowBehaviour -> {
val valueParameters = lambda.valueParameters
if (valueParameters.isEmpty() && lambda is KtFunctionLiteral) {
if (currentBehaviour.argumentIndex == 0) {
lambda.implicitItUsages().forEach {
it.passToProcessor(mode.dropBehaviour())
}
}
} else {
valueParameters.getOrNull(currentBehaviour.argumentIndex)?.passToProcessor(mode.dropBehaviour())
}
} else {
valueParameters.getOrNull(currentBehaviour.argumentIndex)?.passToProcessor(mode.dropBehaviour())
}
is LambdaReceiverInflowBehaviour -> {
processExtensionReceiverUsages(lambda, lambda, mode.dropBehaviour())
}
}
return
@@ -9,7 +9,6 @@ import com.intellij.slicer.SliceUsage
import org.jetbrains.kotlin.idea.KotlinBundle
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
data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : KotlinSliceAnalysisMode.Behaviour {
@@ -19,7 +18,7 @@ data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : Kotl
if (sliceUsage is KotlinSliceUsage && sliceUsage.mode.currentBehaviour === this@LambdaCallsBehaviour) {
val sliceElement = sliceUsage.element ?: return true
val resolvedCall = (sliceElement as? KtElement)?.resolveToCall()
if (resolvedCall?.call?.callType == Call.CallType.INVOKE) {
if (resolvedCall != null && resolvedCall.resultingDescriptor.isImplicitInvokeFunction()) {
val originalMode = sliceUsage.mode.dropBehaviour()
val newSliceUsage = KotlinSliceUsage(resolvedCall.call.callElement, parent, originalMode, true)
return sliceProducer.produceAndProcess(newSliceUsage, originalMode, parent, uniqueProcessor)
@@ -0,0 +1,24 @@
/*
* 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 org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.psi.KtElement
object LambdaReceiverInflowBehaviour : KotlinSliceAnalysisMode.Behaviour {
override fun processUsages(element: KtElement, parent: KotlinSliceUsage, uniqueProcessor: SliceUsageProcessor) {
InflowSlicer(element, uniqueProcessor, parent).processChildren(parent.forcedExpressionMode)
}
override val slicePresentationPrefix: String
get() = TODO()
override val testPresentationPrefix: String
get() = "[LAMBDA RECEIVER IN] "
override fun equals(other: Any?) = other === this
override fun hashCode() = 0
}
@@ -10,15 +10,11 @@ import com.intellij.psi.PsiMethod
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingElement
@@ -26,11 +22,9 @@ 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.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.OperatorNameConventions
class OutflowSlicer(
element: KtElement,
@@ -73,13 +67,13 @@ class OutflowSlicer(
when (declaration) {
is KtFunction -> {
processExtensionReceiver(declaration, declaration)
processExtensionReceiverUsages(declaration, declaration.bodyExpression, mode)
}
is KtProperty -> {
//TODO: process only one of them or both depending on the usage type
declaration.getter?.let { processExtensionReceiver(declaration, it) }
declaration.setter?.let { processExtensionReceiver(declaration, it) }
processExtensionReceiverUsages(declaration, declaration.getter?.bodyExpression, mode)
processExtensionReceiverUsages(declaration, declaration.setter?.bodyExpression, mode)
}
}
}
@@ -154,36 +148,6 @@ class OutflowSlicer(
processCalls(function, includeOverriders = false, CallSliceProducer)
}
private fun processExtensionReceiver(declaration: KtCallableDeclaration, declarationWithBody: KtDeclarationWithBody) {
//TODO: overriders
val resolutionFacade = declaration.getResolutionFacade()
val callableDescriptor = declaration.resolveToDescriptorIfAny(resolutionFacade) as? CallableDescriptor ?: return
val extensionReceiver = callableDescriptor.extensionReceiverParameter ?: return
val body = declarationWithBody.bodyExpression ?: return
body.forEachDescendantOfType<KtThisExpression> { thisExpression ->
val receiverDescriptor = thisExpression.resolveToCall(resolutionFacade)?.resultingDescriptor
if (receiverDescriptor == extensionReceiver) {
thisExpression.passToProcessor()
}
}
// process implicit receiver usages
val pseudocode = pseudocodeCache[body]
if (pseudocode != null) {
for (instruction in pseudocode.instructions) {
if (instruction is MagicInstruction && instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
val receiverPseudoValue = instruction.outputValue
pseudocode.getUsages(receiverPseudoValue).forEach { receiverUseInstruction ->
if (receiverUseInstruction is KtElementInstruction) {
processIfReceiverValue(receiverUseInstruction, receiverPseudoValue)
}
}
}
}
}
}
private fun processExpression(expression: KtExpression) {
val expressionWithValue = when (expression) {
is KtFunctionLiteral -> expression.parent as KtLambdaExpression
@@ -192,24 +156,24 @@ class OutflowSlicer(
expressionWithValue.processPseudocodeUsages { pseudoValue, instruction ->
when (instruction) {
is WriteValueInstruction -> {
if (!processIfReceiverValue(instruction, pseudoValue)) {
if (!processIfReceiverValue(instruction, pseudoValue, mode)) {
instruction.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor()
}
}
is ReadValueInstruction -> {
processIfReceiverValue(instruction, pseudoValue)
processIfReceiverValue(instruction, pseudoValue, mode)
}
is CallInstruction -> {
if (!processIfReceiverValue(instruction, pseudoValue)) {
if (!processIfReceiverValue(instruction, pseudoValue, mode)) {
val parameterDescriptor = instruction.arguments[pseudoValue] ?: return@processPseudocodeUsages
val parameter = parameterDescriptor.originalSource.getPsi()
if (parameter != null) {
parameter.passToProcessorInCallMode(instruction.element)
} else {
val function = parameterDescriptor.containingDeclaration as? FunctionDescriptor
if (function != null && function.isOperator && function.name == OperatorNameConventions.INVOKE) {
val function = parameterDescriptor.containingDeclaration as? FunctionDescriptor ?: return@processPseudocodeUsages
if (function.isImplicitInvokeFunction()) {
val receiverPseudoValue = instruction.receiverValues.entries.singleOrNull()?.key
?: return@processPseudocodeUsages
if (receiverPseudoValue.createdAt != null) {
@@ -256,30 +220,6 @@ class OutflowSlicer(
}
}
private fun processIfReceiverValue(instruction: KtElementInstruction, pseudoValue: PseudoValue): Boolean {
val receiverValue = (instruction as? InstructionWithReceivers)?.receiverValues?.get(pseudoValue) ?: return false
val resolvedCall = instruction.element.resolveToCall() ?: return true
when (resolvedCall.call.callType) {
Call.CallType.DEFAULT -> {
if (receiverValue == resolvedCall.extensionReceiver) {
val targetDeclaration = resolvedCall.resultingDescriptor.originalSource.getPsi()
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessorInCallMode(instruction.element)
}
}
Call.CallType.INVOKE -> {
if (receiverValue == resolvedCall.dispatchReceiver && mode.currentBehaviour is LambdaCallsBehaviour) {
instruction.element.passToProcessor()
}
}
else -> {
//TODO
}
}
return true
}
private fun processDereferenceIfNeeded(
expression: KtExpression,
pseudoValue: PseudoValue,
@@ -12,13 +12,16 @@ import com.intellij.slicer.JavaSliceUsage
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.addIfNotNull
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
@@ -33,9 +36,11 @@ import org.jetbrains.kotlin.idea.util.expectedDescriptor
import org.jetbrains.kotlin.idea.util.hasInlineModifier
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.OperatorNameConventions
import java.util.*
abstract class Slicer(
@@ -71,17 +76,21 @@ abstract class Slicer(
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
}
protected fun PsiElement.passToProcessorInCallMode(callElement: KtElement, withOverriders: Boolean = false) {
protected fun PsiElement.passToProcessorInCallMode(
callElement: KtElement,
mode: KotlinSliceAnalysisMode = this@Slicer.mode,
withOverriders: Boolean = false
) {
val newMode = when (this) {
is KtNamedFunction -> this.callMode(callElement)
is KtNamedFunction -> this.callMode(callElement, mode)
is KtParameter -> ownerFunction.callMode(callElement)
is KtParameter -> ownerFunction.callMode(callElement, mode)
is KtTypeReference -> {
val declaration = parent
require(declaration is KtCallableDeclaration)
require(this == declaration.receiverTypeReference)
declaration.callMode(callElement)
declaration.callMode(callElement, mode)
}
else -> mode
@@ -110,13 +119,6 @@ abstract class Slicer(
}
}
protected fun KtDeclaration?.callMode(callElement: KtElement): KotlinSliceAnalysisMode {
return if (this is KtNamedFunction && hasInlineModifier())
mode.withInlineFunctionCall(callElement, this)
else
mode
}
protected open fun processCalls(
callable: KtCallableDeclaration,
includeOverriders: Boolean,
@@ -227,6 +229,98 @@ abstract class Slicer(
}
protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg
protected fun processExtensionReceiverUsages(
declaration: KtCallableDeclaration,
body: KtExpression?,
mode: KotlinSliceAnalysisMode,
) {
if (body == null) return
//TODO: overriders
val resolutionFacade = declaration.getResolutionFacade()
val callableDescriptor = declaration.resolveToDescriptorIfAny(resolutionFacade) as? CallableDescriptor ?: return
val extensionReceiver = callableDescriptor.extensionReceiverParameter ?: return
body.forEachDescendantOfType<KtThisExpression> { thisExpression ->
val receiverDescriptor = thisExpression.resolveToCall(resolutionFacade)?.resultingDescriptor
if (receiverDescriptor == extensionReceiver) {
thisExpression.passToProcessor(mode)
}
}
// process implicit receiver usages
val pseudocode = pseudocodeCache[body]
if (pseudocode != null) {
for (instruction in pseudocode.instructions) {
if (instruction is MagicInstruction && instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
val receiverPseudoValue = instruction.outputValue
pseudocode.getUsages(receiverPseudoValue).forEach { receiverUseInstruction ->
if (receiverUseInstruction is KtElementInstruction) {
// TODO: make sure it processes correct receiver!!
processIfReceiverValue(receiverUseInstruction, receiverPseudoValue, mode)
}
}
}
}
}
}
protected fun processIfReceiverValue(
instruction: KtElementInstruction,
receiverPseudoValue: PseudoValue,
mode: KotlinSliceAnalysisMode,
): Boolean {
val receiverValue = (instruction as? InstructionWithReceivers)?.receiverValues?.get(receiverPseudoValue) ?: return false
val resolvedCall = instruction.element.resolveToCall() ?: return true
val descriptor = resolvedCall.resultingDescriptor
if (descriptor.isImplicitInvokeFunction()) {
when (receiverValue) {
resolvedCall.dispatchReceiver -> {
if (mode.currentBehaviour is LambdaCallsBehaviour) {
instruction.element.passToProcessor(mode)
}
}
resolvedCall.extensionReceiver -> {
val dispatchReceiver = resolvedCall.dispatchReceiver ?: return true
val dispatchReceiverPseudoValue = instruction.receiverValues.entries
.singleOrNull { it.value == dispatchReceiver }?.key
?: return true
if (dispatchReceiverPseudoValue.createdAt != null) {
when (val createdAt = dispatchReceiverPseudoValue.createdAt) {
is ReadValueInstruction -> {
val accessedDescriptor = createdAt.target.accessedDescriptor ?: return true
val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return true
when (accessedDescriptor) {
is ValueParameterDescriptor -> {
accessedDeclaration.passToProcessor(mode.withBehaviour(LambdaReceiverInflowBehaviour))
}
}
}
}
}
}
}
} else {
if (receiverValue == resolvedCall.extensionReceiver) {
val declaration = descriptor.originalSource.getPsi()
(declaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessorInCallMode(instruction.element, mode)
}
}
return true
}
companion object {
protected fun KtDeclaration?.callMode(callElement: KtElement, defaultMode: KotlinSliceAnalysisMode): KotlinSliceAnalysisMode {
return if (this is KtNamedFunction && hasInlineModifier())
defaultMode.withInlineFunctionCall(callElement, this)
else
defaultMode
}
}
}
val DeclarationDescriptorWithSource.originalSource: SourceElement
@@ -238,3 +332,9 @@ val DeclarationDescriptorWithSource.originalSource: SourceElement
return descriptor.source
}
fun CallableDescriptor.isImplicitInvokeFunction(): Boolean {
if (this !is FunctionDescriptor) return false
if (!isOperator) return false
if (name != OperatorNameConventions.INVOKE) return false
return originalSource.getPsi() == null
}
@@ -1,6 +1,26 @@
15 val <bold>x = A()</bold>
18 <bold>x</bold>[1]
6 operator fun <bold>A</bold>.get(i: Int) = this
6 operator fun A.get(i: Int) = <bold>this</bold>
6 operator fun A.<bold>get(i: Int) = this</bold>
18 <bold>x[1]</bold>
20 <bold>x[1]</bold> += y
8 operator fun <bold>A</bold>.plusAssign(a: A) {
9 val v = <bold>this</bold>
9 val <bold>v = this</bold>
21 <bold>x[1]</bold> *= y
11 operator fun <bold>A</bold>.times(a: A) = this
11 operator fun A.times(a: A) = <bold>this</bold>
11 operator fun A.<bold>times(a: A) = this</bold>
21 <bold>x[1] *= y</bold>
22 <bold>x[1]</bold>++
12 operator fun <bold>A</bold>.inc() = this
12 operator fun A.inc() = <bold>this</bold>
12 operator fun A.<bold>inc() = this</bold>
22 <bold>x[1]++</bold>
12 DUPLICATE: operator fun <bold>A</bold>.inc() = this
19 <bold>x</bold>[1] = y
20 <bold>x</bold>[1] += y
6 DUPLICATE: operator fun <bold>A</bold>.get(i: Int) = this
21 <bold>x</bold>[1] *= y
22 <bold>x</bold>[1]++
@@ -0,0 +1,9 @@
// FLOW: OUT
fun test() {
val x = foo { <caret>1 }
}
fun foo(callback: () -> Int): Int {
return callback.invoke()
}
@@ -0,0 +1,9 @@
4 val x = foo { <bold>1</bold> }
4 val x = foo <bold>{ 1 }</bold>
4 [LAMBDA CALLS] val x = foo <bold>{ 1 }</bold>
7 [LAMBDA CALLS] fun foo(<bold>callback: () -> Int</bold>): Int {
8 [LAMBDA CALLS] return <bold>callback</bold>.invoke()
8 return callback.<bold>invoke()</bold>
7 fun <bold>foo(callback: () -> Int): Int {</bold>
4 val x = <bold>foo { 1 }</bold>
4 val <bold>x = foo { 1 }</bold>
+15
View File
@@ -0,0 +1,15 @@
// FLOW: OUT
fun String.foo(<caret>p: String) {
val v1 = with(p) { this }
val v2 = with(p) { bar(this) }
val v3 = with(p) { this@foo }
}
fun bar(s: String) = s
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
+34
View File
@@ -0,0 +1,34 @@
3 fun String.foo(<bold>p: String</bold>) {
4 val v1 = with(<bold>p</bold>) { this }
13 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
14 (INLINE CALL with) return <bold>receiver</bold>.block()
13 (INLINE CALL with) [LAMBDA RECEIVER IN] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
4 [LAMBDA RECEIVER IN] val v1 = with(p) <bold>{ this }</bold>
4 val v1 = with(p) { <bold>this</bold> }
4 val v1 = with(p) <bold>{ this }</bold>
4 [LAMBDA CALLS] val v1 = with(p) <bold>{ this }</bold>
13 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
14 (INLINE CALL with) return receiver.<bold>block()</bold>
4 val v1 = <bold>with(p) { this }</bold>
4 val <bold>v1 = with(p) { this }</bold>
6 val v2 = with(<bold>p</bold>) { bar(this) }
13 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
14 (INLINE CALL with) return <bold>receiver</bold>.block()
13 (INLINE CALL with) [LAMBDA RECEIVER IN] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
6 [LAMBDA RECEIVER IN] val v2 = with(p) <bold>{ bar(this) }</bold>
6 val v2 = with(p) { bar(<bold>this</bold>) }
11 fun bar(<bold>s: String</bold>) = s
11 fun bar(s: String) = <bold>s</bold>
11 fun <bold>bar(s: String) = s</bold>
6 val v2 = with(p) { <bold>bar(this)</bold> }
6 val v2 = with(p) <bold>{ bar(this) }</bold>
6 [LAMBDA CALLS] val v2 = with(p) <bold>{ bar(this) }</bold>
13 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
14 (INLINE CALL with) return receiver.<bold>block()</bold>
6 val v2 = <bold>with(p) { bar(this) }</bold>
6 val <bold>v2 = with(p) { bar(this) }</bold>
8 val v3 = with(<bold>p</bold>) { this@foo }
13 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
14 (INLINE CALL with) return <bold>receiver</bold>.block()
13 (INLINE CALL with) [LAMBDA RECEIVER IN] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
8 [LAMBDA RECEIVER IN] val v3 = with(p) <bold>{ this@foo }</bold>
@@ -623,6 +623,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt");
}
@TestMetadata("lambdaResultWithInvokeCall.kt")
public void testLambdaResultWithInvokeCall() throws Exception {
runTest("idea/testData/slicer/outflow/lambdaResultWithInvokeCall.kt");
}
@TestMetadata("letResult.kt")
public void testLetResult() throws Exception {
runTest("idea/testData/slicer/outflow/letResult.kt");
@@ -753,6 +758,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/outflow/whenExpression.kt");
}
@TestMetadata("withResult.kt")
public void testWithResult() throws Exception {
runTest("idea/testData/slicer/outflow/withResult.kt");
}
@TestMetadata("wrongThis.kt")
public void testWrongThis() throws Exception {
runTest("idea/testData/slicer/outflow/wrongThis.kt");