Line Markers: Detect recursive calls of companion invoke() member

#KT-21076 Fixed
This commit is contained in:
Alexey Sedunov
2017-11-17 20:12:30 +03:00
parent 169a22226f
commit bcdb46653c
4 changed files with 36 additions and 4 deletions
@@ -21,6 +21,7 @@ package org.jetbrains.kotlin.idea.util
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
@@ -113,3 +114,21 @@ fun ReceiverValue?.getThisReceiverOwner(bindingContext: BindingContext): Declara
else -> null
}
}
fun ReceiverValue?.getReceiverTargetDescriptor(bindingContext: BindingContext): DeclarationDescriptor? {
return when (this) {
is ExpressionReceiver -> {
val expression = KtPsiUtil.deparenthesize(this.expression)
val refExpression = when (expression) {
is KtThisExpression -> expression.instanceReference
is KtReferenceExpression -> expression
else -> null
} ?: return null
bindingContext[BindingContext.REFERENCE_TARGET, refExpression]
}
is ImplicitReceiver -> this.declarationDescriptor
else -> null
}
}
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.RecursivePropertyAccessorInspection
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
import org.jetbrains.kotlin.idea.util.getReceiverTargetDescriptor
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
@@ -82,7 +82,9 @@ class KotlinRecursiveCallLineMarkerProvider : LineMarkerProvider {
val resolveName = getCallNameFromPsi(element) ?: return false
val enclosingFunction = getEnclosingFunction(element, false) ?: return false
if (enclosingFunction.name != resolveName.asString()) return false
val enclosingFunctionName = enclosingFunction.name
if (enclosingFunctionName != OperatorNameConventions.INVOKE.asString()
&& enclosingFunctionName != resolveName.asString()) return false
// Check that there were no not-inlined lambdas on the way to enclosing function
if (enclosingFunction != getEnclosingFunction(element, true)) return false
@@ -98,12 +100,12 @@ class KotlinRecursiveCallLineMarkerProvider : LineMarkerProvider {
fun isDifferentReceiver(receiver: Receiver?): Boolean {
if (receiver !is ReceiverValue) return false
val receiverOwner = receiver.getThisReceiverOwner(bindingContext) ?: return true
val receiverOwner = receiver.getReceiverTargetDescriptor(bindingContext) ?: return true
return when (receiverOwner) {
is SimpleFunctionDescriptor -> receiverOwner != enclosingFunctionDescriptor
is ClassDescriptor -> receiverOwner != enclosingFunctionDescriptor.containingDeclaration
else -> throw IllegalStateException("Unexpected receiver owner: $receiverOwner")
else -> return true
}
}
@@ -0,0 +1,5 @@
class Example(val dummy: Any?) {
companion object {
operator fun invoke() = <lineMarker>Example</lineMarker>()
}
}
@@ -209,6 +209,12 @@ public class LineMarkersTestGenerated extends AbstractLineMarkersTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/lineMarker/recursiveCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("companionInvoke.kt")
public void testCompanionInvoke() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/lineMarker/recursiveCall/companionInvoke.kt");
doTest(fileName);
}
@TestMetadata("conventionCall.kt")
public void testConventionCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/lineMarker/recursiveCall/conventionCall.kt");