191: Uast: initial support for multiple required types
This commit is contained in:
@@ -76,23 +76,27 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
|
|||||||
|
|
||||||
override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class<out UElement>?): UElement? {
|
override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class<out UElement>?): UElement? {
|
||||||
if (!element.isJvmElement) return null
|
if (!element.isJvmElement) return null
|
||||||
return convertDeclarationOrElement(element, parent, requiredType)
|
return convertDeclarationOrElement(element, parent, elementTypes(requiredType))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun convertElementWithParent(element: PsiElement, requiredType: Class<out UElement>?): UElement? {
|
override fun convertElementWithParent(element: PsiElement, requiredType: Class<out UElement>?): UElement? {
|
||||||
if (!element.isJvmElement) return null
|
if (!element.isJvmElement) return null
|
||||||
if (element is PsiFile) return convertDeclaration(element, null, requiredType)
|
if (element is PsiFile) return convertDeclaration(element, null, elementTypes(requiredType))
|
||||||
if (element is KtLightClassForFacade) return convertDeclaration(element, null, 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<out UElement>?): UElement? {
|
private fun convertDeclarationOrElement(
|
||||||
|
element: PsiElement,
|
||||||
|
givenParent: UElement?,
|
||||||
|
requiredType: Array<out Class<out UElement>>
|
||||||
|
): UElement? {
|
||||||
if (element is UElement) return element
|
if (element is UElement) return element
|
||||||
|
|
||||||
if (element.isValid) {
|
if (element.isValid) {
|
||||||
element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement ->
|
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,
|
internal fun convertDeclaration(element: PsiElement,
|
||||||
givenParent: UElement?,
|
givenParent: UElement?,
|
||||||
requiredType: Class<out UElement>?): UElement? {
|
expectedTypes: Array<out Class<out UElement>>
|
||||||
|
): UElement? {
|
||||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) }
|
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) }
|
||||||
|
|
||||||
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? =
|
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? =
|
||||||
@@ -158,7 +163,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
|
|||||||
{ ctor(element as P, ktElement, givenParent) }
|
{ ctor(element as P, ktElement, givenParent) }
|
||||||
|
|
||||||
val original = element.originalElement
|
val original = element.originalElement
|
||||||
return with(requiredType) {
|
return with(expectedTypes) {
|
||||||
when (original) {
|
when (original) {
|
||||||
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
|
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
|
||||||
is KtLightClass -> when (original.kotlinOrigin) {
|
is KtLightClass -> when (original.kotlinOrigin) {
|
||||||
@@ -200,21 +205,21 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
|
|||||||
else {
|
else {
|
||||||
el<UMethod> {
|
el<UMethod> {
|
||||||
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
|
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
|
||||||
convertDeclaration(lightMethod, givenParent, requiredType)
|
convertDeclaration(lightMethod, givenParent, expectedTypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtPropertyAccessor -> el<UMethod> {
|
is KtPropertyAccessor -> el<UMethod> {
|
||||||
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
|
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
|
||||||
convertDeclaration(lightMethod, givenParent, requiredType)
|
convertDeclaration(lightMethod, givenParent, expectedTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtProperty ->
|
is KtProperty ->
|
||||||
if (original.isLocal) {
|
if (original.isLocal) {
|
||||||
KotlinConverter.convertPsiElement(element, givenParent, requiredType)
|
KotlinConverter.convertPsiElement(element, givenParent, expectedTypes)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
convertNonLocalProperty(original, givenParent, requiredType)
|
convertNonLocalProperty(original, givenParent, expectedTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtParameter -> el<UParameter> {
|
is KtParameter -> el<UParameter> {
|
||||||
@@ -228,10 +233,10 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
|
|||||||
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) }
|
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) }
|
||||||
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
|
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
|
||||||
is KtCallExpression ->
|
is KtCallExpression ->
|
||||||
if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) {
|
if (expectedTypes.isAssignableFrom(KotlinUNestedAnnotation::class.java) && !expectedTypes.isAssignableFrom(UCallExpression::class.java)) {
|
||||||
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
|
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
|
||||||
} else null
|
} else null
|
||||||
is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType)
|
is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, expectedTypes)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,19 +263,41 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : UElement> convertElement(element: PsiElement, parent: UElement?, expectedTypes: Array<out Class<out T>>): T? {
|
||||||
|
val nonEmptyExpectedTypes = expectedTypes.nonEmptyOr(DEFAULT_TYPES_LIST)
|
||||||
|
return (convertDeclaration(element, parent, nonEmptyExpectedTypes)
|
||||||
|
?: KotlinConverter.convertPsiElement(element, parent, nonEmptyExpectedTypes)) as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : UElement> convertElementWithParent(element: PsiElement, requiredTypes: Array<out Class<out T>>): T? {
|
||||||
|
return convertElement(element, null, requiredTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: Array<out Class<out T>>) = when (element) {
|
||||||
|
else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <reified ActualT : UElement> Class<out UElement>?.el(f: () -> UElement?): UElement? {
|
internal inline fun <reified ActualT : UElement> Class<*>?.el(f: () -> UElement?): UElement? {
|
||||||
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
|
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <reified ActualT : UElement> Class<out UElement>?.expr(f: () -> UExpression?): UExpression? {
|
internal inline fun <reified ActualT : UElement> Array<out Class<out UElement>>.el(f: () -> UElement?): UElement? {
|
||||||
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
|
return if (isAssignableFrom(ActualT::class.java)) f() else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inline fun <reified ActualT : UElement> Array<out Class<out UElement>>.expr(f: () -> UExpression?): UExpression? {
|
||||||
|
return if (isAssignableFrom(ActualT::class.java)) f() else null
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Array<out Class<out UElement>>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) }
|
||||||
|
|
||||||
private fun convertNonLocalProperty(property: KtProperty,
|
private fun convertNonLocalProperty(property: KtProperty,
|
||||||
givenParent: UElement?,
|
givenParent: UElement?,
|
||||||
requiredType: Class<out UElement>?): UElement? {
|
requiredType: Array<out Class<out UElement>>
|
||||||
|
): UElement? {
|
||||||
val methods = LightClassUtil.getLightClassPropertyMethods(property)
|
val methods = LightClassUtil.getLightClassPropertyMethods(property)
|
||||||
return methods.backingField?.let { backingField ->
|
return methods.backingField?.let { backingField ->
|
||||||
with(requiredType) {
|
with(requiredType) {
|
||||||
@@ -298,12 +325,13 @@ internal object KotlinConverter {
|
|||||||
|
|
||||||
internal fun convertPsiElement(element: PsiElement?,
|
internal fun convertPsiElement(element: PsiElement?,
|
||||||
givenParent: UElement?,
|
givenParent: UElement?,
|
||||||
requiredType: Class<out UElement>?): UElement? {
|
expectedTypes: Array<out Class<out UElement>>
|
||||||
|
): UElement? {
|
||||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
|
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
|
||||||
return { ctor(element as P, givenParent) }
|
return { ctor(element as P, givenParent) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return with (requiredType) { when (element) {
|
return with (expectedTypes) { when (element) {
|
||||||
is KtParameterList -> el<UDeclarationsExpression> {
|
is KtParameterList -> el<UDeclarationsExpression> {
|
||||||
val declarationsExpression = KotlinUDeclarationsExpression(givenParent)
|
val declarationsExpression = KotlinUDeclarationsExpression(givenParent)
|
||||||
declarationsExpression.apply {
|
declarationsExpression.apply {
|
||||||
@@ -328,19 +356,19 @@ internal object KotlinConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtExpression -> KotlinConverter.convertExpression(element, givenParent, requiredType)
|
is KtExpression -> KotlinConverter.convertExpression(element, givenParent, expectedTypes)
|
||||||
is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) }
|
is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, expectedTypes) }
|
||||||
is KtLightElementBase -> {
|
is KtLightElementBase -> {
|
||||||
val expression = element.kotlinOrigin
|
val expression = element.kotlinOrigin
|
||||||
when (expression) {
|
when (expression) {
|
||||||
is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, requiredType)
|
is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, expectedTypes)
|
||||||
else -> el<UExpression> { UastEmptyExpression(givenParent) }
|
else -> el<UExpression> { UastEmptyExpression(givenParent) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el<ULiteralExpression>(build(::KotlinStringULiteralExpression))
|
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el<ULiteralExpression>(build(::KotlinStringULiteralExpression))
|
||||||
is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, requiredType) } ?: expr<UExpression> { UastEmptyExpression }
|
is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, expectedTypes) } ?: expr<UExpression> { UastEmptyExpression }
|
||||||
is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry))
|
is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry))
|
||||||
is KtWhenCondition -> convertWhenCondition(element, givenParent, requiredType)
|
is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes)
|
||||||
is KtTypeReference -> el<UTypeReferenceExpression> { LazyKotlinUTypeReferenceExpression(element, givenParent) }
|
is KtTypeReference -> el<UTypeReferenceExpression> { LazyKotlinUTypeReferenceExpression(element, givenParent) }
|
||||||
is KtConstructorDelegationCall ->
|
is KtConstructorDelegationCall ->
|
||||||
el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) }
|
el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) }
|
||||||
@@ -378,7 +406,8 @@ internal object KotlinConverter {
|
|||||||
|
|
||||||
internal fun convertEntry(entry: KtStringTemplateEntry,
|
internal fun convertEntry(entry: KtStringTemplateEntry,
|
||||||
givenParent: UElement?,
|
givenParent: UElement?,
|
||||||
requiredType: Class<out UElement>? = null): UExpression? {
|
requiredType: Array<out Class<out UElement>>
|
||||||
|
): UExpression? {
|
||||||
return with(requiredType) {
|
return with(requiredType) {
|
||||||
if (entry is KtStringTemplateEntryWithExpression) {
|
if (entry is KtStringTemplateEntryWithExpression) {
|
||||||
expr<UExpression> {
|
expr<UExpression> {
|
||||||
@@ -404,7 +433,8 @@ internal object KotlinConverter {
|
|||||||
|
|
||||||
internal fun convertExpression(expression: KtExpression,
|
internal fun convertExpression(expression: KtExpression,
|
||||||
givenParent: UElement?,
|
givenParent: UElement?,
|
||||||
requiredType: Class<out UElement>? = null): UExpression? {
|
requiredType: Array<out Class<out UElement>>
|
||||||
|
): UExpression? {
|
||||||
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
|
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
|
||||||
return { ctor(expression as P, givenParent) }
|
return { ctor(expression as P, givenParent) }
|
||||||
}
|
}
|
||||||
@@ -414,7 +444,7 @@ internal object KotlinConverter {
|
|||||||
|
|
||||||
is KtStringTemplateExpression -> {
|
is KtStringTemplateExpression -> {
|
||||||
when {
|
when {
|
||||||
forceUInjectionHost || (requiredType != null && UInjectionHost::class.java.isAssignableFrom(requiredType)) ->
|
forceUInjectionHost || requiredType.contains(UInjectionHost::class.java) ->
|
||||||
expr<UInjectionHost> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
|
expr<UInjectionHost> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
|
||||||
expression.entries.isEmpty() -> {
|
expression.entries.isEmpty() -> {
|
||||||
expr<ULiteralExpression> { KotlinStringULiteralExpression(expression, givenParent, "") }
|
expr<ULiteralExpression> { KotlinStringULiteralExpression(expression, givenParent, "") }
|
||||||
@@ -496,7 +526,8 @@ internal object KotlinConverter {
|
|||||||
|
|
||||||
internal fun convertWhenCondition(condition: KtWhenCondition,
|
internal fun convertWhenCondition(condition: KtWhenCondition,
|
||||||
givenParent: UElement?,
|
givenParent: UElement?,
|
||||||
requiredType: Class<out UElement>? = null): UExpression? {
|
requiredType: Array<out Class<out UElement>>
|
||||||
|
): UExpression? {
|
||||||
return with(requiredType) {
|
return with(requiredType) {
|
||||||
when (condition) {
|
when (condition) {
|
||||||
is KtWhenConditionInRange -> expr<UBinaryExpression> {
|
is KtWhenConditionInRange -> expr<UBinaryExpression> {
|
||||||
@@ -532,11 +563,11 @@ internal object KotlinConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression {
|
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? {
|
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 =
|
internal fun KtPsiFactory.createAnalyzableExpression(text: String, context: PsiElement): KtExpression =
|
||||||
@@ -567,3 +598,10 @@ private fun convertVariablesDeclaration(
|
|||||||
}
|
}
|
||||||
return declarationsExpression.apply { declarations = listOf(variable) }
|
return declarationsExpression.apply { declarations = listOf(variable) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun expressionTypes(requiredType: Class<out UElement>?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST
|
||||||
|
|
||||||
|
private fun elementTypes(requiredType: Class<out UElement>?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_TYPES_LIST
|
||||||
|
|
||||||
|
private fun <T : UElement> Array<out Class<out T>>.nonEmptyOr(default: Array<out Class<out UElement>>) = takeIf { it.isNotEmpty() }
|
||||||
|
?: default
|
||||||
@@ -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<UAnnotation> = 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<UAnnotation> = 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<UAnnotation> = emptyList()
|
||||||
|
override val javaPsi: PsiElement? = null
|
||||||
|
override val sourcePsi: PsiElement? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createElvisExpressions(
|
||||||
|
left: KtExpression,
|
||||||
|
right: KtExpression,
|
||||||
|
containingElement: UElement?,
|
||||||
|
psiParent: PsiElement): List<UExpression> {
|
||||||
|
|
||||||
|
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<UAnnotation> = 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<UAnnotation> = emptyList()
|
||||||
|
override val expressions: List<UExpression> 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-5
@@ -18,10 +18,7 @@ package org.jetbrains.uast.kotlin
|
|||||||
|
|
||||||
import com.intellij.psi.PsiLanguageInjectionHost
|
import com.intellij.psi.PsiLanguageInjectionHost
|
||||||
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
|
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
|
||||||
import org.jetbrains.uast.UElement
|
import org.jetbrains.uast.*
|
||||||
import org.jetbrains.uast.UExpression
|
|
||||||
import org.jetbrains.uast.UPolyadicExpression
|
|
||||||
import org.jetbrains.uast.UastBinaryOperator
|
|
||||||
import org.jetbrains.uast.expressions.UInjectionHost
|
import org.jetbrains.uast.expressions.UInjectionHost
|
||||||
|
|
||||||
class KotlinStringTemplateUPolyadicExpression(
|
class KotlinStringTemplateUPolyadicExpression(
|
||||||
@@ -32,7 +29,15 @@ class KotlinStringTemplateUPolyadicExpression(
|
|||||||
KotlinUElementWithType,
|
KotlinUElementWithType,
|
||||||
KotlinEvaluatableUElement,
|
KotlinEvaluatableUElement,
|
||||||
UInjectionHost {
|
UInjectionHost {
|
||||||
override val operands: List<UExpression> by lz { psi.entries.map { KotlinConverter.convertEntry(it, this)!! } }
|
override val operands: List<UExpression> by lz {
|
||||||
|
psi.entries.map {
|
||||||
|
KotlinConverter.convertEntry(
|
||||||
|
it,
|
||||||
|
this,
|
||||||
|
DEFAULT_EXPRESSION_TYPES_LIST
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
override val operator = UastBinaryOperator.PLUS
|
override val operator = UastBinaryOperator.PLUS
|
||||||
|
|
||||||
override val psiLanguageInjectionHost: PsiLanguageInjectionHost get() = psi
|
override val psiLanguageInjectionHost: PsiLanguageInjectionHost get() = psi
|
||||||
|
|||||||
+52
@@ -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<ResolveResult> = getResolveResultVariants(psi.callableReference)
|
||||||
|
|
||||||
|
}
|
||||||
+41
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -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<UAnnotation>
|
||||||
|
get() = emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertParent(): UElement? {
|
||||||
|
val result = KotlinConverter.unwrapElements(psi.parent)?.let { parentUnwrapped ->
|
||||||
|
KotlinUastLanguagePlugin().convertElementWithParent(parentUnwrapped, null)
|
||||||
|
}
|
||||||
|
return (result as? KotlinUSwitchExpression)?.body ?: result
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -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<UVariable>
|
||||||
|
get() = emptyList()
|
||||||
|
|
||||||
|
override val hasResources: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override val tryIdentifier: UIdentifier
|
||||||
|
get() = KotlinUIdentifier(null, this)
|
||||||
|
|
||||||
|
override val finallyIdentifier: UIdentifier?
|
||||||
|
get() = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
UFile (package = )
|
||||||
|
UClass (name = Foo)
|
||||||
|
UClass (name = Bar)
|
||||||
|
UAnnotationMethod (name = Bar)
|
||||||
|
UAnnotationMethod (name = getAPlusB)
|
||||||
|
UClass (name = Baz)
|
||||||
|
UAnnotationMethod (name = doNothing)
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user