From 6a659f33b5a9342137a8a539afb8e8d8fe281da4 Mon Sep 17 00:00:00 2001 From: nickl Date: Fri, 20 Oct 2017 16:48:09 +0300 Subject: [PATCH] UastKotlinPsiVariable `psiParent` made lazy to eliminate recursion when building UAST parents --- .../uast/kotlin/psi/UastKotlinPsiVariable.kt | 71 ++++++++----------- .../testData/WhenAndDestructing.kt | 17 +++++ .../testData/WhenAndDestructing.log.txt | 42 +++++++++++ .../testData/WhenAndDestructing.render.txt | 21 ++++++ .../uast-kotlin/tests/KotlinUastApiTest.kt | 32 +++++++-- .../tests/SimpleKotlinRenderLogTest.kt | 3 + 6 files changed, 138 insertions(+), 48 deletions(-) create mode 100644 plugins/uast-kotlin/testData/WhenAndDestructing.kt create mode 100644 plugins/uast-kotlin/testData/WhenAndDestructing.log.txt create mode 100644 plugins/uast-kotlin/testData/WhenAndDestructing.render.txt diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/psi/UastKotlinPsiVariable.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/psi/UastKotlinPsiVariable.kt index f40611d0d88..89c0fce877c 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/psi/UastKotlinPsiVariable.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/psi/UastKotlinPsiVariable.kt @@ -11,12 +11,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.uast.* import org.jetbrains.kotlin.types.isError -import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UElement -import org.jetbrains.uast.UastErrorType -import org.jetbrains.uast.getParentOfType +import org.jetbrains.uast.* import org.jetbrains.uast.kotlin.analyze import org.jetbrains.uast.kotlin.lz import org.jetbrains.uast.kotlin.orAnonymous @@ -27,7 +23,7 @@ class UastKotlinPsiVariable private constructor( name: String, typeProducer: () -> PsiType, val ktInitializer: KtExpression?, - val psiParent: PsiElement?, + psiParentProducer: () -> PsiElement?, val containingElement: UElement, val ktElement: KtElement ) : LightVariableBuilder( @@ -37,6 +33,8 @@ class UastKotlinPsiVariable private constructor( KotlinLanguage.INSTANCE ), PsiLocalVariable { + val psiParent by lz(psiParentProducer) + private val psiType: PsiType by lz(typeProducer) private val psiTypeElement: PsiTypeElement by lz { @@ -86,46 +84,37 @@ class UastKotlinPsiVariable private constructor( name = declaration.name.orAnonymous(""), typeProducer = { declaration.getType(containingElement) ?: UastErrorType }, ktInitializer = initializerExpression, - psiParent = psiParent, - containingElement = containingElement, - ktElement = declaration) - } - - fun create(declaration: KtDestructuringDeclaration, containingElement: UElement): PsiLocalVariable { - val psiParent = containingElement.getParentOfType()?.psi ?: declaration.parent - return UastKotlinPsiVariable( - manager = declaration.manager, - name = "var" + Integer.toHexString(declaration.getHashCode()), - typeProducer = { declaration.getType(containingElement) ?: UastErrorType }, - ktInitializer = declaration.initializer, - psiParent = psiParent, + psiParentProducer = { psiParent }, containingElement = containingElement, ktElement = declaration) } - fun create(initializer: KtExpression, containingElement: UElement, parent: PsiElement): PsiLocalVariable { - val psiParent = containingElement.getParentOfType()?.psi ?: parent - return UastKotlinPsiVariable( - manager = initializer.manager, - name = "var" + Integer.toHexString(initializer.getHashCode()), - typeProducer = { initializer.getType(containingElement) ?: UastErrorType }, - ktInitializer = initializer, - psiParent = psiParent, - containingElement = containingElement, - ktElement = initializer) - } + fun create(declaration: KtDestructuringDeclaration, containingElement: UElement): PsiLocalVariable = UastKotlinPsiVariable( + manager = declaration.manager, + name = "var" + Integer.toHexString(declaration.getHashCode()), + typeProducer = { declaration.getType(containingElement) ?: UastErrorType }, + ktInitializer = declaration.initializer, + psiParentProducer = { containingElement.getParentOfType()?.psi ?: declaration.parent }, + containingElement = containingElement, + ktElement = declaration) - fun create(name: String, localFunction: KtFunction, containingElement: UElement): PsiLocalVariable { - val psiParent = containingElement.getParentOfType()?.psi ?: localFunction.parent - return UastKotlinPsiVariable( - manager = localFunction.manager, - name = name, - typeProducer = { localFunction.getFunctionType(containingElement) ?: UastErrorType }, - ktInitializer = localFunction, - psiParent = psiParent, - containingElement = containingElement, - ktElement = localFunction) - } + fun create(initializer: KtExpression, containingElement: UElement, parent: PsiElement): PsiLocalVariable = UastKotlinPsiVariable( + manager = initializer.manager, + name = "var" + Integer.toHexString(initializer.getHashCode()), + typeProducer = { initializer.getType(containingElement) ?: UastErrorType }, + ktInitializer = initializer, + psiParentProducer = { containingElement.getParentOfType()?.psi ?: parent }, + containingElement = containingElement, + ktElement = initializer) + + fun create(name: String, localFunction: KtFunction, containingElement: UElement): PsiLocalVariable = UastKotlinPsiVariable( + manager = localFunction.manager, + name = name, + typeProducer = { localFunction.getFunctionType(containingElement) ?: UastErrorType }, + ktInitializer = localFunction, + psiParentProducer = { containingElement.getParentOfType()?.psi ?: localFunction.parent }, + containingElement = containingElement, + ktElement = localFunction) } } diff --git a/plugins/uast-kotlin/testData/WhenAndDestructing.kt b/plugins/uast-kotlin/testData/WhenAndDestructing.kt new file mode 100644 index 00000000000..da4ea1bb20c --- /dev/null +++ b/plugins/uast-kotlin/testData/WhenAndDestructing.kt @@ -0,0 +1,17 @@ +fun getElementsAdditionalResolve(string: String): String { + + val arr = listOf("1", "2") + + when (string) { + "aaaa" -> { + return "bindingContext" + } + + else -> { + val (bindingContext, statementFilter) = arr + return bindingContext + } + } +} + + diff --git a/plugins/uast-kotlin/testData/WhenAndDestructing.log.txt b/plugins/uast-kotlin/testData/WhenAndDestructing.log.txt new file mode 100644 index 00000000000..fe1b4642d28 --- /dev/null +++ b/plugins/uast-kotlin/testData/WhenAndDestructing.log.txt @@ -0,0 +1,42 @@ +UFile (package = ) + UClass (name = WhenAndDestructingKt) + UAnnotationMethod (name = getElementsAdditionalResolve) + UParameter (name = string) + UAnnotation (fqName = org.jetbrains.annotations.NotNull) + UBlockExpression + UDeclarationsExpression + ULocalVariable (name = arr) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2)) + UIdentifier (Identifier (listOf)) + USimpleNameReferenceExpression (identifier = listOf) + ULiteralExpression (value = "1") + ULiteralExpression (value = "2") + USwitchExpression + USimpleNameReferenceExpression (identifier = string) + UExpressionList (when) + USwitchClauseExpressionWithBody + ULiteralExpression (value = "aaaa") + UExpressionList (when_entry) + UReturnExpression + ULiteralExpression (value = "bindingContext") + UBreakExpression (label = null) + USwitchClauseExpressionWithBody + UExpressionList (when_entry) + UDeclarationsExpression + ULocalVariable (name = var837f1e82) + USimpleNameReferenceExpression (identifier = arr) + ULocalVariable (name = bindingContext) + UQualifiedReferenceExpression + USimpleNameReferenceExpression (identifier = var837f1e82) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (component1)) + USimpleNameReferenceExpression (identifier = ) + ULocalVariable (name = statementFilter) + UQualifiedReferenceExpression + USimpleNameReferenceExpression (identifier = var837f1e82) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (component2)) + USimpleNameReferenceExpression (identifier = ) + UReturnExpression + USimpleNameReferenceExpression (identifier = bindingContext) + UBreakExpression (label = null) diff --git a/plugins/uast-kotlin/testData/WhenAndDestructing.render.txt b/plugins/uast-kotlin/testData/WhenAndDestructing.render.txt new file mode 100644 index 00000000000..24f4b7a612b --- /dev/null +++ b/plugins/uast-kotlin/testData/WhenAndDestructing.render.txt @@ -0,0 +1,21 @@ +public final class WhenAndDestructingKt { + public static final fun getElementsAdditionalResolve(string: java.lang.String) : java.lang.String { + var arr: java.util.List = listOf("1", "2") + switch (string) { + "aaaa" -> { + return "bindingContext" + break + } + + -> { + var var837f1e82: = arr + var bindingContext: java.lang.String = var837f1e82.() + var statementFilter: java.lang.String = var837f1e82.() + return bindingContext + break + } + + } + + } +} diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt index 14f69a8adb6..681f83b8622 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt @@ -131,23 +131,19 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { fun testWhenStringLiteral() { doTest("WhenStringLiteral") { _, file -> - fun UFile.findULiteralExpressionFromPsi(refText: String): ULiteralExpression = - this.psi.findElementAt(file.psi.text.indexOf(refText))!! - .parentsWithSelf.mapNotNull { it.toUElementOfType() }.first() - - file.findULiteralExpressionFromPsi("abc").let { literalExpression -> + file.findFromPsi("abc").let { literalExpression -> val psi = literalExpression.psi!! Assert.assertTrue(psi is KtLiteralStringTemplateEntry) UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java) } - file.findULiteralExpressionFromPsi("def").let { literalExpression -> + file.findFromPsi("def").let { literalExpression -> val psi = literalExpression.psi!! Assert.assertTrue(psi is KtLiteralStringTemplateEntry) UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java) } - file.findULiteralExpressionFromPsi("def1").let { literalExpression -> + file.findFromPsi("def1").let { literalExpression -> val psi = literalExpression.psi!! Assert.assertTrue(psi is KtLiteralStringTemplateEntry) UsefulTestCase.assertInstanceOf(literalExpression.uastParent, UBlockExpression::class.java) @@ -156,4 +152,26 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } + + @Test + fun testWhenAndDestructing() { + doTest("WhenAndDestructing") { _, file -> + + file.findFromPsi("val (bindingContext, statementFilter) = arr").let { e -> + val uBlockExpression = e.getParentOfType() + Assert.assertNotNull(uBlockExpression) + val uMethod = uBlockExpression!!.getParentOfType() + Assert.assertNotNull(uMethod) + } + + } + } +} + + + +inline fun UFile.findFromPsi(refText: String): T = + (this.psi.findElementAt(this.psi.text.indexOf(refText)) ?: throw AssertionError("requested text '$refText' was not found in ${this.psi.name}")) + .parentsWithSelf.dropWhile { !it.text.contains(refText) }.mapNotNull { it.toUElementOfType() }.first().also { + if (it.psi != null && it.psi?.text != refText) throw AssertionError("requested text '$refText' found as '${it.psi?.text}' in $it") } diff --git a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt index 86a2309a2f0..d8d50993b41 100644 --- a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt +++ b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt @@ -48,4 +48,7 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() { @Test fun testUnexpectedContainer() = doTest("UnexpectedContainerException") { testName, file -> check(testName, file, false) } @Test fun testWhenStringLiteral() = doTest("WhenStringLiteral") { testName, file -> check(testName, file, false) } + + @Test + fun testWhenAndDestructing() = doTest("WhenAndDestructing") { testName, file -> check(testName, file, false) } }