From bcdb46653c499dcb2b435bd4ba827a56d5878a71 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 17 Nov 2017 20:12:30 +0300 Subject: [PATCH] Line Markers: Detect recursive calls of companion `invoke()` member #KT-21076 Fixed --- .../kotlin/idea/util/extensionsUtils.kt | 19 +++++++++++++++++++ .../KotlinRecursiveCallLineMarkerProvider.kt | 10 ++++++---- .../recursiveCall/companionInvoke.kt | 5 +++++ .../codeInsight/LineMarkersTestGenerated.java | 6 ++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 idea/testData/codeInsight/lineMarker/recursiveCall/companionInvoke.kt diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt index 681ce03b937..41a4c32021d 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt @@ -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 + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt index 27a55db7eaf..231be8d8fc5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt @@ -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 } } diff --git a/idea/testData/codeInsight/lineMarker/recursiveCall/companionInvoke.kt b/idea/testData/codeInsight/lineMarker/recursiveCall/companionInvoke.kt new file mode 100644 index 00000000000..eced2f29f8a --- /dev/null +++ b/idea/testData/codeInsight/lineMarker/recursiveCall/companionInvoke.kt @@ -0,0 +1,5 @@ +class Example(val dummy: Any?) { + companion object { + operator fun invoke() = Example() + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java index bad67c21789..b9c4e9d4014 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java @@ -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");