From 9559346199a976c25f9e027803db753e2a7138f5 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 15 Jan 2019 18:51:50 +0300 Subject: [PATCH] 191: Uast: implementation of `UMethod.returnTypeReference` (KT-29012) --- .../kotlin/declarations/KotlinUMethod.kt.191 | 144 ++++++ .../tests/KotlinUastApiTest.kt.191 | 478 ++++++++++++++++++ 2 files changed, 622 insertions(+) create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.191 create mode 100644 plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.191 new file mode 100644 index 00000000000..825b11e7adf --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.191 @@ -0,0 +1,144 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.kotlin.declarations + +import com.intellij.psi.* +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.elements.isGetter +import org.jetbrains.kotlin.asJava.elements.isSetter +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.uast.* +import org.jetbrains.uast.java.internal.JavaUElementWithComments +import org.jetbrains.uast.kotlin.* + +open class KotlinUMethod( + psi: KtLightMethod, + givenParent: UElement? +) : KotlinAbstractUElement(givenParent), UAnnotationMethod, UMethodTypeSpecific, UAnchorOwner, JavaUElementWithComments, PsiMethod by psi { + override val comments: List + get() = super.comments + + override val psi: KtLightMethod = unwrap(psi) + + override val javaPsi = psi + + override val sourcePsi = psi.kotlinOrigin + + override fun getSourceElement() = sourcePsi ?: this + + override val uastDefaultValue by lz { + val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null + val defaultValue = annotationParameter.defaultValue ?: return@lz null + getLanguagePlugin().convertElement(defaultValue, this) as? UExpression + } + + private val kotlinOrigin = (psi.originalElement as KtLightElement<*, *>).kotlinOrigin + + override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile) + + override fun getNameIdentifier() = UastLightIdentifier(psi, kotlinOrigin as KtNamedDeclaration?) + + override val annotations by lz { + psi.annotations + .mapNotNull { (it as? KtLightElement<*, *>)?.kotlinOrigin as? KtAnnotationEntry } + .map { KotlinUAnnotation(it, this) } + } + + private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference } + + override val uastParameters by lz { + val lightParams = psi.parameterList.parameters + val receiver = receiver ?: return@lz lightParams.map { + KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) + } + val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList() + val uParameters = SmartList(KotlinReceiverUParameter(receiverLight, receiver, this)) + lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) } + uParameters + } + + override val uastAnchor by lazy { + KotlinUIdentifier( + nameIdentifier, + sourcePsi.let { sourcePsi -> + when (sourcePsi) { + is PsiNameIdentifierOwner -> sourcePsi.nameIdentifier + is KtObjectDeclaration -> sourcePsi.getObjectKeyword() + else -> sourcePsi?.navigationElement + } + }, + this + ) + } + + + override val uastBody by lz { + val bodyExpression = when (kotlinOrigin) { + is KtFunction -> kotlinOrigin.bodyExpression + is KtProperty -> when { + psi.isGetter -> kotlinOrigin.getter?.bodyExpression + psi.isSetter -> kotlinOrigin.setter?.bodyExpression + else -> null + } + else -> null + } ?: return@lz null + + when (bodyExpression) { + !is KtBlockExpression -> { + KotlinUBlockExpression.KotlinLazyUBlockExpression(this, { block -> + val implicitReturn = KotlinUImplicitReturnExpression(block) + val uBody = getLanguagePlugin().convertElement(bodyExpression, implicitReturn) as? UExpression + ?: return@KotlinLazyUBlockExpression emptyList() + listOf(implicitReturn.apply { returnExpression = uBody }) + }) + + } + else -> getLanguagePlugin().convertElement(bodyExpression, this) as? UExpression + } + } + + override val isOverride: Boolean + get() = (kotlinOrigin as? KtCallableDeclaration)?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false + + override fun getBody(): PsiCodeBlock? = super.getBody() + + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() + + override val returnTypeReference: UTypeReferenceExpression? by lz { + (sourcePsi as? KtCallableDeclaration)?.typeReference?.let { + LazyKotlinUTypeReferenceExpression(it, this) { javaPsi.returnType ?: UastErrorType } + } + } + + override fun equals(other: Any?) = other is KotlinUMethod && psi == other.psi + + companion object { + fun create(psi: KtLightMethod, containingElement: UElement?) = + if (psi.kotlinOrigin is KtConstructor<*>) { + KotlinConstructorUMethod( + psi.kotlinOrigin?.containingClassOrObject, + psi, containingElement + ) + } + else + KotlinUMethod(psi, containingElement) + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 new file mode 100644 index 00000000000..798d453fdd5 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 @@ -0,0 +1,478 @@ +package org.jetbrains.uast.test.kotlin + +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiModifier +import com.intellij.testFramework.UsefulTestCase +import org.jetbrains.kotlin.asJava.toLightAnnotation +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase +import org.jetbrains.kotlin.utils.addToStdlib.assertedCast +import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.utils.sure +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin +import org.jetbrains.uast.test.env.kotlin.findElementByText +import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi +import org.jetbrains.uast.visitor.AbstractUastVisitor +import org.junit.Assert +import org.junit.Test + + +class KotlinUastApiTest : AbstractKotlinUastTest() { + override fun check(testName: String, file: UFile) { + } + + @Test fun testAnnotationParameters() { + doTest("AnnotationParameters") { _, file -> + val annotation = file.findElementByText("@IntRange(from = 10, to = 0)") + assertEquals(annotation.findAttributeValue("from")?.evaluate(), 10) + val toAttribute = annotation.findAttributeValue("to")!! + assertEquals(toAttribute.evaluate(), 0) + KtUsefulTestCase.assertInstanceOf(annotation.psi.toUElement(), UAnnotation::class.java) + KtUsefulTestCase.assertInstanceOf( + annotation.psi.cast().toLightAnnotation().toUElement(), + UAnnotation::class.java + ) + KtUsefulTestCase.assertInstanceOf(toAttribute.uastParent, UNamedExpression::class.java) + KtUsefulTestCase.assertInstanceOf(toAttribute.psi.toUElement()?.uastParent, UNamedExpression::class.java) + } + } + + @Test fun testConvertStringTemplate() { + doTest("StringTemplateInClass") { _, file -> + val literalExpression = file.findElementByText("lorem") + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + val literalExpressionAgain = psi.toUElement() + Assert.assertTrue(literalExpressionAgain is ULiteralExpression) + + } + } + + @Test fun testConvertStringTemplateWithExpectedType() { + doTest("StringTemplateWithVar") { _, file -> + val index = file.psi.text.indexOf("foo") + val stringTemplate = file.psi.findElementAt(index)!!.getParentOfType(false) + val uLiteral = stringTemplate.toUElementOfType() + assertNull(uLiteral) + } + } + + @Test fun testNameContainingFile() { + doTest("NameContainingFile") { _, file -> + val foo = file.findElementByText("class Foo") + assertEquals(file.psi, foo.nameIdentifier!!.containingFile) + + val bar = file.findElementByText("fun bar() {}") + assertEquals(file.psi, bar.nameIdentifier!!.containingFile) + + val xyzzy = file.findElementByText("val xyzzy: Int = 0") + assertEquals(file.psi, xyzzy.nameIdentifier!!.containingFile) + } + } + + @Test fun testInterfaceMethodWithBody() { + doTest("DefaultImpls") { _, file -> + val bar = file.findElementByText("fun bar() = \"Hello!\"") + assertFalse(bar.containingFile.text!!, bar.psi.modifierList.hasExplicitModifier(PsiModifier.DEFAULT)) + assertTrue(bar.containingFile.text!!, bar.psi.modifierList.hasModifierProperty(PsiModifier.DEFAULT)) + } + } + + @Test fun testSAM() { + doTest("SAM") { _, file -> + assertNull(file.findElementByText("{ /* Not SAM */ }").functionalInterfaceType) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Variable */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Assignment */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Type Cast */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Argument */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Return */}").functionalInterfaceType?.canonicalText) + + assertEquals( + "java.lang.Runnable", + file.findElementByText("{ /* SAM */ }").functionalInterfaceType?.canonicalText + ) + } + } + + @Test fun testParameterPropertyWithAnnotation() { + doTest("ParameterPropertyWithAnnotation") { _, file -> + val test1 = file.classes.find { it.name == "Test1" }!! + + val constructor1 = test1.methods.find { it.name == "Test1" }!! + assertTrue(constructor1.uastParameters.first().annotations.any { it.qualifiedName == "MyAnnotation" }) + + val getter1 = test1.methods.find { it.name == "getBar" }!! + assertFalse(getter1.annotations.any { it.qualifiedName == "MyAnnotation" }) + + val setter1 = test1.methods.find { it.name == "setBar" }!! + assertFalse(setter1.annotations.any { it.qualifiedName == "MyAnnotation" }) + assertFalse(setter1.uastParameters.first().annotations.any { it.qualifiedName == "MyAnnotation" }) + + + val test2 = file.classes.find { it.name == "Test2" }!! + val constructor2 = test2.methods.find { it.name == "Test2" }!! + assertFalse(constructor2.uastParameters.first().annotations.any { it.qualifiedName?.startsWith("MyAnnotation") ?: false }) + + val getter2 = test2.methods.find { it.name == "getBar" }!! + getter2.annotations.single { it.qualifiedName == "MyAnnotation" } + + val setter2 = test2.methods.find { it.name == "setBar" }!! + setter2.annotations.single { it.qualifiedName == "MyAnnotation2" } + setter2.uastParameters.first().annotations.single { it.qualifiedName == "MyAnnotation3" } + + test2.fields.find { it.name == "bar" }!!.annotations.single { it.qualifiedName == "MyAnnotation5" } + } + } + + @Test fun testConvertTypeInAnnotation() { + doTest("TypeInAnnotation") { _, file -> + val index = file.psi.text.indexOf("Test") + val element = file.psi.findElementAt(index)!!.getParentOfType(false)!! + assertNotNull(element.getUastParentOfType(UAnnotation::class.java)) + } + } + + @Test fun testElvisType() { + doTest("ElvisType") { _, file -> + val elvisExpression = file.findElementByText("text ?: return") + assertEquals("String", elvisExpression.getExpressionType()!!.presentableText) + } + } + + @Test fun testFindAttributeDefaultValue() { + doTest("AnnotationParameters") { _, file -> + val witDefaultValue = file.findElementByText("@WithDefaultValue") + assertEquals(42, witDefaultValue.findAttributeValue("value")!!.evaluate()) + assertEquals(42, witDefaultValue.findAttributeValue(null)!!.evaluate()) + } + } + + @Test fun testIfCondition() { + doTest("IfStatement") { _, file -> + val psiFile = file.psi + val element = psiFile.findElementAt(psiFile.text.indexOf("\"abc\""))!! + val binaryExpression = element.getParentOfType(false)!! + val uBinaryExpression = KotlinUastLanguagePlugin().convertElementWithParent(binaryExpression, null)!! + UsefulTestCase.assertInstanceOf(uBinaryExpression.uastParent, UIfExpression::class.java) + } + } + + @Test + fun testWhenStringLiteral() { + doTest("WhenStringLiteral") { _, file -> + + file.findElementByTextFromPsi("abc").let { literalExpression -> + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java) + } + + file.findElementByTextFromPsi("def").let { literalExpression -> + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java) + } + + file.findElementByTextFromPsi("def1").let { literalExpression -> + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + UsefulTestCase.assertInstanceOf(literalExpression.uastParent, UBlockExpression::class.java) + } + + + } + } + + @Test + fun testWhenAndDestructing() { + doTest("WhenAndDestructing") { _, file -> + + file.findElementByTextFromPsi("val (bindingContext, statementFilter) = arr").let { e -> + val uBlockExpression = e.getParentOfType() + Assert.assertNotNull(uBlockExpression) + val uMethod = uBlockExpression!!.getParentOfType() + Assert.assertNotNull(uMethod) + } + + } + } + + @Test + fun testBrokenMethodTypeResolve() { + doTest("BrokenMethod") { _, file -> + + file.accept(object : AbstractUastVisitor() { + override fun visitCallExpression(node: UCallExpression): Boolean { + node.returnType + return false + } + }) + } + } + + @Test + fun testSimpleAnnotated() { + doTest("SimpleAnnotated") { _, file -> + file.findElementByTextFromPsi("@kotlin.SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field -> + val annotation = field.annotations.assertedFind("kotlin.SinceKotlin") { it.qualifiedName } + Assert.assertEquals("1.0", annotation.findDeclaredAttributeValue("version")?.evaluateString()) + Assert.assertEquals("SinceKotlin", annotation.cast().uastAnchor?.sourcePsi?.text) + } + } + } + + + fun UFile.checkUastSuperTypes(refText: String, superTypes: List) { + findElementByTextFromPsi(refText, false).let { + assertEquals("base classes", superTypes, it.uastSuperTypes.map { it.getQualifiedName() }) + } + } + + + @Test + fun testSuperTypes() { + doTest("SuperCalls") { _, file -> + file.checkUastSuperTypes("B", listOf("A")) + file.checkUastSuperTypes("O", listOf("A")) + file.checkUastSuperTypes("innerObject ", listOf("A")) + file.checkUastSuperTypes("InnerClass", listOf("A")) + file.checkUastSuperTypes("object : A(\"textForAnon\")", listOf("A")) + } + } + + @Test + fun testAnonymousSuperTypes() { + doTest("Anonymous") { _, file -> + file.checkUastSuperTypes("object : Runnable { override fun run() {} }", listOf("java.lang.Runnable")) + file.checkUastSuperTypes( + "object : Runnable, Closeable { override fun close() {} override fun run() {} }", + listOf("java.lang.Runnable", "java.io.Closeable") + ) + file.checkUastSuperTypes( + "object : InputStream(), Runnable { override fun read(): Int = 0; override fun run() {} }", + listOf("java.io.InputStream", "java.lang.Runnable") + ) + } + } + + @Test + fun testLiteralArraysTypes() { + doTest("AnnotationParameters") { _, file -> + file.findElementByTextFromPsi("intArrayOf(1, 2, 3)").let { field -> + Assert.assertEquals("PsiType:int[]", field.returnType.toString()) + } + file.findElementByTextFromPsi("[1, 2, 3]").let { field -> + Assert.assertEquals("PsiType:int[]", field.returnType.toString()) + Assert.assertEquals("PsiType:int", field.typeArguments.single().toString()) + } + file.findElementByTextFromPsi("[\"a\", \"b\", \"c\"]").let { field -> + Assert.assertEquals("PsiType:String[]", field.returnType.toString()) + Assert.assertEquals("PsiType:String", field.typeArguments.single().toString()) + } + + } + } + + @Test + fun testTypeAliases() { + doTest("TypeAliases") { _, file -> + val g = (file.psi as KtFile).declarations.single { it.name == "G" } as KtTypeAlias + val originalType = g.getTypeReference()!!.typeElement as KtFunctionType + val originalTypeParameters = originalType.parameterList.toUElement() as UDeclarationsExpression + Assert.assertTrue((originalTypeParameters.declarations.single() as UParameter).type.isValid) + } + } + + @Test + fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> + file.findElementByTextFromPsi("@AnnotationArray(value = Annotation())") + .findElementByTextFromPsi("Annotation()") + .sourcePsiElement + .let { referenceExpression -> + val convertedUAnnotation = referenceExpression + .cast() + .toUElementOfType() + ?: throw AssertionError("haven't got annotation from $referenceExpression(${referenceExpression?.javaClass})") + + checkDescriptorsLeak(convertedUAnnotation) + assertEquals("Annotation", convertedUAnnotation.qualifiedName) + val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java) + ?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation") + assertEquals("Annotation", lightAnnotation.qualifiedName) + assertEquals("Annotation", (convertedUAnnotation as UAnchorOwner).uastAnchor?.sourcePsi?.text) + } + } + + + @Test + fun testParametersDisorder() = doTest("ParametersDisorder") { _, file -> + + fun assertArguments(argumentsInPositionalOrder: List?, refText: String) = + file.findElementByTextFromPsi(refText).let { call -> + if (call !is UCallExpressionEx) throw AssertionError("${call.javaClass} is not a UCallExpressionEx") + Assert.assertEquals( + argumentsInPositionalOrder, + call.resolve()?.let { psiMethod -> + (0 until psiMethod.parameterList.parametersCount).map { + call.getArgumentForParameter(it)?.asRenderString() + } + } + ) + } + + + assertArguments(listOf("2", "2.2"), "global(b = 2.2F, a = 2)") + assertArguments(listOf(null, "\"bbb\""), "withDefault(d = \"bbb\")") + assertArguments(listOf("1.3", "3.4"), "atan2(1.3, 3.4)") + assertArguments(null, "unresolvedMethod(\"param1\", \"param2\")") + assertArguments(listOf("\"%i %i %i\"", "varargs 1 : 2 : 3"), "format(\"%i %i %i\", 1, 2, 3)") + assertArguments(listOf("\"%i %i %i\"", "varargs arrayOf(1, 2, 3)"), "format(\"%i %i %i\", arrayOf(1, 2, 3))") + assertArguments( + listOf("\"%i %i %i\"", "varargs arrayOf(1, 2, 3) : arrayOf(4, 5, 6)"), + "format(\"%i %i %i\", arrayOf(1, 2, 3), arrayOf(4, 5, 6))" + ) + assertArguments(listOf("\"%i %i %i\"", "\"\".chunked(2).toTypedArray()"), "format(\"%i %i %i\", *\"\".chunked(2).toTypedArray())") + assertArguments(listOf("\"def\"", "8", "7.0"), "with2Receivers(8, 7.0F)") + assertArguments(listOf("\"foo\"", "1"), "object : Parent(b = 1, a = \"foo\")\n") + } + + @Test + fun testResolvedDeserializedMethod() = doTest("Resolve") { _, file -> + val barMethod = file.findElementByTextFromPsi("bar").getParentOfType()!! + + fun UElement.assertResolveCall(callText: String, methodName: String = callText.substringBefore("(")) { + this.findElementByTextFromPsi(callText).let { + val resolve = it.resolve().sure { "resolving '$callText'" } + assertEquals(methodName, resolve.name) + } + } + barMethod.assertResolveCall("foo()") + barMethod.assertResolveCall("inlineFoo()") + barMethod.assertResolveCall("forEach { println(it) }", "forEach") + barMethod.assertResolveCall("joinToString()") + barMethod.assertResolveCall("last()") + barMethod.assertResolveCall("setValue(\"123\")") + barMethod.assertResolveCall("contains(2 as Int)", "longRangeContains") + barMethod.assertResolveCall("IntRange(1, 2)") + } + + @Test + fun testUtilsStreamLambda() { + doTest("Lambdas") { _, file -> + val lambda = file.findElementByTextFromPsi("{ it.isEmpty() }") + assertEquals( + "java.util.function.Predicate", + lambda.functionalInterfaceType?.canonicalText + ) + assertEquals( + "kotlin.jvm.functions.Function1", + lambda.getExpressionType()?.canonicalText + ) + 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()) + } + } + + @Test + fun testLocalLambdaCall() { + doTest("Lambdas") { _, file -> + val lambdaCall = file.findElementByTextFromPsi("baz()") + assertEquals( + "UIdentifier (Identifier (baz))", + lambdaCall.methodIdentifier?.asLogString() + ) + assertEquals( + "baz", + 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("ULocalVariable (name = baz)", uParameter.asLogString()) + } + } + + @Test + fun testLocalDeclarationCall() { + doTest("LocalDeclarations") { _, file -> + val localFunction = file.findElementByTextFromPsi("bar() == Local()"). + findElementByText("bar()") + assertEquals( + "UIdentifier (Identifier (bar))", + localFunction.methodIdentifier?.asLogString() + ) + assertEquals( + "bar", + localFunction.methodIdentifier?.name + ) + assertEquals( + "bar", + localFunction.methodName + ) + assertNull(localFunction.resolve()) + val receiver = localFunction.receiver ?: kotlin.test.fail("receiver expected") + assertEquals("UReferenceExpression", receiver.asLogString()) + val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected") + assertEquals("ULambdaExpression", uParameter.asLogString()) + } + } + + @Test + fun testMethodReturnTypeReference() { + doTest("Elvis") { _, file -> + assertEquals( + "UTypeReferenceExpression (name = java.lang.String)", + file.findElementByTextFromPsi("fun foo(bar: String): String? = null").returnTypeReference?.asLogString() + ) + assertEquals( + null, + file.findElementByTextFromPsi("fun bar() = 42").returnTypeReference?.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() }}")