From 5212728a82c1ecb5a20d2adb982c9e43ed858c1e Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 17 Jul 2018 10:56:02 +0300 Subject: [PATCH] Uast: implicit receiver for function variables calls (KT-25524) --- .../KotlinUFunctionCallExpression.kt | 26 ++++++++++++++++- .../KotlinUFunctionCallExpression.kt.173 | 24 +++++++++++++++- plugins/uast-kotlin/testData/Lambdas.kt | 4 +++ plugins/uast-kotlin/testData/Lambdas.log.txt | 28 +++++++++++++++++++ .../uast-kotlin/testData/Lambdas.render.txt | 12 ++++++++ .../uast-kotlin/tests/KotlinUastApiTest.kt | 23 +++++++++++++++ .../tests/KotlinUastApiTest.kt.172 | 25 ++++++++++++++++- .../tests/KotlinUastApiTest.kt.173 | 23 +++++++++++++++ .../tests/SimpleKotlinRenderLogTest.kt | 3 ++ .../tests/SimpleKotlinRenderLogTest.kt.172 | 3 ++ 10 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 plugins/uast-kotlin/testData/Lambdas.log.txt create mode 100644 plugins/uast-kotlin/testData/Lambdas.render.txt diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt index 68c19f28b66..e48f2d6dede 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt @@ -16,7 +16,9 @@ package org.jetbrains.uast.kotlin +import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiNamedElement import com.intellij.psi.PsiType import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor @@ -25,6 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.uast.* import org.jetbrains.uast.internal.acceptList import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier @@ -101,7 +104,28 @@ class KotlinUFunctionCallExpression( } override val receiver: UExpression? - get() = (uastParent as? UQualifiedReferenceExpression)?.takeIf { it.selector == this }?.receiver + get() { + (uastParent as? UQualifiedReferenceExpression)?.let { + if (it.selector == this) return it.receiver + } + + val ktNameReferenceExpression = psi.calleeExpression as? KtNameReferenceExpression ?: return null + val variableAsFunctionResolvedCall = resolvedCall as? VariableAsFunctionResolvedCall ?: return null + + // an implicit receiver for variables calls (KT-25524) + return object : KotlinAbstractUExpression(this), UReferenceExpression { + + private val resolvedDeclaration = variableAsFunctionResolvedCall.variableCall.resultingDescriptor.toSource() + + override val psi: KtNameReferenceExpression get() = ktNameReferenceExpression + + override val resolvedName: String? get() = (resolvedDeclaration as? PsiNamedElement)?.name + + override fun resolve(): PsiElement? = resolvedDeclaration + + } + + } override fun resolve(): PsiMethod? { val descriptor = resolvedCall?.resultingDescriptor ?: return null diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 index 04a5f9b3192..6f17e3040bc 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.uast.* import org.jetbrains.uast.internal.acceptList import org.jetbrains.uast.visitor.UastVisitor @@ -87,7 +88,28 @@ class KotlinUFunctionCallExpression( } override val receiver: UExpression? - get() = (uastParent as? UQualifiedReferenceExpression)?.takeIf { it.selector == this }?.receiver + get() { + (uastParent as? UQualifiedReferenceExpression)?.let { + if (it.selector == this) return it.receiver + } + + val ktNameReferenceExpression = psi.calleeExpression as? KtNameReferenceExpression ?: return null + val variableAsFunctionResolvedCall = resolvedCall as? VariableAsFunctionResolvedCall ?: return null + + // an implicit receiver for variables calls (KT-25524) + return object : KotlinAbstractUExpression(this), UReferenceExpression { + + private val resolvedDeclaration = variableAsFunctionResolvedCall.variableCall.resultingDescriptor.toSource() + + override val psi: KtNameReferenceExpression get() = ktNameReferenceExpression + + override val resolvedName: String? get() = (resolvedDeclaration as? PsiNamedElement)?.name + + override fun resolve(): PsiElement? = resolvedDeclaration + + } + + } override fun resolve(): PsiMethod? { val descriptor = resolvedCall?.resultingDescriptor ?: return null diff --git a/plugins/uast-kotlin/testData/Lambdas.kt b/plugins/uast-kotlin/testData/Lambdas.kt index 59905883327..b4425aa7611 100644 --- a/plugins/uast-kotlin/testData/Lambdas.kt +++ b/plugins/uast-kotlin/testData/Lambdas.kt @@ -2,4 +2,8 @@ import java.util.stream.Stream fun foo() { Stream.empty().filter { it.isEmpty() } +} + +fun doSelectItem(selectItemFunction: () -> Unit) { + selectItemFunction() } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/Lambdas.log.txt b/plugins/uast-kotlin/testData/Lambdas.log.txt new file mode 100644 index 00000000000..19ba4ec1ada --- /dev/null +++ b/plugins/uast-kotlin/testData/Lambdas.log.txt @@ -0,0 +1,28 @@ +UFile (package = ) + UImportStatement (isOnDemand = false) + UClass (name = LambdasKt) + UAnnotationMethod (name = foo) + UBlockExpression + UQualifiedReferenceExpression + UQualifiedReferenceExpression + USimpleNameReferenceExpression (identifier = Stream) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (empty)) + USimpleNameReferenceExpression (identifier = empty) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) + UIdentifier (Identifier (filter)) + USimpleNameReferenceExpression (identifier = filter) + ULambdaExpression + UBlockExpression + UQualifiedReferenceExpression + USimpleNameReferenceExpression (identifier = it) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (isEmpty)) + USimpleNameReferenceExpression (identifier = isEmpty) + UAnnotationMethod (name = doSelectItem) + UParameter (name = selectItemFunction) + UAnnotation (fqName = org.jetbrains.annotations.NotNull) + UBlockExpression + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (selectItemFunction)) + USimpleNameReferenceExpression (identifier = invoke) diff --git a/plugins/uast-kotlin/testData/Lambdas.render.txt b/plugins/uast-kotlin/testData/Lambdas.render.txt new file mode 100644 index 00000000000..47ac24d34a4 --- /dev/null +++ b/plugins/uast-kotlin/testData/Lambdas.render.txt @@ -0,0 +1,12 @@ +import java.util.stream.Stream + +public final class LambdasKt { + public static final fun foo() : void { + Stream.empty().filter({ + it.isEmpty() + }) + } + public static final fun doSelectItem(@org.jetbrains.annotations.NotNull selectItemFunction: kotlin.jvm.functions.Function0) : void { + invoke() + } +} diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt index 285d9b9ea9e..3762ea6048c 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt @@ -384,6 +384,29 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } + @Test + fun testLambdaParamCall() { + doTest("Lambdas") { _, file -> + val lambdaCall = file.findElementByTextFromPsi("selectItemFunction()") + assertEquals( + "UIdentifier (Identifier (selectItemFunction))", + lambdaCall.methodIdentifier?.asLogString() + ) + assertEquals( + "selectItemFunction", + lambdaCall.methodIdentifier?.name + ) + assertEquals( + "invoke", + lambdaCall.methodName + ) + val receiver = lambdaCall.receiver ?: kotlin.test.fail("receiver expected") + assertEquals("UReferenceExpression", receiver.asLogString()) + val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected") + assertEquals("UParameter (name = selectItemFunction)", uParameter.asLogString()) + } + } + } fun Iterable.assertedFind(value: R, transform: (T) -> R): T = diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 index 12815fb5230..0211d97e4dc 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 @@ -227,6 +227,29 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { val uCallExpression = lambda.uastParent.assertedCast { "UCallExpression expected" } assertTrue(uCallExpression.valueArguments.contains(lambda)) } - } + + @Test + fun testLambdaParamCall() { + doTest("Lambdas") { _, file -> + val lambdaCall = file.findElementByTextFromPsi("selectItemFunction()") + assertEquals( + "UIdentifier (Identifier (selectItemFunction))", + lambdaCall.methodIdentifier?.asLogString() + ) + assertEquals( + "selectItemFunction", + lambdaCall.methodIdentifier?.name + ) + assertEquals( + "invoke", + lambdaCall.methodName + ) + val receiver = lambdaCall.receiver ?: kotlin.test.fail("receiver expected") + assertEquals("UReferenceExpression", receiver.asLogString()) + val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected") + assertEquals("UParameter (name = selectItemFunction)", uParameter.asLogString()) + } + } + } diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 index 19857fea93a..2978b0b2329 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 @@ -348,6 +348,29 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } + @Test + fun testLambdaParamCall() { + doTest("Lambdas") { _, file -> + val lambdaCall = file.findElementByTextFromPsi("selectItemFunction()") + assertEquals( + "UIdentifier (Identifier (selectItemFunction))", + lambdaCall.methodIdentifier?.asLogString() + ) + assertEquals( + "selectItemFunction", + lambdaCall.methodIdentifier?.name + ) + assertEquals( + "invoke", + lambdaCall.methodName + ) + val receiver = lambdaCall.receiver ?: kotlin.test.fail("receiver expected") + assertEquals("UReferenceExpression", receiver.asLogString()) + val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected") + assertEquals("UParameter (name = selectItemFunction)", uParameter.asLogString()) + } + } + } fun Iterable.assertedFind(value: R, transform: (T) -> R): T = find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}") diff --git a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt index 66812b55f10..a3b4e418715 100644 --- a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt +++ b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt @@ -81,4 +81,7 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() { // disabled due to inconsistent parents for 2-receivers call (KT-22344) check(testName, file, false) } + + @Test + fun testLambdas() = doTest("Lambdas") } diff --git a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 index 603b78ea1af..9841846a29e 100644 --- a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 +++ b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 @@ -59,4 +59,7 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() { @Test fun testConstructors() = doTest("Constructors") + + @Test + fun testLambdas() = doTest("Lambdas") }