From 529d0b8326451a4729c71065614caf89ef5c0d94 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 29 Jan 2019 12:35:33 +0300 Subject: [PATCH] 191: Uast: initial support for multiple required types --- .../kotlin/KotlinUastLanguagePlugin.kt.191 | 102 ++++++++++----- .../kotlin/expressions/ElvisExpression.kt.191 | 121 ++++++++++++++++++ ...inStringTemplateUPolyadicExpression.kt.191 | 15 ++- .../KotlinUCallableReferenceExpression.kt.191 | 52 ++++++++ .../KotlinUClassLiteralExpression.kt.191 | 41 ++++++ .../KotlinUSwitchExpression.kt.191 | 94 ++++++++++++++ .../expressions/KotlinUTryExpression.kt.191 | 50 ++++++++ .../testData/InnerClasses.splog.txt.191 | 7 + .../tests/MultiplesRequiredTypesTest.kt.191 | 49 +++++++ 9 files changed, 494 insertions(+), 37 deletions(-) create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.191 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.191 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUClassLiteralExpression.kt.191 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.191 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.191 create mode 100644 plugins/uast-kotlin/testData/InnerClasses.splog.txt.191 create mode 100644 plugins/uast-kotlin/tests/MultiplesRequiredTypesTest.kt.191 diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 index d1c2a09b61c..39d4bed2a8d 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 @@ -76,23 +76,27 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class?): UElement? { if (!element.isJvmElement) return null - return convertDeclarationOrElement(element, parent, requiredType) + return convertDeclarationOrElement(element, parent, elementTypes(requiredType)) } override fun convertElementWithParent(element: PsiElement, requiredType: Class?): UElement? { if (!element.isJvmElement) return null - if (element is PsiFile) return convertDeclaration(element, null, requiredType) - if (element is KtLightClassForFacade) return convertDeclaration(element, null, requiredType) + if (element is PsiFile) return convertDeclaration(element, null, elementTypes(requiredType)) + if (element is KtLightClassForFacade) return convertDeclaration(element, null, elementTypes(requiredType)) - return convertDeclarationOrElement(element, null, requiredType) + return convertDeclarationOrElement(element, null, elementTypes(requiredType)) } - private fun convertDeclarationOrElement(element: PsiElement, givenParent: UElement?, requiredType: Class?): UElement? { + private fun convertDeclarationOrElement( + element: PsiElement, + givenParent: UElement?, + requiredType: Array> + ): UElement? { if (element is UElement) return element if (element.isValid) { element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement -> - return if (requiredType == null || requiredType.isInstance(cachedUElement)) cachedUElement else null + return if (requiredType.isAssignableFrom(cachedUElement.javaClass)) cachedUElement else null } } @@ -147,8 +151,9 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { } internal fun convertDeclaration(element: PsiElement, - givenParent: UElement?, - requiredType: Class?): UElement? { + givenParent: UElement?, + expectedTypes: Array> + ): UElement? { fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) } fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = @@ -158,7 +163,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { { ctor(element as P, ktElement, givenParent) } val original = element.originalElement - return with(requiredType) { + return with(expectedTypes) { when (original) { is KtLightMethod -> el(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 is KtLightClass -> when (original.kotlinOrigin) { @@ -200,21 +205,21 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { else { el { val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null - convertDeclaration(lightMethod, givenParent, requiredType) + convertDeclaration(lightMethod, givenParent, expectedTypes) } } is KtPropertyAccessor -> el { val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null - convertDeclaration(lightMethod, givenParent, requiredType) + convertDeclaration(lightMethod, givenParent, expectedTypes) } is KtProperty -> if (original.isLocal) { - KotlinConverter.convertPsiElement(element, givenParent, requiredType) + KotlinConverter.convertPsiElement(element, givenParent, expectedTypes) } else { - convertNonLocalProperty(original, givenParent, requiredType) + convertNonLocalProperty(original, givenParent, expectedTypes) } is KtParameter -> el { @@ -228,10 +233,10 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) } is KtAnnotationEntry -> el(build(::KotlinUAnnotation)) is KtCallExpression -> - if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) { + if (expectedTypes.isAssignableFrom(KotlinUNestedAnnotation::class.java) && !expectedTypes.isAssignableFrom(UCallExpression::class.java)) { el { KotlinUNestedAnnotation.tryCreate(original, givenParent) } } else null - is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType) + is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, expectedTypes) else -> null } } @@ -258,19 +263,41 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { else -> false } } + + @Suppress("UNCHECKED_CAST") + fun convertElement(element: PsiElement, parent: UElement?, expectedTypes: Array>): T? { + val nonEmptyExpectedTypes = expectedTypes.nonEmptyOr(DEFAULT_TYPES_LIST) + return (convertDeclaration(element, parent, nonEmptyExpectedTypes) + ?: KotlinConverter.convertPsiElement(element, parent, nonEmptyExpectedTypes)) as? T + } + + override fun convertElementWithParent(element: PsiElement, requiredTypes: Array>): T? { + return convertElement(element, null, requiredTypes) + } + + override fun convertToAlternatives(element: PsiElement, requiredTypes: Array>) = when (element) { + else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull() + } } -internal inline fun Class?.el(f: () -> UElement?): UElement? { +internal inline fun Class<*>?.el(f: () -> UElement?): UElement? { return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null } -internal inline fun Class?.expr(f: () -> UExpression?): UExpression? { - return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +internal inline fun Array>.el(f: () -> UElement?): UElement? { + return if (isAssignableFrom(ActualT::class.java)) f() else null } +internal inline fun Array>.expr(f: () -> UExpression?): UExpression? { + return if (isAssignableFrom(ActualT::class.java)) f() else null +} + +internal fun Array>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) } + private fun convertNonLocalProperty(property: KtProperty, givenParent: UElement?, - requiredType: Class?): UElement? { + requiredType: Array> +): UElement? { val methods = LightClassUtil.getLightClassPropertyMethods(property) return methods.backingField?.let { backingField -> with(requiredType) { @@ -298,12 +325,13 @@ internal object KotlinConverter { internal fun convertPsiElement(element: PsiElement?, givenParent: UElement?, - requiredType: Class?): UElement? { + expectedTypes: Array> + ): UElement? { fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { return { ctor(element as P, givenParent) } } - return with (requiredType) { when (element) { + return with (expectedTypes) { when (element) { is KtParameterList -> el { val declarationsExpression = KotlinUDeclarationsExpression(givenParent) declarationsExpression.apply { @@ -328,19 +356,19 @@ internal object KotlinConverter { } } - is KtExpression -> KotlinConverter.convertExpression(element, givenParent, requiredType) - is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) } + is KtExpression -> KotlinConverter.convertExpression(element, givenParent, expectedTypes) + is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, expectedTypes) } is KtLightElementBase -> { val expression = element.kotlinOrigin when (expression) { - is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, requiredType) + is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, expectedTypes) else -> el { UastEmptyExpression(givenParent) } } } is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el(build(::KotlinStringULiteralExpression)) - is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, requiredType) } ?: expr { UastEmptyExpression } + is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, expectedTypes) } ?: expr { UastEmptyExpression } is KtWhenEntry -> el(build(::KotlinUSwitchEntry)) - is KtWhenCondition -> convertWhenCondition(element, givenParent, requiredType) + is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes) is KtTypeReference -> el { LazyKotlinUTypeReferenceExpression(element, givenParent) } is KtConstructorDelegationCall -> el { KotlinUFunctionCallExpression(element, givenParent) } @@ -378,7 +406,8 @@ internal object KotlinConverter { internal fun convertEntry(entry: KtStringTemplateEntry, givenParent: UElement?, - requiredType: Class? = null): UExpression? { + requiredType: Array> + ): UExpression? { return with(requiredType) { if (entry is KtStringTemplateEntryWithExpression) { expr { @@ -404,7 +433,8 @@ internal object KotlinConverter { internal fun convertExpression(expression: KtExpression, givenParent: UElement?, - requiredType: Class? = null): UExpression? { + requiredType: Array> + ): UExpression? { fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { return { ctor(expression as P, givenParent) } } @@ -414,7 +444,7 @@ internal object KotlinConverter { is KtStringTemplateExpression -> { when { - forceUInjectionHost || (requiredType != null && UInjectionHost::class.java.isAssignableFrom(requiredType)) -> + forceUInjectionHost || requiredType.contains(UInjectionHost::class.java) -> expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } expression.entries.isEmpty() -> { expr { KotlinStringULiteralExpression(expression, givenParent, "") } @@ -496,7 +526,8 @@ internal object KotlinConverter { internal fun convertWhenCondition(condition: KtWhenCondition, givenParent: UElement?, - requiredType: Class? = null): UExpression? { + requiredType: Array> + ): UExpression? { return with(requiredType) { when (condition) { is KtWhenConditionInRange -> expr { @@ -532,11 +563,11 @@ internal object KotlinConverter { } internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression { - return expression?.let { convertExpression(it, parent, null) } ?: UastEmptyExpression + return expression?.let { convertExpression(it, parent, DEFAULT_EXPRESSION_TYPES_LIST) } ?: UastEmptyExpression } internal fun convertOrNull(expression: KtExpression?, parent: UElement?): UExpression? { - return if (expression != null) convertExpression(expression, parent, null) else null + return if (expression != null) convertExpression(expression, parent, DEFAULT_EXPRESSION_TYPES_LIST) else null } internal fun KtPsiFactory.createAnalyzableExpression(text: String, context: PsiElement): KtExpression = @@ -567,3 +598,10 @@ private fun convertVariablesDeclaration( } return declarationsExpression.apply { declarations = listOf(variable) } } + +private fun expressionTypes(requiredType: Class?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST + +private fun elementTypes(requiredType: Class?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_TYPES_LIST + +private fun Array>.nonEmptyOr(default: Array>) = takeIf { it.isNotEmpty() } + ?: default \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.191 new file mode 100644 index 00000000000..2ced5a28b57 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.191 @@ -0,0 +1,121 @@ +package org.jetbrains.uast.kotlin.expressions + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.CommonSupertypes +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier +import org.jetbrains.uast.kotlin.kinds.KotlinSpecialExpressionKinds +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable + + +private fun createVariableReferenceExpression(variable: UVariable, containingElement: UElement?) = + object : USimpleNameReferenceExpression, JvmDeclarationUElementPlaceholder { + override val psi: PsiElement? = null + override fun resolve(): PsiElement? = variable + override val uastParent: UElement? = containingElement + override val resolvedName: String? = variable.name + override val annotations: List = emptyList() + override val identifier: String = variable.name.orAnonymous() + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + } + +private fun createNullLiteralExpression(containingElement: UElement?) = + object : ULiteralExpression, JvmDeclarationUElementPlaceholder { + override val psi: PsiElement? = null + override val uastParent: UElement? = containingElement + override val value: Any? = null + override val annotations: List = emptyList() + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + } + +private fun createNotEqWithNullExpression(variable: UVariable, containingElement: UElement?) = + object : UBinaryExpression, JvmDeclarationUElementPlaceholder { + override val psi: PsiElement? = null + override val uastParent: UElement? = containingElement + override val leftOperand: UExpression by lz { createVariableReferenceExpression(variable, this) } + override val rightOperand: UExpression by lz { createNullLiteralExpression(this) } + override val operator: UastBinaryOperator = UastBinaryOperator.NOT_EQUALS + override val operatorIdentifier: UIdentifier? = KotlinUIdentifier(null, this) + override fun resolveOperator(): PsiMethod? = null + override val annotations: List = emptyList() + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + } + +private fun createElvisExpressions( + left: KtExpression, + right: KtExpression, + containingElement: UElement?, + psiParent: PsiElement): List { + + val declaration = KotlinUDeclarationsExpression(containingElement) + val tempVariable = KotlinULocalVariable(UastKotlinPsiVariable.create(left, declaration, psiParent), left, declaration) + declaration.declarations = listOf(tempVariable) + + val ifExpression = object : UIfExpression, JvmDeclarationUElementPlaceholder { + override val psi: PsiElement? = null + override val uastParent: UElement? = containingElement + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + override val condition: UExpression by lz { createNotEqWithNullExpression(tempVariable, this) } + override val thenExpression: UExpression? by lz { createVariableReferenceExpression(tempVariable, this) } + override val elseExpression: UExpression? by lz { KotlinConverter.convertExpression(right, this, DEFAULT_EXPRESSION_TYPES_LIST) } + override val isTernary: Boolean = false + override val annotations: List = emptyList() + override val ifIdentifier: UIdentifier = KotlinUIdentifier(null, this) + override val elseIdentifier: UIdentifier? = KotlinUIdentifier(null, this) + } + + return listOf(declaration, ifExpression) +} + +fun createElvisExpression(elvisExpression: KtBinaryExpression, givenParent: UElement?): UExpression { + val left = elvisExpression.left ?: return UastEmptyExpression + val right = elvisExpression.right ?: return UastEmptyExpression + + return KotlinUElvisExpression(elvisExpression, left, right, givenParent) +} + +class KotlinUElvisExpression( + private val elvisExpression: KtBinaryExpression, + private val left: KtExpression, + private val right: KtExpression, + givenParent: UElement? +) : KotlinAbstractUElement(givenParent), UExpressionList, KotlinEvaluatableUElement { + + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = elvisExpression + override val psi: PsiElement? = sourcePsi + override val kind = KotlinSpecialExpressionKinds.ELVIS + override val annotations: List = emptyList() + override val expressions: List by lz { + createElvisExpressions(left, right, this, elvisExpression.parent) + } + + val lhsDeclaration get() = (expressions[0] as UDeclarationsExpression).declarations.single() + val rhsIfExpression get() = expressions[1] as UIfExpression + + override fun asRenderString(): String { + return kind.name + " " + + expressions.joinToString(separator = "\n", prefix = "{\n", postfix = "\n}") { + it.asRenderString().withMargin + } + } + + override fun getExpressionType(): PsiType? { + val leftType = left.analyze()[BindingContext.EXPRESSION_TYPE_INFO, left]?.type ?: return null + val rightType = right.analyze()[BindingContext.EXPRESSION_TYPE_INFO, right]?.type ?: return null + + return CommonSupertypes + .commonSupertype(listOf(leftType, rightType)) + .toPsiType(this, elvisExpression, boxed = false) + } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinStringTemplateUPolyadicExpression.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinStringTemplateUPolyadicExpression.kt.191 index f760d4be49c..65c25c8a1ed 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinStringTemplateUPolyadicExpression.kt.191 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinStringTemplateUPolyadicExpression.kt.191 @@ -18,10 +18,7 @@ package org.jetbrains.uast.kotlin import com.intellij.psi.PsiLanguageInjectionHost import org.jetbrains.kotlin.psi.KtStringTemplateExpression -import org.jetbrains.uast.UElement -import org.jetbrains.uast.UExpression -import org.jetbrains.uast.UPolyadicExpression -import org.jetbrains.uast.UastBinaryOperator +import org.jetbrains.uast.* import org.jetbrains.uast.expressions.UInjectionHost class KotlinStringTemplateUPolyadicExpression( @@ -32,7 +29,15 @@ class KotlinStringTemplateUPolyadicExpression( KotlinUElementWithType, KotlinEvaluatableUElement, UInjectionHost { - override val operands: List by lz { psi.entries.map { KotlinConverter.convertEntry(it, this)!! } } + override val operands: List by lz { + psi.entries.map { + KotlinConverter.convertEntry( + it, + this, + DEFAULT_EXPRESSION_TYPES_LIST + )!! + } + } override val operator = UastBinaryOperator.PLUS override val psiLanguageInjectionHost: PsiLanguageInjectionHost get() = psi diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.191 new file mode 100644 index 00000000000..83011877fbe --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.191 @@ -0,0 +1,52 @@ +/* + * 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 + +import com.intellij.psi.PsiNamedElement +import com.intellij.psi.ResolveResult +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.resolve.BindingContext.DOUBLE_COLON_LHS +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.internal.getResolveResultVariants + +class KotlinUCallableReferenceExpression( + override val psi: KtCallableReferenceExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UCallableReferenceExpression, UMultiResolvable, KotlinUElementWithType { + override val qualifierExpression: UExpression? + get() { + if (qualifierType != null) return null + val receiverExpression = psi.receiverExpression ?: return null + return KotlinConverter.convertExpression(receiverExpression, this, DEFAULT_EXPRESSION_TYPES_LIST) + } + + override val qualifierType by lz { + val ktType = psi.analyze()[DOUBLE_COLON_LHS, psi.receiverExpression]?.type ?: return@lz null + ktType.toPsiType(this, psi, boxed = true) + } + + override val callableName: String + get() = psi.callableReference.getReferencedName() + + override val resolvedName: String? + get() = (resolve() as? PsiNamedElement)?.name + + override fun resolve() = psi.callableReference.resolveCallToDeclaration(this) + + override fun multiResolve(): Iterable = getResolveResultVariants(psi.callableReference) + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUClassLiteralExpression.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUClassLiteralExpression.kt.191 new file mode 100644 index 00000000000..748ea720f53 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUClassLiteralExpression.kt.191 @@ -0,0 +1,41 @@ +/* + * 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 + +import org.jetbrains.kotlin.psi.KtClassLiteralExpression +import org.jetbrains.kotlin.resolve.BindingContext.DOUBLE_COLON_LHS +import org.jetbrains.uast.DEFAULT_EXPRESSION_TYPES_LIST +import org.jetbrains.uast.UClassLiteralExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression + +class KotlinUClassLiteralExpression( + override val psi: KtClassLiteralExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UClassLiteralExpression, KotlinUElementWithType { + override val type by lz { + val ktType = psi.analyze()[DOUBLE_COLON_LHS, psi.receiverExpression]?.type ?: return@lz null + ktType.toPsiType(this, psi, boxed = true) + } + + override val expression: UExpression? + get() { + if (type != null) return null + val receiverExpression = psi.receiverExpression ?: return null + return KotlinConverter.convertExpression(receiverExpression, this, DEFAULT_EXPRESSION_TYPES_LIST) + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.191 new file mode 100644 index 00000000000..99a7fe36f64 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.191 @@ -0,0 +1,94 @@ +/* + * 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 + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtWhenEntry +import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier +import org.jetbrains.uast.kotlin.kinds.KotlinSpecialExpressionKinds + +class KotlinUSwitchExpression( + override val psi: KtWhenExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USwitchExpression, KotlinUElementWithType { + override val expression by lz { KotlinConverter.convertOrNull(psi.subjectExpression, this) } + + override val body: UExpressionList by lz { + object : KotlinUExpressionList(psi, KotlinSpecialExpressionKinds.WHEN, this@KotlinUSwitchExpression) { + override fun asRenderString() = expressions.joinToString("\n") { it.asRenderString().withMargin } + }.apply { + expressions = this@KotlinUSwitchExpression.psi.entries.map { KotlinUSwitchEntry(it, this) } + } + } + + override fun asRenderString() = buildString { + val expr = expression?.let { "(" + it.asRenderString() + ") " } ?: "" + appendln("switch $expr {") + appendln(body.asRenderString()) + appendln("}") + } + + override val switchIdentifier: UIdentifier + get() = KotlinUIdentifier(null, this) +} + +class KotlinUSwitchEntry( + override val psi: KtWhenEntry, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USwitchClauseExpressionWithBody { + override val caseValues by lz { + psi.conditions.map { KotlinConverter.convertWhenCondition(it, this, DEFAULT_EXPRESSION_TYPES_LIST) ?: UastEmptyExpression } + } + + override val body: UExpressionList by lz { + object : KotlinUExpressionList(psi, KotlinSpecialExpressionKinds.WHEN_ENTRY, this@KotlinUSwitchEntry) { + override fun asRenderString() = buildString { + appendln("{") + expressions.forEach { appendln(it.asRenderString().withMargin) } + appendln("}") + } + }.apply { + val exprPsi = this@KotlinUSwitchEntry.psi.expression + val userExpressions = when (exprPsi) { + is KtBlockExpression -> exprPsi.statements.map { KotlinConverter.convertOrEmpty(it, this) } + else -> listOf(KotlinConverter.convertOrEmpty(exprPsi, this)) + } + expressions = userExpressions + object : UBreakExpression, JvmDeclarationUElementPlaceholder { + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + override val psi: PsiElement? + get() = null + override val label: String? + get() = null + override val uastParent: UElement? + get() = this@KotlinUSwitchEntry + override val annotations: List + get() = emptyList() + } + } + } + + override fun convertParent(): UElement? { + val result = KotlinConverter.unwrapElements(psi.parent)?.let { parentUnwrapped -> + KotlinUastLanguagePlugin().convertElementWithParent(parentUnwrapped, null) + } + return (result as? KotlinUSwitchExpression)?.body ?: result + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.191 new file mode 100644 index 00000000000..548d9520778 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.191 @@ -0,0 +1,50 @@ +/* + * 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 + +import org.jetbrains.kotlin.psi.KtTryExpression +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier + +class KotlinUTryExpression( + override val psi: KtTryExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UTryExpression, KotlinUElementWithType { + override val tryClause by lz { KotlinConverter.convertOrEmpty(psi.tryBlock, this) } + override val catchClauses by lz { psi.catchClauses.map { KotlinUCatchClause(it, this) } } + override val finallyClause by lz { + psi.finallyBlock?.finalExpression?.let { + KotlinConverter.convertExpression( + it, + this, + DEFAULT_EXPRESSION_TYPES_LIST + ) + } + } + + override val resourceVariables: List + get() = emptyList() + + override val hasResources: Boolean + get() = false + + override val tryIdentifier: UIdentifier + get() = KotlinUIdentifier(null, this) + + override val finallyIdentifier: UIdentifier? + get() = null +} \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/InnerClasses.splog.txt.191 b/plugins/uast-kotlin/testData/InnerClasses.splog.txt.191 new file mode 100644 index 00000000000..d4d525ee83c --- /dev/null +++ b/plugins/uast-kotlin/testData/InnerClasses.splog.txt.191 @@ -0,0 +1,7 @@ +UFile (package = ) + UClass (name = Foo) + UClass (name = Bar) + UAnnotationMethod (name = Bar) + UAnnotationMethod (name = getAPlusB) + UClass (name = Baz) + UAnnotationMethod (name = doNothing) diff --git a/plugins/uast-kotlin/tests/MultiplesRequiredTypesTest.kt.191 b/plugins/uast-kotlin/tests/MultiplesRequiredTypesTest.kt.191 new file mode 100644 index 00000000000..df241db01d4 --- /dev/null +++ b/plugins/uast-kotlin/tests/MultiplesRequiredTypesTest.kt.191 @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.uast.test.kotlin + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.uast.* +import org.jetbrains.uast.test.env.kotlin.assertEqualsToFile +import java.io.File + +class MultiplesRequiredTypesTest : AbstractKotlinUastTest() { + + + fun testInnerClasses() = doTest("InnerClasses") + + override fun check(testName: String, file: UFile) { + val valuesFile = getTestFile(testName, "splog.txt") + assertEqualsToFile("MultiplesTargetConversionResult", valuesFile, file.asMultiplesTargetConversionResult()) + } + + private fun UFile.asMultiplesTargetConversionResult(): String { + + val builder = StringBuilder() + var level = 0 + (this.psi as KtFile).accept(object : PsiElementVisitor() { + override fun visitElement(element: PsiElement) { + val uElement = + element.toUElementOfExpectedTypes(UFile::class.java, UClass::class.java, UField::class.java, UMethod::class.java) + if (uElement != null) { + builder.append(" ".repeat(level)) + builder.append(uElement.asLogString()) + builder.appendln() + } + if (uElement != null) level++ + element.acceptChildren(this) + if (uElement != null) level-- + } + }) + return builder.toString() + } + + private fun getTestFile(testName: String, ext: String) = + File(File(AbstractKotlinUastTest.TEST_KOTLIN_MODEL_DIR, testName).canonicalPath + '.' + ext) + +}