201: Uast: KotlinUastCodeGenerationPlugin on the level needed for ReactorSupport (KT-38062)
This commit is contained in:
+297
-25
@@ -6,15 +6,25 @@
|
||||
package org.jetbrains.uast.kotlin.generate
|
||||
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiClassType
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiType
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.resolveToKotlinType
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.uast.*
|
||||
@@ -22,6 +32,8 @@ import org.jetbrains.uast.generate.UParameterInfo
|
||||
import org.jetbrains.uast.generate.UastCodeGenerationPlugin
|
||||
import org.jetbrains.uast.generate.UastElementFactory
|
||||
import org.jetbrains.uast.kotlin.*
|
||||
import org.jetbrains.uast.kotlin.internal.KotlinFakeUElement
|
||||
import org.jetbrains.uast.kotlin.internal.toSourcePsiFakeAware
|
||||
|
||||
class KotlinUastCodeGenerationPlugin : UastCodeGenerationPlugin {
|
||||
override val language: Language
|
||||
@@ -31,7 +43,7 @@ class KotlinUastCodeGenerationPlugin : UastCodeGenerationPlugin {
|
||||
KotlinUastElementFactory(project)
|
||||
|
||||
override fun <T : UElement> replace(oldElement: UElement, newElement: T, elementType: Class<T>): T? {
|
||||
val oldPsi = oldElement.sourcePsi ?: return null
|
||||
val oldPsi = oldElement.toSourcePsiFakeAware().singleOrNull() ?: return null
|
||||
val newPsi = newElement.sourcePsi?.let {
|
||||
when {
|
||||
it is KtCallExpression && it.parent is KtQualifiedExpression -> it.parent
|
||||
@@ -42,9 +54,23 @@ class KotlinUastCodeGenerationPlugin : UastCodeGenerationPlugin {
|
||||
val psiFactory = KtPsiFactory(oldPsi.project)
|
||||
val oldParentPsi = oldPsi.parent
|
||||
val (updOldPsi, updNewPsi) = when {
|
||||
oldParentPsi is KtStringTemplateExpression && oldParentPsi.entries.size == 1 -> oldParentPsi to newPsi
|
||||
oldPsi is KtStringTemplateEntry && newPsi !is KtStringTemplateEntry && newPsi is KtExpression -> oldPsi to psiFactory.createBlockStringTemplateEntry(newPsi)
|
||||
else -> oldPsi to newPsi
|
||||
oldParentPsi is KtStringTemplateExpression && oldParentPsi.entries.size == 1 ->
|
||||
oldParentPsi to newPsi
|
||||
|
||||
oldPsi is KtStringTemplateEntry && newPsi !is KtStringTemplateEntry && newPsi is KtExpression ->
|
||||
oldPsi to psiFactory.createBlockStringTemplateEntry(newPsi)
|
||||
|
||||
oldPsi is KtBlockExpression && newPsi is KtBlockExpression -> {
|
||||
if (!hasBraces(oldPsi) && hasBraces(newPsi)) {
|
||||
oldPsi to psiFactory.createLambdaExpression("none", newPsi.statements.joinToString("\n") { "println()" }).bodyExpression!!.also {
|
||||
it.statements.zip(newPsi.statements).forEach { it.first.replace(it.second) }
|
||||
}
|
||||
} else
|
||||
oldPsi to newPsi
|
||||
}
|
||||
|
||||
else ->
|
||||
oldPsi to newPsi
|
||||
}
|
||||
|
||||
return when (val replaced = updOldPsi.replace(updNewPsi)?.safeAs<KtElement>()?.let { ShortenReferences.DEFAULT.process(it) }) {
|
||||
@@ -54,10 +80,18 @@ class KotlinUastCodeGenerationPlugin : UastCodeGenerationPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasBraces(oldPsi: KtBlockExpression): Boolean = oldPsi.lBrace != null && oldPsi.rBrace != null
|
||||
|
||||
class KotlinUastElementFactory(project: Project) : UastElementFactory {
|
||||
private val psiFactory = KtPsiFactory(project)
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createQualifiedReference(qualifiedName: String, context: UElement?): UQualifiedReferenceExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createQualifiedReference(qualifiedName, context?.sourcePsi)
|
||||
}
|
||||
|
||||
/*override*/ fun createQualifiedReference(qualifiedName: String, context: PsiElement?): UQualifiedReferenceExpression? {
|
||||
return psiFactory.createExpression(qualifiedName).let {
|
||||
when (it) {
|
||||
is KtDotQualifiedExpression -> KotlinUQualifiedReferenceExpression(it, null)
|
||||
@@ -68,21 +102,43 @@ class KotlinUastElementFactory(project: Project) : UastElementFactory {
|
||||
}
|
||||
}
|
||||
|
||||
override fun createCallExpression(receiver: UExpression?, methodName: String, parameters: List<UExpression>, expectedReturnType: PsiType?, kind: UastCallKind, context: PsiElement?): UCallExpression? {
|
||||
override fun createCallExpression(
|
||||
receiver: UExpression?,
|
||||
methodName: String,
|
||||
parameters: List<UExpression>,
|
||||
expectedReturnType: PsiType?,
|
||||
kind: UastCallKind,
|
||||
context: PsiElement?
|
||||
): UCallExpression? {
|
||||
if (kind != UastCallKind.METHOD_CALL) return null
|
||||
|
||||
val typeParams = (context as? KtElement)?.let { kontext ->
|
||||
val resolutionFacade = kontext.getResolutionFacade()
|
||||
(expectedReturnType as? PsiClassType)?.parameters?.map { it.resolveToKotlinType(resolutionFacade) }
|
||||
}
|
||||
|
||||
val name = methodName.quoteIfNeeded()
|
||||
val methodCall = psiFactory.createExpression(
|
||||
if (receiver != null) "a.$name()" else "$name()"
|
||||
buildString {
|
||||
if (receiver != null)
|
||||
append("a.")
|
||||
append(name)
|
||||
if (typeParams != null) {
|
||||
append(typeParams.joinToString(", ", "<", ">") { type ->
|
||||
type.fqName?.asString() ?: ""
|
||||
})
|
||||
}
|
||||
append("()")
|
||||
}
|
||||
).getPossiblyQualifiedCallExpression() ?: return null
|
||||
|
||||
if (receiver != null) {
|
||||
methodCall.parent.safeAs<KtDotQualifiedExpression>()?.receiverExpression?.replace(receiver.sourcePsi!!)
|
||||
methodCall.parent.safeAs<KtDotQualifiedExpression>()?.receiverExpression?.replace(wrapULiteral(receiver).sourcePsi!!)
|
||||
}
|
||||
|
||||
val valueArgumentList = methodCall.valueArgumentList
|
||||
for (parameter in parameters) {
|
||||
valueArgumentList?.addArgument(psiFactory.createArgument(parameter.sourcePsi as? KtExpression))
|
||||
valueArgumentList?.addArgument(psiFactory.createArgument(wrapULiteral(parameter).sourcePsi as KtExpression))
|
||||
}
|
||||
|
||||
return KotlinUFunctionCallExpression(methodCall, null)
|
||||
@@ -93,65 +149,281 @@ class KotlinUastElementFactory(project: Project) : UastElementFactory {
|
||||
return KotlinStringULiteralExpression(psiFactory.createExpression(StringUtil.wrapWithDoubleQuote(text)), null)
|
||||
}
|
||||
|
||||
/*override*/ fun createNullLiteral(context: PsiElement?): ULiteralExpression {
|
||||
return psiFactory.createExpression("null").toUElementOfType<ULiteralExpression>()!!
|
||||
}
|
||||
|
||||
/*override*/ fun createIntLiteral(value: Int, context: PsiElement?): ULiteralExpression {
|
||||
return psiFactory.createExpression(value.toString()).toUElementOfType<ULiteralExpression>()!!
|
||||
}
|
||||
|
||||
private fun KtExpression.ensureBlockExpressionBraces(): KtExpression {
|
||||
if (this !is KtBlockExpression || hasBraces(this)) return this
|
||||
val blockExpression = psiFactory.createBlock(this.statements.joinToString("\n") { "println()" })
|
||||
for ((placeholder, statement) in blockExpression.statements.zip(this.statements)) {
|
||||
placeholder.replace(statement)
|
||||
}
|
||||
return blockExpression
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createIfExpression(condition: UExpression, thenBranch: UExpression, elseBranch: UExpression?): UIfExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createIfExpression(condition, thenBranch, elseBranch, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createIfExpression(
|
||||
condition: UExpression,
|
||||
thenBranch: UExpression,
|
||||
elseBranch: UExpression?,
|
||||
context: PsiElement?
|
||||
): UIfExpression? {
|
||||
val conditionPsi = condition.sourcePsi as? KtExpression ?: return null
|
||||
val thenBranchPsi = thenBranch.sourcePsi as? KtExpression ?: return null
|
||||
val elseBranchPsi = elseBranch?.sourcePsi as? KtExpression
|
||||
|
||||
return KotlinUIfExpression(psiFactory.createIf(conditionPsi, thenBranchPsi, elseBranchPsi), null)
|
||||
return KotlinUIfExpression(psiFactory.createIf(conditionPsi, thenBranchPsi.ensureBlockExpressionBraces(), elseBranchPsi?.ensureBlockExpressionBraces()), null)
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createParenthesizedExpression(expression: UExpression): UParenthesizedExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createParenthesizedExpression(expression, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createParenthesizedExpression(expression: UExpression, context: PsiElement?): UParenthesizedExpression? {
|
||||
val source = expression.sourcePsi ?: return null
|
||||
val parenthesized = psiFactory.createExpression("(${source.text})") as? KtParenthesizedExpression ?: return null
|
||||
return KotlinUParenthesizedExpression(parenthesized, null)
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createSimpleReference(name: String): USimpleNameReferenceExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createSimpleReference(name, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createSimpleReference(name: String, context: PsiElement?): USimpleNameReferenceExpression? {
|
||||
return KotlinUSimpleReferenceExpression(psiFactory.createSimpleName(name), null)
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createSimpleReference(variable: UVariable): USimpleNameReferenceExpression? {
|
||||
return createSimpleReference(variable.name ?: return null)
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createSimpleReference(variable, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createSimpleReference(variable: UVariable, context: PsiElement?): USimpleNameReferenceExpression? {
|
||||
return createSimpleReference(variable.name ?: return null, context)
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createReturnExpresion(expression: UExpression?, inLambda: Boolean): UReturnExpression? {
|
||||
val returnExpression = psiFactory.createExpression("return") as KtReturnExpression
|
||||
expression?.sourcePsi?.let { returnExpression.add(it) }
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createReturnExpresion(expression, inLambda, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createReturnExpresion(expression: UExpression?, inLambda: Boolean, context: PsiElement?): UReturnExpression? {
|
||||
val label = if (inLambda && context != null) getParentLambdaLabelName(context)?.let { "@$it" } ?: "" else ""
|
||||
val returnExpression = psiFactory.createExpression("return$label 1") as KtReturnExpression
|
||||
val sourcePsi = expression?.sourcePsi
|
||||
if (sourcePsi != null) {
|
||||
returnExpression.returnedExpression!!.replace(sourcePsi)
|
||||
} else {
|
||||
returnExpression.returnedExpression?.delete()
|
||||
}
|
||||
return KotlinUReturnExpression(returnExpression, null)
|
||||
}
|
||||
|
||||
override fun createBinaryExpression(leftOperand: UExpression, rightOperand: UExpression, operator: UastBinaryOperator): UBinaryExpression? {
|
||||
private fun getParentLambdaLabelName(context: PsiElement): String? {
|
||||
val lambdaExpression = context.getParentOfType<KtLambdaExpression>(false) ?: return null
|
||||
lambdaExpression.parent.safeAs<KtLabeledExpression>()?.let { return it.getLabelName() }
|
||||
val callExpression = lambdaExpression.getStrictParentOfType<KtCallExpression>() ?: return null
|
||||
callExpression.valueArguments.find {
|
||||
it.getArgumentExpression()?.unpackFunctionLiteral(allowParentheses = false) === lambdaExpression
|
||||
} ?: return null
|
||||
return callExpression.getCallNameExpression()?.text
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createBinaryExpression(
|
||||
leftOperand: UExpression,
|
||||
rightOperand: UExpression,
|
||||
operator: UastBinaryOperator
|
||||
): UBinaryExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createBinaryExpression(leftOperand, rightOperand, operator, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createBinaryExpression(
|
||||
leftOperand: UExpression,
|
||||
rightOperand: UExpression,
|
||||
operator: UastBinaryOperator,
|
||||
context: PsiElement?
|
||||
): UBinaryExpression? {
|
||||
val binaryExpression = joinBinaryExpression(leftOperand, rightOperand, operator) ?: return null
|
||||
return KotlinUBinaryExpression(binaryExpression, null)
|
||||
}
|
||||
|
||||
private fun joinBinaryExpression(
|
||||
leftOperand: UExpression,
|
||||
rightOperand: UExpression,
|
||||
operator: UastBinaryOperator
|
||||
): KtBinaryExpression? {
|
||||
val leftPsi = leftOperand.sourcePsi ?: return null
|
||||
val rightPsi = rightOperand.sourcePsi ?: return null
|
||||
|
||||
val binaryExpression = psiFactory.createExpression("a ${operator.text} b") as? KtBinaryExpression ?: return null
|
||||
binaryExpression.left?.replace(leftPsi)
|
||||
binaryExpression.right?.replace(rightPsi)
|
||||
return KotlinUBinaryExpression(binaryExpression, null)
|
||||
return binaryExpression
|
||||
}
|
||||
|
||||
override fun createFlatBinaryExpression(leftOperand: UExpression, rightOperand: UExpression, operator: UastBinaryOperator): UPolyadicExpression? {
|
||||
return createBinaryExpression(leftOperand, rightOperand, operator)
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createFlatBinaryExpression(
|
||||
leftOperand: UExpression,
|
||||
rightOperand: UExpression,
|
||||
operator: UastBinaryOperator
|
||||
): UPolyadicExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createFlatBinaryExpression(leftOperand, rightOperand, operator, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createFlatBinaryExpression(
|
||||
leftOperand: UExpression,
|
||||
rightOperand: UExpression,
|
||||
operator: UastBinaryOperator,
|
||||
context: PsiElement?
|
||||
): UPolyadicExpression? {
|
||||
|
||||
fun unwrapParentheses(exp: KtExpression?) {
|
||||
if (exp !is KtParenthesizedExpression) return
|
||||
if (!KtPsiUtil.areParenthesesUseless(exp)) return
|
||||
exp.expression?.let { exp.replace(it) }
|
||||
}
|
||||
|
||||
val binaryExpression = joinBinaryExpression(leftOperand, rightOperand, operator) ?: return null
|
||||
unwrapParentheses(binaryExpression.left)
|
||||
unwrapParentheses(binaryExpression.right)
|
||||
|
||||
return psiFactory.createExpression(binaryExpression.text).toUElementOfType()!!
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createBlockExpression(expressions: List<UExpression>): UBlockExpression? {
|
||||
if (expressions.any { it.sourcePsi == null}) return null
|
||||
val block = psiFactory.createBlock(expressions.joinToString(separator = "\n") { it.sourcePsi?.text ?: "" })
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createBlockExpression(expressions, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createBlockExpression(expressions: List<UExpression>, context: PsiElement?): UBlockExpression? {
|
||||
val sourceExpressions = expressions.flatMap { it.toSourcePsiFakeAware() }
|
||||
val block = psiFactory.createBlock(
|
||||
sourceExpressions.joinToString(separator = "\n") { "println()" }
|
||||
)
|
||||
for ((placeholder, psiElement) in block.statements.zip(sourceExpressions)) {
|
||||
placeholder.replace(psiElement)
|
||||
}
|
||||
return KotlinUBlockExpression(block, null)
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createDeclarationExpression(declarations: List<UDeclaration>): UDeclarationsExpression? {
|
||||
return object : KotlinUDeclarationsExpression(null) {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createDeclarationExpression(declarations, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createDeclarationExpression(declarations: List<UDeclaration>, context: PsiElement?): UDeclarationsExpression? {
|
||||
return object : KotlinUDeclarationsExpression(null), KotlinFakeUElement {
|
||||
override var declarations: List<UDeclaration> = declarations
|
||||
override fun unwrapToSourcePsi(): List<PsiElement> = declarations.flatMap { it.toSourcePsiFakeAware() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun createLambdaExpression(parameters: List<UParameterInfo>, body: UExpression): ULambdaExpression? {
|
||||
TODO("Not yet implemented")
|
||||
/*override*/ fun createLambdaExpression(
|
||||
parameters: List<UParameterInfo>,
|
||||
body: UExpression,
|
||||
context: PsiElement?
|
||||
): ULambdaExpression? {
|
||||
val resolutionFacade = (context as? KtElement)?.getResolutionFacade()
|
||||
val validator = (context as? KtElement)?.let { usedNamesFilter(it) } ?: { true }
|
||||
|
||||
val newLambdaStatements = if (body is UBlockExpression) {
|
||||
body.expressions.flatMap { member ->
|
||||
when {
|
||||
member is UReturnExpression -> member.returnExpression?.toSourcePsiFakeAware().orEmpty()
|
||||
else -> member.toSourcePsiFakeAware()
|
||||
}
|
||||
}
|
||||
} else
|
||||
listOf(body.sourcePsi!!)
|
||||
|
||||
val ktLambdaExpression = psiFactory.createLambdaExpression(
|
||||
parameters.joinToString(", ") { p ->
|
||||
val ktype = resolutionFacade?.let { p.type?.resolveToKotlinType(it) }
|
||||
StringBuilder().apply {
|
||||
append(p.suggestedName ?: ktype?.let { KotlinNameSuggester.suggestNamesByType(it, validator).firstOrNull() })
|
||||
?: KotlinNameSuggester.suggestNameByName("v", validator)
|
||||
ktype?.fqName?.toString()?.let { append(": ").append(it) }
|
||||
}
|
||||
},
|
||||
newLambdaStatements.joinToString("\n") { "placeholder" }
|
||||
)
|
||||
|
||||
for ((old, new) in ktLambdaExpression.bodyExpression!!.statements.zip(newLambdaStatements)) {
|
||||
old.replace(new)
|
||||
}
|
||||
|
||||
return ktLambdaExpression.toUElementOfType()!!
|
||||
}
|
||||
|
||||
override fun createLocalVariable(suggestedName: String?, type: PsiType?, initializer: UExpression, immutable: Boolean): ULocalVariable? {
|
||||
TODO("Not yet implemented")
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createLambdaExpression(parameters: List<UParameterInfo>, body: UExpression): ULambdaExpression? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createLambdaExpression(parameters, body, null)
|
||||
}
|
||||
|
||||
/*override*/ fun createLocalVariable(
|
||||
suggestedName: String?,
|
||||
type: PsiType?,
|
||||
initializer: UExpression,
|
||||
immutable: Boolean,
|
||||
context: PsiElement?
|
||||
): ULocalVariable? {
|
||||
val resolutionFacade = (context as? KtElement)?.getResolutionFacade()
|
||||
val validator = (context as? KtElement)?.let { usedNamesFilter(it) } ?: { true }
|
||||
val ktype = resolutionFacade?.let { type?.resolveToKotlinType(it) }
|
||||
|
||||
val function = psiFactory.createFunction(
|
||||
buildString {
|
||||
append("fun foo() { ")
|
||||
append(if (immutable) "val" else "var")
|
||||
append(" ")
|
||||
append(suggestedName ?: ktype?.let { KotlinNameSuggester.suggestNamesByType(it, validator).firstOrNull() })
|
||||
?: KotlinNameSuggester.suggestNameByName("v", validator)
|
||||
ktype?.fqName?.toString()?.let { append(": ").append(it) }
|
||||
append(" = null")
|
||||
append("}")
|
||||
}
|
||||
)
|
||||
|
||||
val ktVariable = PsiTreeUtil.findChildOfType(function, KtVariableDeclaration::class.java)!!
|
||||
val newVariable = ktVariable.initializer!!.replace(initializer.sourcePsi!!).parent
|
||||
return newVariable.toUElementOfType<UVariable>() as ULocalVariable
|
||||
}
|
||||
|
||||
@Deprecated("use version with context parameter")
|
||||
override fun createLocalVariable(
|
||||
suggestedName: String?,
|
||||
type: PsiType?,
|
||||
initializer: UExpression,
|
||||
immutable: Boolean
|
||||
): ULocalVariable? {
|
||||
logger<KotlinUastElementFactory>().error("Please switch caller to the version with a context parameter")
|
||||
return createLocalVariable(suggestedName, type, initializer, immutable, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun usedNamesFilter(context: KtElement): (String) -> Boolean {
|
||||
val scope = context.getResolutionScope()
|
||||
return { name: String -> scope.findClassifier(Name.identifier(name), NoLookupLocation.FROM_IDE) == null }
|
||||
}
|
||||
+13
-7
@@ -23,22 +23,26 @@ import org.jetbrains.kotlin.psi.KtConstantExpression
|
||||
import org.jetbrains.kotlin.psi.KtEscapeStringTemplateEntry
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.ULiteralExpression
|
||||
import org.jetbrains.uast.kotlin.internal.KotlinFakeUElement
|
||||
import org.jetbrains.uast.wrapULiteral
|
||||
|
||||
class KotlinULiteralExpression(
|
||||
override val sourcePsi: KtConstantExpression,
|
||||
givenParent: UElement?
|
||||
) : KotlinAbstractUExpression(givenParent), ULiteralExpression, KotlinUElementWithType, KotlinEvaluatableUElement {
|
||||
override val sourcePsi: KtConstantExpression,
|
||||
givenParent: UElement?
|
||||
) : KotlinAbstractUExpression(givenParent), ULiteralExpression, KotlinUElementWithType, KotlinEvaluatableUElement, KotlinFakeUElement {
|
||||
override val isNull: Boolean
|
||||
get() = sourcePsi.unwrapBlockOrParenthesis().node?.elementType == KtNodeTypes.NULL
|
||||
|
||||
override val value by lz { evaluate() }
|
||||
|
||||
override fun unwrapToSourcePsi(): List<PsiElement> = listOfNotNull(wrapULiteral(this).sourcePsi)
|
||||
}
|
||||
|
||||
class KotlinStringULiteralExpression(
|
||||
override val sourcePsi: PsiElement,
|
||||
givenParent: UElement?,
|
||||
val text: String
|
||||
) : KotlinAbstractUExpression(givenParent), ULiteralExpression, KotlinUElementWithType{
|
||||
override val sourcePsi: PsiElement,
|
||||
givenParent: UElement?,
|
||||
val text: String
|
||||
) : KotlinAbstractUExpression(givenParent), ULiteralExpression, KotlinUElementWithType, KotlinFakeUElement {
|
||||
constructor(psi: PsiElement, uastParent: UElement?)
|
||||
: this(psi, uastParent, if (psi is KtEscapeStringTemplateEntry) psi.unescapedValue else psi.text)
|
||||
|
||||
@@ -48,4 +52,6 @@ class KotlinStringULiteralExpression(
|
||||
override fun evaluate() = value
|
||||
|
||||
override fun getExpressionType(): PsiType? = PsiType.getJavaLangString(sourcePsi.manager, sourcePsi.resolveScope)
|
||||
|
||||
override fun unwrapToSourcePsi(): List<PsiElement> = listOfNotNull(wrapULiteral(this).sourcePsi)
|
||||
}
|
||||
|
||||
+7
-1
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.psi.KtReturnExpression
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.UExpression
|
||||
import org.jetbrains.uast.UReturnExpression
|
||||
import org.jetbrains.uast.kotlin.internal.KotlinFakeUElement
|
||||
import org.jetbrains.uast.kotlin.internal.toSourcePsiFakeAware
|
||||
|
||||
class KotlinUReturnExpression(
|
||||
override val sourcePsi: KtReturnExpression,
|
||||
@@ -31,11 +33,15 @@ class KotlinUReturnExpression(
|
||||
|
||||
class KotlinUImplicitReturnExpression(
|
||||
givenParent: UElement?
|
||||
) : KotlinAbstractUExpression(givenParent), UReturnExpression, KotlinUElementWithType {
|
||||
) : KotlinAbstractUExpression(givenParent), UReturnExpression, KotlinUElementWithType, KotlinFakeUElement {
|
||||
override val psi: PsiElement?
|
||||
get() = null
|
||||
|
||||
override lateinit var returnExpression: UExpression
|
||||
internal set
|
||||
|
||||
override fun unwrapToSourcePsi(): List<PsiElement> {
|
||||
return returnExpression.toSourcePsiFakeAware()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.kotlin.internal
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.uast.UElement
|
||||
|
||||
interface KotlinFakeUElement {
|
||||
fun unwrapToSourcePsi(): List<PsiElement>
|
||||
}
|
||||
|
||||
fun UElement.toSourcePsiFakeAware(): List<PsiElement> {
|
||||
if (this is KotlinFakeUElement) return this.unwrapToSourcePsi()
|
||||
return listOfNotNull(this.sourcePsi)
|
||||
}
|
||||
@@ -0,0 +1,938 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.expressions.UInjectionHost
|
||||
import org.jetbrains.uast.generate.UParameterInfo
|
||||
import org.jetbrains.uast.generate.UastCodeGenerationPlugin
|
||||
import org.jetbrains.uast.generate.refreshed
|
||||
import org.jetbrains.uast.generate.replace
|
||||
import org.jetbrains.uast.kotlin.generate.KotlinUastElementFactory
|
||||
import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi
|
||||
import org.jetbrains.uast.test.env.kotlin.findUElementByTextFromPsi
|
||||
import kotlin.test.fail as kfail
|
||||
|
||||
class KotlinUastGenerationTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor =
|
||||
KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
|
||||
private val psiFactory
|
||||
get() = KtPsiFactory(project)
|
||||
private val generatePlugin: UastCodeGenerationPlugin
|
||||
get() = UastCodeGenerationPlugin.byLanguage(KotlinLanguage.INSTANCE)!!
|
||||
private val uastElementFactory
|
||||
get() = generatePlugin.getElementFactory(myFixture.project) as KotlinUastElementFactory
|
||||
|
||||
fun `test logical and operation with simple operands`() {
|
||||
val left = psiFactory.createExpression("true").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot create left UExpression")
|
||||
val right = psiFactory.createExpression("false").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot create right UExpression")
|
||||
|
||||
val expression = uastElementFactory.createBinaryExpression(left, right, UastBinaryOperator.LOGICAL_AND, dummyContextFile())
|
||||
?: kfail("Cannot create expression")
|
||||
|
||||
TestCase.assertEquals("true && false", expression.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test logical and operation with simple operands with parenthesis`() {
|
||||
val left = psiFactory.createExpression("(true)").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot create left UExpression")
|
||||
val right = psiFactory.createExpression("(false)").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot create right UExpression")
|
||||
|
||||
val expression = uastElementFactory.createFlatBinaryExpression(left, right, UastBinaryOperator.LOGICAL_AND, dummyContextFile())
|
||||
?: kfail("Cannot create expression")
|
||||
|
||||
TestCase.assertEquals("true && false", expression.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
UBinaryExpression (operator = &&)
|
||||
ULiteralExpression (value = true)
|
||||
ULiteralExpression (value = false)
|
||||
""".trimIndent(), expression.putIntoFunctionBody().asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test logical and operation with simple operands with parenthesis polyadic`() {
|
||||
val left = psiFactory.createExpression("(true && false)").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot create left UExpression")
|
||||
val right = psiFactory.createExpression("(false)").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot create right UExpression")
|
||||
|
||||
val expression = uastElementFactory.createFlatBinaryExpression(left, right, UastBinaryOperator.LOGICAL_AND, dummyContextFile())
|
||||
?: kfail("Cannot create expression")
|
||||
|
||||
TestCase.assertEquals("true && false && false", expression.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
UBinaryExpression (operator = &&)
|
||||
UBinaryExpression (operator = &&)
|
||||
ULiteralExpression (value = null)
|
||||
ULiteralExpression (value = null)
|
||||
ULiteralExpression (value = null)
|
||||
""".trimIndent(), expression.asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test simple reference creating from variable`() {
|
||||
val context = dummyContextFile()
|
||||
val variable = uastElementFactory.createLocalVariable(
|
||||
"a", PsiType.INT, uastElementFactory.createNullLiteral(context), false, context
|
||||
) ?: kfail("cannot create variable")
|
||||
|
||||
val reference = uastElementFactory.createSimpleReference(variable, context) ?: kfail("cannot create reference")
|
||||
TestCase.assertEquals("a", reference.identifier)
|
||||
}
|
||||
|
||||
fun `test simple reference by name`() {
|
||||
val reference = uastElementFactory.createSimpleReference("a", dummyContextFile()) ?: kfail("cannot create reference")
|
||||
TestCase.assertEquals("a", reference.identifier)
|
||||
}
|
||||
|
||||
fun `test parenthesised expression`() {
|
||||
val expression = psiFactory.createExpression("a + b").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create expression")
|
||||
val parenthesizedExpression = uastElementFactory.createParenthesizedExpression(expression, dummyContextFile())
|
||||
?: kfail("cannot create parenthesized expression")
|
||||
|
||||
TestCase.assertEquals("(a + b)", parenthesizedExpression.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test return expression`() {
|
||||
val expression = psiFactory.createExpression("a + b").toUElementOfType<UExpression>()
|
||||
?: kfail("Cannot find plugin")
|
||||
|
||||
val returnExpression = uastElementFactory.createReturnExpresion(expression, false, dummyContextFile()) ?: kfail("cannot create return expression")
|
||||
TestCase.assertEquals("a + b", returnExpression.returnExpression?.asRenderString())
|
||||
TestCase.assertEquals("return a + b", returnExpression.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test variable declaration without type`() {
|
||||
val expression = psiFactory.createExpression("1 + 2").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create variable declaration")
|
||||
|
||||
val declaration = uastElementFactory.createLocalVariable("a", null, expression, false, dummyContextFile()) ?: kfail("cannot create variable")
|
||||
|
||||
TestCase.assertEquals("var a = 1 + 2", declaration.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test variable declaration with type`() {
|
||||
val expression = psiFactory.createExpression("b").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create variable declaration")
|
||||
|
||||
val declaration = uastElementFactory.createLocalVariable("a", PsiType.DOUBLE, expression, false, dummyContextFile()) ?: kfail("cannot create variable")
|
||||
|
||||
TestCase.assertEquals("var a: kotlin.Double = b", declaration.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test final variable declaration`() {
|
||||
val expression = psiFactory.createExpression("b").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create variable declaration")
|
||||
|
||||
val declaration = uastElementFactory.createLocalVariable("a", PsiType.DOUBLE, expression, true, dummyContextFile())
|
||||
?: kfail("cannot create variable")
|
||||
|
||||
TestCase.assertEquals("val a: kotlin.Double = b", declaration.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test final variable declaration with unique name`() {
|
||||
val expression = psiFactory.createExpression("b").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create variable declaration")
|
||||
|
||||
val declaration = uastElementFactory.createLocalVariable("a", PsiType.DOUBLE, expression, true, dummyContextFile())
|
||||
?: kfail("cannot create variable")
|
||||
|
||||
TestCase.assertEquals("val a: kotlin.Double = b", declaration.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
ULocalVariable (name = a)
|
||||
USimpleNameReferenceExpression (identifier = b)
|
||||
""".trimIndent(), declaration.asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test block expression`() {
|
||||
val statement1 = psiFactory.createExpression("System.out.println()").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create statement")
|
||||
val statement2 = psiFactory.createExpression("System.out.println(2)").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create statement")
|
||||
|
||||
val block = uastElementFactory.createBlockExpression(listOf(statement1, statement2), dummyContextFile()) ?: kfail("cannot create block")
|
||||
|
||||
TestCase.assertEquals("""
|
||||
{
|
||||
System.out.println()
|
||||
System.out.println(2)
|
||||
}
|
||||
""".trimIndent(), block.sourcePsi?.text
|
||||
)
|
||||
}
|
||||
|
||||
fun `test lambda expression`() {
|
||||
val statement = psiFactory.createExpression("System.out.println()").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create statement")
|
||||
|
||||
val lambda = uastElementFactory.createLambdaExpression(
|
||||
listOf(
|
||||
UParameterInfo(PsiType.INT, "a"),
|
||||
UParameterInfo(null, "b")
|
||||
),
|
||||
statement,
|
||||
dummyContextFile()
|
||||
) ?: kfail("cannot create lambda")
|
||||
|
||||
TestCase.assertEquals("{ a: kotlin.Int, b -> System.out.println() }", lambda.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
ULambdaExpression
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = b)
|
||||
UAnnotation (fqName = null)
|
||||
UBlockExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = System)
|
||||
USimpleNameReferenceExpression (identifier = out)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
""".trimIndent(), lambda.putIntoFunctionBody().asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
private fun UExpression.putIntoFunctionBody(): UExpression {
|
||||
val file = myFixture.configureByText("dummyFile.kt", "fun foo() { TODO() }") as KtFile
|
||||
val ktFunction = file.declarations.single { it.name == "foo" } as KtFunction
|
||||
val uMethod = ktFunction.toUElementOfType<UMethod>()!!
|
||||
return runWriteCommand {
|
||||
uMethod.uastBody.cast<UBlockExpression>().expressions.single().replace(this)!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : UExpression> T.putIntoVarInitializer(): T {
|
||||
val file = myFixture.configureByText("dummyFile.kt", "val foo = TODO()") as KtFile
|
||||
val ktFunction = file.declarations.single { it.name == "foo" } as KtProperty
|
||||
val uMethod = ktFunction.toUElementOfType<UVariable>()!!
|
||||
return runWriteCommand {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
generatePlugin.replace(uMethod.uastInitializer!!, this, UExpression::class.java) as T
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : UExpression> runWriteCommand(uExpression: () -> T): T {
|
||||
val result = Ref<T>()
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
result.set(uExpression())
|
||||
}
|
||||
return result.get()
|
||||
}
|
||||
|
||||
fun `test lambda expression with explicit types`() {
|
||||
val statement = psiFactory.createExpression("System.out.println()").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create statement")
|
||||
|
||||
val lambda = uastElementFactory.createLambdaExpression(
|
||||
listOf(
|
||||
UParameterInfo(PsiType.INT, "a"),
|
||||
UParameterInfo(PsiType.DOUBLE, "b")
|
||||
),
|
||||
statement,
|
||||
dummyContextFile()
|
||||
) ?: kfail("cannot create lambda")
|
||||
|
||||
TestCase.assertEquals("{ a: kotlin.Int, b: kotlin.Double -> System.out.println() }", lambda.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
ULambdaExpression
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = b)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UBlockExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = System)
|
||||
USimpleNameReferenceExpression (identifier = out)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
""".trimIndent(), lambda.putIntoFunctionBody().asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test lambda expression with simplified block body with context`() {
|
||||
val r = psiFactory.createExpression("return \"10\"").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create return")
|
||||
|
||||
val block = uastElementFactory.createBlockExpression(listOf(r), dummyContextFile()) ?: kfail("cannot create block")
|
||||
|
||||
val lambda = uastElementFactory.createLambdaExpression(listOf(UParameterInfo(null, "a")), block, dummyContextFile())
|
||||
?: kfail("cannot create lambda")
|
||||
TestCase.assertEquals("""{ a -> "10" }""".trimMargin(), lambda.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
ULambdaExpression
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UBlockExpression
|
||||
UReturnExpression
|
||||
ULiteralExpression (value = "10")
|
||||
""".trimIndent(), lambda.putIntoVarInitializer().asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test function argument replacement`() {
|
||||
|
||||
val file = myFixture.configureByText(
|
||||
"test.kt", """
|
||||
fun f(a: Any){}
|
||||
|
||||
fun main(){
|
||||
f(a)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val expression = file.findUElementByTextFromPsi<UCallExpression>("f(a)")
|
||||
val newArgument = psiFactory.createExpression("b").toUElementOfType<USimpleNameReferenceExpression>()
|
||||
?: kfail("cannot create reference")
|
||||
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
TestCase.assertNotNull(expression.valueArguments[0].replace(newArgument))
|
||||
}
|
||||
|
||||
val updated = expression.refreshed() ?: kfail("cannot update expression")
|
||||
TestCase.assertEquals("f", updated.methodName)
|
||||
TestCase.assertTrue(updated.valueArguments[0] is USimpleNameReferenceExpression)
|
||||
TestCase.assertEquals("b", (updated.valueArguments[0] as USimpleNameReferenceExpression).identifier)
|
||||
TestCase.assertEquals("""
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (f))
|
||||
USimpleNameReferenceExpression (identifier = f, resolvesTo = null)
|
||||
USimpleNameReferenceExpression (identifier = b)
|
||||
""".trimIndent(), updated.asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test suggested name`() {
|
||||
val expression = psiFactory.createExpression("f(a) + 1").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create expression")
|
||||
val variable = uastElementFactory.createLocalVariable(null, PsiType.INT, expression, true, dummyContextFile())
|
||||
?: kfail("cannot create variable")
|
||||
|
||||
TestCase.assertEquals("val i: kotlin.Int = f(a) + 1", variable.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
ULocalVariable (name = i)
|
||||
UBinaryExpression (operator = +)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (f))
|
||||
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
ULiteralExpression (value = null)
|
||||
""".trimIndent(), variable.asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test method call generation with receiver`() {
|
||||
val receiver = psiFactory.createExpression(""""10"""").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create receiver")
|
||||
val arg1 = psiFactory.createExpression("1").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create arg1")
|
||||
val arg2 = psiFactory.createExpression("2").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create arg2")
|
||||
val methodCall = uastElementFactory.createCallExpression(
|
||||
receiver,
|
||||
"substring",
|
||||
listOf(arg1, arg2),
|
||||
null,
|
||||
UastCallKind.METHOD_CALL
|
||||
) ?: kfail("cannot create call")
|
||||
|
||||
TestCase.assertEquals(""""10".substring(1,2)""", methodCall.uastParent?.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
UQualifiedReferenceExpression
|
||||
ULiteralExpression (value = "10")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (substring))
|
||||
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
|
||||
ULiteralExpression (value = null)
|
||||
ULiteralExpression (value = null)
|
||||
""".trimIndent(), methodCall.uastParent?.asRecursiveLogString()?.trim()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test method call generation without receiver`() {
|
||||
val arg1 = psiFactory.createExpression("1").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create arg1")
|
||||
val arg2 = psiFactory.createExpression("2").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create arg2")
|
||||
val methodCall = uastElementFactory.createCallExpression(
|
||||
null,
|
||||
"substring",
|
||||
listOf(arg1, arg2),
|
||||
null,
|
||||
UastCallKind.METHOD_CALL
|
||||
) ?: kfail("cannot create call")
|
||||
|
||||
TestCase.assertEquals("""substring(1,2)""", methodCall.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test method call generation with generics restoring`() {
|
||||
val arrays = psiFactory.createExpression("java.util.Arrays").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create receiver")
|
||||
val methodCall = uastElementFactory.createCallExpression(
|
||||
arrays,
|
||||
"asList",
|
||||
listOf(),
|
||||
createTypeFromText("java.util.List<java.lang.String>", null),
|
||||
UastCallKind.METHOD_CALL,
|
||||
dummyContextFile()
|
||||
) ?: kfail("cannot create call")
|
||||
TestCase.assertEquals("java.util.Arrays.asList<kotlin.String>()", methodCall.uastParent?.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test method call generation with generics restoring 2 parameters`() {
|
||||
val collections = psiFactory.createExpression("java.util.Collections").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create receiver")
|
||||
TestCase.assertEquals("java.util.Collections", collections.asRenderString())
|
||||
val methodCall = uastElementFactory.createCallExpression(
|
||||
collections,
|
||||
"emptyMap",
|
||||
listOf(),
|
||||
createTypeFromText(
|
||||
"java.util.Map<java.lang.String, java.lang.Integer>",
|
||||
null
|
||||
),
|
||||
UastCallKind.METHOD_CALL,
|
||||
dummyContextFile()
|
||||
) ?: kfail("cannot create call")
|
||||
TestCase.assertEquals("emptyMap<kotlin.String, kotlin.Int>()", methodCall.sourcePsi?.text)
|
||||
TestCase.assertEquals("java.util.Collections.emptyMap<kotlin.String, kotlin.Int>()", methodCall.sourcePsi?.parent?.text)
|
||||
TestCase.assertEquals(
|
||||
"""
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = java)
|
||||
USimpleNameReferenceExpression (identifier = util)
|
||||
USimpleNameReferenceExpression (identifier = Collections)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
|
||||
UIdentifier (Identifier (emptyMap))
|
||||
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
|
||||
""".trimIndent(), methodCall.uastParent?.asRecursiveLogString()?.trim()
|
||||
)
|
||||
}
|
||||
|
||||
private fun dummyContextFile(): KtFile = myFixture.configureByText("file.kt", "fun foo() {}") as KtFile
|
||||
|
||||
fun `test method call generation with generics restoring 1 parameter with 1 existing`() {
|
||||
val a = psiFactory.createExpression("A").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create a receiver")
|
||||
val param = psiFactory.createExpression("\"a\"").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create a parameter")
|
||||
val methodCall = uastElementFactory.createCallExpression(
|
||||
a,
|
||||
"kek",
|
||||
listOf(param),
|
||||
createTypeFromText(
|
||||
"java.util.Map<java.lang.String, java.lang.Integer>",
|
||||
null
|
||||
),
|
||||
UastCallKind.METHOD_CALL,
|
||||
dummyContextFile()
|
||||
) ?: kfail("cannot create call")
|
||||
|
||||
TestCase.assertEquals("A.kek<kotlin.String, kotlin.Int>(\"a\")", methodCall.sourcePsi?.parent?.text)
|
||||
TestCase.assertEquals("""
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = A)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (kek))
|
||||
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
|
||||
ULiteralExpression (value = "a")
|
||||
""".trimIndent(), methodCall.uastParent?.asRecursiveLogString()?.trim())
|
||||
}
|
||||
|
||||
|
||||
//not implemented (currently we dont perform resolve in code generating)
|
||||
fun `ignore method call generation with generics restoring 1 parameter with 1 unused `() {
|
||||
val aClassFile = myFixture.configureByText("A.kt",
|
||||
"""
|
||||
object A {
|
||||
fun <T1, T2, T3> kek(a: T1): Map<T1, T3> {
|
||||
return TODO();
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
val a = psiFactory.createExpression("A").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create a receiver")
|
||||
val param = psiFactory.createExpression("\"a\"").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create a parameter")
|
||||
val methodCall = uastElementFactory.createCallExpression(
|
||||
a,
|
||||
"kek",
|
||||
listOf(param),
|
||||
createTypeFromText(
|
||||
"java.util.Map<java.lang.String, java.lang.Integer>",
|
||||
null
|
||||
),
|
||||
UastCallKind.METHOD_CALL,
|
||||
aClassFile
|
||||
) ?: kfail("cannot create call")
|
||||
|
||||
TestCase.assertEquals("A.<String, Object, Integer>kek(\"a\")", methodCall.sourcePsi?.text)
|
||||
}
|
||||
|
||||
|
||||
fun `test method call generation with generics with context`() {
|
||||
val file = myFixture.configureByText("file.kt", """
|
||||
class A {
|
||||
fun <T> method(): List<T> { TODO() }
|
||||
}
|
||||
|
||||
fun main(){
|
||||
val a = A()
|
||||
println(a)
|
||||
}
|
||||
""".trimIndent()
|
||||
) as KtFile
|
||||
|
||||
val reference = file.findUElementByTextFromPsi<UElement>("println(a)")
|
||||
.findElementByTextFromPsi<UReferenceExpression>("a")
|
||||
|
||||
val callExpression = uastElementFactory.createCallExpression(
|
||||
reference,
|
||||
"method",
|
||||
emptyList(),
|
||||
createTypeFromText(
|
||||
"java.util.List<java.lang.Integer>",
|
||||
null
|
||||
),
|
||||
UastCallKind.METHOD_CALL,
|
||||
context = reference.sourcePsi
|
||||
) ?: kfail("cannot create method call")
|
||||
|
||||
TestCase.assertEquals("a.method<kotlin.Int>()", callExpression.uastParent?.sourcePsi?.text)
|
||||
TestCase.assertEquals("""
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
|
||||
UIdentifier (Identifier (method))
|
||||
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
|
||||
""".trimIndent(), callExpression.uastParent?.asRecursiveLogString()?.trim()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun `test replace lambda implicit return value`() {
|
||||
|
||||
val file = myFixture.configureByText(
|
||||
"file.kt", """
|
||||
fun main(){
|
||||
val a: (Int) -> String = {
|
||||
println(it)
|
||||
println(2)
|
||||
"abc"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
) as KtFile
|
||||
|
||||
val uLambdaExpression = file.findUElementByTextFromPsi<UInjectionHost>("\"abc\"")
|
||||
.getParentOfType<ULambdaExpression>() ?: kfail("cant get lambda")
|
||||
|
||||
val expressions = uLambdaExpression.body.cast<UBlockExpression>().expressions
|
||||
UsefulTestCase.assertSize(3, expressions)
|
||||
|
||||
val uReturnExpression = expressions.last() as UReturnExpression
|
||||
val newStringLiteral = uastElementFactory.createStringLiteralExpression("def", file) ?: kfail("cannot create method call")
|
||||
|
||||
val defReturn = runWriteCommand { uReturnExpression.replace(newStringLiteral) ?: kfail("cant replace") }
|
||||
val uLambdaExpression2 = defReturn.getParentOfType<ULambdaExpression>() ?: kfail("cant get lambda")
|
||||
|
||||
TestCase.assertEquals("{\n println(it)\n println(2)\n \"def\"\n }", uLambdaExpression2.sourcePsi?.text)
|
||||
TestCase.assertEquals(
|
||||
"""
|
||||
ULambdaExpression
|
||||
UParameter (name = it)
|
||||
UBlockExpression
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
USimpleNameReferenceExpression (identifier = it)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
ULiteralExpression (value = 2)
|
||||
UReturnExpression
|
||||
ULiteralExpression (value = "def")
|
||||
""".trimIndent(), uLambdaExpression2.asRecursiveLogString().trim()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class UserDataChecker {
|
||||
|
||||
private val storedData = Any()
|
||||
private val KEY = Key.create<Any>("testKey")
|
||||
|
||||
private lateinit var uniqueStringLiteralText: String
|
||||
|
||||
fun store(uElement: UInjectionHost) {
|
||||
val psiElement = uElement.sourcePsi as KtStringTemplateExpression
|
||||
uniqueStringLiteralText = psiElement.text
|
||||
psiElement.putCopyableUserData(KEY, storedData)
|
||||
}
|
||||
|
||||
fun checkUserDataAlive(uElement: UElement) {
|
||||
val psiElements = uElement.let { SyntaxTraverser.psiTraverser(it.sourcePsi) }
|
||||
.filter(KtStringTemplateExpression::class.java)
|
||||
.filter { it.text == uniqueStringLiteralText }.toList()
|
||||
|
||||
UsefulTestCase.assertNotEmpty(psiElements)
|
||||
UsefulTestCase.assertTrue("uElement still should keep the userdata", psiElements.any { storedData === it!!.getCopyableUserData(KEY) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun `test add intermediate returns to lambda`() {
|
||||
|
||||
val file = myFixture.configureByText(
|
||||
"file.kt", """
|
||||
fun main(){
|
||||
val a: (Int) -> String = lname@{
|
||||
println(it)
|
||||
println(2)
|
||||
"abc"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
) as KtFile
|
||||
|
||||
val aliveChecker = UserDataChecker()
|
||||
val uLambdaExpression = file.findUElementByTextFromPsi<UInjectionHost>("\"abc\"")
|
||||
.also(aliveChecker::store)
|
||||
.getParentOfType<ULambdaExpression>() ?: kfail("cant get lambda")
|
||||
|
||||
val oldBlockExpression = uLambdaExpression.body.cast<UBlockExpression>()
|
||||
UsefulTestCase.assertSize(3, oldBlockExpression.expressions)
|
||||
|
||||
val conditionalExit = with(uastElementFactory) {
|
||||
createIfExpression(
|
||||
createBinaryExpression(
|
||||
createSimpleReference("it", uLambdaExpression.sourcePsi)!!,
|
||||
createIntLiteral(3, uLambdaExpression.sourcePsi),
|
||||
UastBinaryOperator.GREATER,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!,
|
||||
createReturnExpresion(
|
||||
createStringLiteralExpression("exit", uLambdaExpression.sourcePsi)!!, true,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!,
|
||||
null,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!
|
||||
}
|
||||
|
||||
val newBlockExpression = uastElementFactory.createBlockExpression(
|
||||
listOf(conditionalExit) + oldBlockExpression.expressions,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!
|
||||
|
||||
aliveChecker.checkUserDataAlive(newBlockExpression)
|
||||
|
||||
val uLambdaExpression2 = runWriteCommand {
|
||||
oldBlockExpression.replace(newBlockExpression) ?: kfail("cant replace")
|
||||
}.getParentOfType<ULambdaExpression>() ?: kfail("cant get lambda")
|
||||
|
||||
aliveChecker.checkUserDataAlive(uLambdaExpression2)
|
||||
|
||||
TestCase.assertEquals(
|
||||
"""
|
||||
lname@{
|
||||
if (it > 3) return@lname "exit"
|
||||
println(it)
|
||||
println(2)
|
||||
"abc"
|
||||
}
|
||||
""".trimIndent(), uLambdaExpression2.sourcePsi?.parent?.text
|
||||
)
|
||||
TestCase.assertEquals(
|
||||
"""
|
||||
ULambdaExpression
|
||||
UParameter (name = it)
|
||||
UBlockExpression
|
||||
UIfExpression
|
||||
UBinaryExpression (operator = >)
|
||||
USimpleNameReferenceExpression (identifier = it)
|
||||
ULiteralExpression (value = 3)
|
||||
UReturnExpression
|
||||
ULiteralExpression (value = "exit")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
USimpleNameReferenceExpression (identifier = it)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
ULiteralExpression (value = 2)
|
||||
UReturnExpression
|
||||
ULiteralExpression (value = "abc")
|
||||
""".trimIndent(), uLambdaExpression2.asRecursiveLogString().trim()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun `test converting lambda to if`() {
|
||||
|
||||
val file = myFixture.configureByText(
|
||||
"file.kt", """
|
||||
fun foo(call: (Int) -> String): String = call.invoke(2)
|
||||
|
||||
fun main() {
|
||||
foo {
|
||||
println(it)
|
||||
println(2)
|
||||
"abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
) as KtFile
|
||||
|
||||
val aliveChecker = UserDataChecker()
|
||||
val uLambdaExpression = file.findUElementByTextFromPsi<UInjectionHost>("\"abc\"")
|
||||
.also { aliveChecker.store(it) }
|
||||
.getParentOfType<ULambdaExpression>() ?: kfail("cant get lambda")
|
||||
|
||||
val oldBlockExpression = uLambdaExpression.body.cast<UBlockExpression>()
|
||||
aliveChecker.checkUserDataAlive(oldBlockExpression)
|
||||
|
||||
val newLambda = with(uastElementFactory) {
|
||||
createLambdaExpression(
|
||||
listOf(UParameterInfo(null, "it")),
|
||||
createIfExpression(
|
||||
createBinaryExpression(
|
||||
createSimpleReference("it", uLambdaExpression.sourcePsi)!!,
|
||||
createIntLiteral(3, uLambdaExpression.sourcePsi),
|
||||
UastBinaryOperator.GREATER,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!,
|
||||
oldBlockExpression,
|
||||
createReturnExpresion(
|
||||
createStringLiteralExpression("exit", uLambdaExpression.sourcePsi)!!, true,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!,
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!.also {
|
||||
aliveChecker.checkUserDataAlive(it)
|
||||
},
|
||||
uLambdaExpression.sourcePsi
|
||||
)!!
|
||||
}
|
||||
aliveChecker.checkUserDataAlive(newLambda)
|
||||
|
||||
val uLambdaExpression2 = runWriteCommand {
|
||||
uLambdaExpression.replace(newLambda) ?: kfail("cant replace")
|
||||
}
|
||||
|
||||
TestCase.assertEquals(
|
||||
"""
|
||||
{ it ->
|
||||
if (it > 3) {
|
||||
println(it)
|
||||
println(2)
|
||||
"abc"
|
||||
} else return@foo "exit"
|
||||
}
|
||||
""".trimIndent(), uLambdaExpression2.sourcePsi?.parent?.text
|
||||
)
|
||||
TestCase.assertEquals(
|
||||
"""
|
||||
ULambdaExpression
|
||||
UParameter (name = it)
|
||||
UAnnotation (fqName = null)
|
||||
UBlockExpression
|
||||
UReturnExpression
|
||||
UIfExpression
|
||||
UBinaryExpression (operator = >)
|
||||
USimpleNameReferenceExpression (identifier = it)
|
||||
ULiteralExpression (value = 3)
|
||||
UBlockExpression
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
USimpleNameReferenceExpression (identifier = it)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
ULiteralExpression (value = 2)
|
||||
ULiteralExpression (value = "abc")
|
||||
UReturnExpression
|
||||
ULiteralExpression (value = "exit")
|
||||
""".trimIndent(), uLambdaExpression2.asRecursiveLogString().trim()
|
||||
)
|
||||
aliveChecker.checkUserDataAlive(uLambdaExpression2)
|
||||
|
||||
}
|
||||
|
||||
fun `test removing unnecessary type parameters while replace`() {
|
||||
val aClassFile = myFixture.configureByText(
|
||||
"A.kt",
|
||||
"""
|
||||
class A {
|
||||
fun <T> method():List<T> = TODO()
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val reference = psiFactory.createExpression("a")
|
||||
.toUElementOfType<UReferenceExpression>() ?: kfail("cannot create reference expression")
|
||||
val callExpression = uastElementFactory.createCallExpression(
|
||||
reference,
|
||||
"method",
|
||||
emptyList(),
|
||||
createTypeFromText(
|
||||
"java.util.List<java.lang.Integer>",
|
||||
null
|
||||
),
|
||||
UastCallKind.METHOD_CALL,
|
||||
context = aClassFile
|
||||
) ?: kfail("cannot create method call")
|
||||
|
||||
val listAssigment = myFixture.addFileToProject("temp.kt", """
|
||||
fun foo(kek: List<Int>) {
|
||||
val list: List<Int> = kek
|
||||
}
|
||||
""".trimIndent()).findUElementByTextFromPsi<UVariable>("val list: List<Int> = kek")
|
||||
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
val methodCall = listAssigment.uastInitializer?.replace(callExpression) ?: kfail("cannot replace!")
|
||||
// originally result expected be `a.method()` but we expect to clean up type arguments in other plase
|
||||
TestCase.assertEquals("a.method<Int>()", methodCall.sourcePsi?.parent?.text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun `test create if`() {
|
||||
val condition = psiFactory.createExpression("true").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create condition")
|
||||
val thenBranch = psiFactory.createBlock("{a(b);}").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create then branch")
|
||||
val elseBranch = psiFactory.createExpression("c++").toUElementOfType<UExpression>()
|
||||
?: kfail("cannot create else branch")
|
||||
val ifExpression = uastElementFactory.createIfExpression(condition, thenBranch, elseBranch, dummyContextFile())
|
||||
?: kfail("cannot create if expression")
|
||||
TestCase.assertEquals("if (true) {\n { a(b); }\n } else c++", ifExpression.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test qualified reference`() {
|
||||
val reference = uastElementFactory.createQualifiedReference("java.util.List", myFixture.file)
|
||||
TestCase.assertEquals("java.util.List", reference?.sourcePsi?.text)
|
||||
}
|
||||
|
||||
fun `test build lambda from returning a variable`() {
|
||||
val context = dummyContextFile()
|
||||
val localVariable = uastElementFactory.createLocalVariable("a", null, uastElementFactory.createNullLiteral(context), true, context)
|
||||
?: kfail("cannot create variable")
|
||||
val declarationExpression =
|
||||
uastElementFactory.createDeclarationExpression(listOf(localVariable), context) ?: kfail("cannot create declaration expression")
|
||||
val returnExpression = uastElementFactory.createReturnExpresion(
|
||||
uastElementFactory.createSimpleReference(localVariable, context), false, context
|
||||
) ?: kfail("cannot create return expression")
|
||||
val block = uastElementFactory.createBlockExpression(listOf(declarationExpression, returnExpression), context)
|
||||
?: kfail("cannot create block expression")
|
||||
|
||||
TestCase.assertEquals("""
|
||||
UBlockExpression
|
||||
UDeclarationsExpression
|
||||
ULocalVariable (name = a)
|
||||
ULiteralExpression (value = null)
|
||||
UReturnExpression
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
""".trimIndent(), block.asRecursiveLogString().trim())
|
||||
|
||||
|
||||
val lambda = uastElementFactory.createLambdaExpression(listOf(), block, context) ?: kfail("cannot create lambda expression")
|
||||
TestCase.assertEquals("{ val a = null\na }", lambda.sourcePsi?.text)
|
||||
|
||||
TestCase.assertEquals("""
|
||||
ULambdaExpression
|
||||
UBlockExpression
|
||||
UDeclarationsExpression
|
||||
ULocalVariable (name = a)
|
||||
ULiteralExpression (value = null)
|
||||
UReturnExpression
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
""".trimIndent(), lambda.putIntoVarInitializer().asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
fun `test expand oneline lambda`() {
|
||||
|
||||
val context = dummyContextFile()
|
||||
val parameters = listOf(UParameterInfo(PsiType.INT, "a"))
|
||||
val oneLineLambda = with(uastElementFactory) {
|
||||
createLambdaExpression(
|
||||
parameters,
|
||||
createBinaryExpression(
|
||||
createSimpleReference("a", context)!!,
|
||||
createSimpleReference("a", context)!!,
|
||||
UastBinaryOperator.PLUS, context
|
||||
)!!, context
|
||||
)!!
|
||||
}.putIntoVarInitializer()
|
||||
|
||||
val lambdaReturn = (oneLineLambda.body as UBlockExpression).expressions.single()
|
||||
|
||||
val lambda = with(uastElementFactory) {
|
||||
createLambdaExpression(
|
||||
parameters,
|
||||
createBlockExpression(
|
||||
listOf(
|
||||
createCallExpression(
|
||||
null,
|
||||
"println",
|
||||
listOf(createSimpleReference("a", context)!!),
|
||||
PsiType.VOID,
|
||||
UastCallKind.METHOD_CALL,
|
||||
context
|
||||
)!!,
|
||||
lambdaReturn
|
||||
),
|
||||
context
|
||||
)!!, context
|
||||
)!!
|
||||
}
|
||||
|
||||
TestCase.assertEquals("{ a: kotlin.Int -> println(a)\na + a }", lambda.sourcePsi?.text)
|
||||
|
||||
TestCase.assertEquals("""
|
||||
ULambdaExpression
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UBlockExpression
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (println))
|
||||
USimpleNameReferenceExpression (identifier = println, resolvesTo = null)
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
UReturnExpression
|
||||
UBinaryExpression (operator = +)
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
USimpleNameReferenceExpression (identifier = a)
|
||||
""".trimIndent(), lambda.putIntoVarInitializer().asRecursiveLogString().trim())
|
||||
}
|
||||
|
||||
private fun createTypeFromText(s: String, newClass: PsiElement?): PsiType? {
|
||||
return JavaPsiFacade.getElementFactory(myFixture.project).createTypeFromText(s, newClass)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user