[uast] generating method calls in java, with restoring generics type parameters,if generation, smart replace IDEA-CR-50955

GitOrigin-RevId: a42242a1c30c2d7b9d1ee19881b0ba4d16aee4fa
This commit is contained in:
aleksandr.izmaylov
2019-08-06 11:14:12 +03:00
committed by intellij-monorepo-bot
parent 4e8a2ef411
commit 876df1daa5
3 changed files with 437 additions and 72 deletions
@@ -38,19 +38,34 @@ interface UastCodeGenerationPlugin {
fun createReturnExpresion(expression: UExpression, inLambda: Boolean = false): UReturnExpression?
fun createLocalVariable(name: String?, type: PsiType?, initializer: UExpression, immutable: Boolean = false): ULocalVariable?
fun createLocalVariable(suggestedName: String?, type: PsiType?, initializer: UExpression, immutable: Boolean = false): ULocalVariable?
fun createBlockExpression(expressions: List<UExpression>, project: Project): UBlockExpression?
fun createLambdaExpression(parameters: List<UParameter>, body: UExpression): ULambdaExpression?
fun createLambdaExpression(parameters: List<UParameterInfo>, body: UExpression): ULambdaExpression?
fun createDeclarationExpression(declarations: List<UDeclaration>, project: Project): UDeclarationsExpression?
fun <T: UElement> replace(oldElement: UElement, newElement: T, elementType: Class<T>): T?
fun createCallExpression(receiver: UExpression?,
methodName: String,
parameters: List<UExpression>,
project: Project,
expectedReturnType: PsiType?,
kind: UastCallKind): UCallExpression?
fun createIfExpression(condition: UExpression, thenBranch: UExpression, elseBranch: UExpression? = null): UIfExpression?
fun <T : UElement> replace(oldElement: UElement, newElement: T, elementType: Class<T>): T?
}
@ApiStatus.Experimental
inline fun <reified T: UElement> UElement.replace(newElement: T): T? =
data class UParameterInfo(val type: PsiType?, val suggestedName: String?)
@ApiStatus.Experimental
infix fun String?.ofType(type: PsiType?): UParameterInfo = UParameterInfo(type, this)
@ApiStatus.Experimental
inline fun <reified T : UElement> UElement.replace(newElement: T): T? =
this.getLanguagePlugin()
.let { UastCodeGenerationPlugin.byLanguage(it.language) }
?.replace(this, newElement, T::class.java)
@@ -60,4 +75,5 @@ inline fun <reified T : UElement> T.refreshed() = sourcePsi?.toUElementOfType<T>
val UElement.generationPlugin: UastCodeGenerationPlugin?
@ApiStatus.Experimental
get() = getLanguagePlugin().let { UastCodeGenerationPlugin.byLanguage(it.language) }
get() = getLanguagePlugin().let { UastCodeGenerationPlugin.byLanguage(it.language) }
@@ -10,27 +10,172 @@ import com.intellij.psi.*
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.codeStyle.VariableKind
import com.intellij.psi.impl.source.tree.CompositeElement
import com.intellij.psi.util.PsiTypesUtil
import com.intellij.psi.util.PsiUtil
import com.intellij.util.castSafelyTo
import com.siyeh.ig.psiutils.ParenthesesUtils
import org.jetbrains.uast.*
import org.jetbrains.uast.generate.UParameterInfo
import org.jetbrains.uast.generate.UastCodeGenerationPlugin
import org.jetbrains.uast.java.*
import java.lang.AssertionError
class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin {
override fun <T: UElement> replace(oldElement: UElement, newElement: T, elementType: Class<T>): T? =
newElement.sourcePsi?.let { oldElement.sourcePsi?.replace(it) }?.toUElementOfExpectedTypes(elementType)
override fun createIfExpression(condition: UExpression, thenBranch: UExpression, elseBranch: UExpression?): UIfExpression? {
val conditionPsi = condition.sourcePsi ?: return null
val thenBranchPsi = thenBranch.sourcePsi ?: return null
val factory = JavaPsiFacade.getElementFactory(conditionPsi.project)
val ifStatement = factory.createStatementFromText("if (a) b else c", null) as? PsiIfStatement ?: return null
ifStatement.condition?.replace(conditionPsi)
ifStatement.thenBranch?.replace(thenBranchPsi.branchStatement ?: return null)
elseBranch?.sourcePsi?.branchStatement?.let { ifStatement.elseBranch?.replace(it) } ?: ifStatement.elseBranch?.delete()
return JavaUIfExpression(ifStatement, null)
}
override fun createCallExpression(receiver: UExpression?,
methodName: String,
parameters: List<UExpression>,
project: Project,
expectedReturnType: PsiType?,
kind: UastCallKind): UCallExpression? {
if (kind != UastCallKind.METHOD_CALL) return null
val factory = JavaPsiFacade.getElementFactory(project)
val methodCall = factory.createExpressionFromText(if (receiver != null) "a.b()" else "a()", null) as? PsiMethodCallExpression
?: return null
val methodIdentifier = factory.createIdentifier(methodName)
if (receiver != null) {
methodCall.methodExpression.qualifierExpression?.replace(receiver.sourcePsi!!)
}
methodCall.methodExpression.referenceNameElement?.replace(methodIdentifier)
for (parameter in parameters) {
methodCall.argumentList.add(parameter.sourcePsi!!)
}
return if (expectedReturnType == null)
JavaUCallExpression(methodCall, null)
else
MethodCallUpgradeHelper(project, methodCall, expectedReturnType).tryUpgradeToExpectedType()
?.let { JavaUCallExpression(it, null) }
}
private class MethodCallUpgradeHelper(val project: Project, val methodCall: PsiMethodCallExpression, val expectedReturnType: PsiType) {
lateinit var resultType: PsiType
fun tryUpgradeToExpectedType(): PsiMethodCallExpression? {
resultType = methodCall.type ?: return null
if (expectedReturnType.isAssignableFrom(resultType)) return methodCall
if (!(resultType eqResolved expectedReturnType))
return null
return methodCall.methodExpression.qualifierExpression?.let { tryPickUpTypeParameters() }
}
private fun tryPickUpTypeParameters(): PsiMethodCallExpression? {
val expectedTypeTypeParameters = expectedReturnType.castSafelyTo<PsiClassType>()?.parameters ?: return null
val resultTypeTypeParameters = resultType.castSafelyTo<PsiClassType>()?.parameters ?: return null
val notEqualTypeParametersIndices = expectedTypeTypeParameters
.zip(resultTypeTypeParameters)
.withIndex()
.filterNot { (_, pair) -> PsiTypesUtil.compareTypes(pair.first, pair.second, false) }
.map { (i, _) -> i }
val resolvedMethod = methodCall.resolveMethod() ?: return null
val methodReturnTypeTypeParameters = (resolvedMethod.returnType.castSafelyTo<PsiClassType>())?.parameters ?: return null
val methodDefinitionTypeParameters = resolvedMethod.typeParameters
val methodDefinitionToReturnTypeParametersMapping = methodDefinitionTypeParameters.map {
it to methodReturnTypeTypeParameters.indexOfFirst { param -> it.name == param.canonicalText }
}
.filter { it.second != -1 }
.toMap()
val notMatchedTypesParametersNames = notEqualTypeParametersIndices
.map { methodReturnTypeTypeParameters[it].canonicalText }
.toSet()
val callTypeParametersSubstitutor = methodCall.resolveMethodGenerics().substitutor
val newParametersList = mutableListOf<PsiType>()
for (parameter in methodDefinitionTypeParameters) {
if (parameter.name in notMatchedTypesParametersNames) {
newParametersList += expectedTypeTypeParameters[methodDefinitionToReturnTypeParametersMapping[parameter] ?: return null]
}
else {
newParametersList += callTypeParametersSubstitutor.substitute(parameter) ?: return null
}
}
return buildMethodCallFromNewTypeParameters(newParametersList)
}
private fun buildMethodCallFromNewTypeParameters(newParametersList: List<PsiType>): PsiMethodCallExpression? {
val factory = JavaPsiFacade.getElementFactory(project)
val expr = factory.createExpressionFromText(buildString {
append("a.<")
newParametersList.joinTo(this) { "T" }
append(">b()")
}, null) as? PsiMethodCallExpression ?: return null
for ((i, param) in newParametersList.withIndex()) {
val typeElement = factory.createTypeElement(param)
expr.typeArgumentList.typeParameterElements[i].replace(typeElement)
}
methodCall.typeArgumentList.replace(expr.typeArgumentList)
if (methodCall.type?.let { expectedReturnType.isAssignableFrom(it) } != true)
return null
return methodCall
}
infix fun PsiType.eqResolved(other: PsiType): Boolean {
val resolvedThis = this.castSafelyTo<PsiClassType>()?.resolve() ?: return false
val resolvedOther = other.castSafelyTo<PsiClassType>()?.resolve() ?: return false
return PsiManager.getInstance(project).areElementsEquivalent(resolvedThis, resolvedOther)
}
}
private fun adjustChainStyleToMethodCalls(oldPsi: PsiElement, newPsi: PsiElement) {
if (oldPsi is PsiMethodCallExpression && newPsi is PsiMethodCallExpression &&
oldPsi.methodExpression.qualifierExpression != null && newPsi.methodExpression.qualifier != null) {
if (oldPsi.methodExpression.children.getOrNull(1) is PsiWhiteSpace &&
newPsi.methodExpression.children.getOrNull(1) !is PsiWhiteSpace
) {
newPsi.methodExpression.addAfter(oldPsi.methodExpression.children[1], newPsi.methodExpression.children[0])
}
}
}
override fun <T : UElement> replace(oldElement: UElement, newElement: T, elementType: Class<T>): T? {
val oldPsi = oldElement.sourcePsi ?: return null
val newPsi = newElement.sourcePsi ?: return null
adjustChainStyleToMethodCalls(oldPsi, newPsi)
val factory = JavaPsiFacade.getElementFactory(oldPsi.project)
return if (oldPsi is PsiStatement && newPsi is PsiExpression) {
newPsi.let { factory.createExpresionStatement(it) }
}
else {
newPsi
}
?.let { oldPsi.replace(it) }
?.toUElementOfExpectedTypes(elementType)
}
override fun createDeclarationExpression(declarations: List<UDeclaration>, project: Project): UDeclarationsExpression? {
return JavaUDeclarationsExpression(null, declarations)
}
private fun PsiElementFactory.createExpresionStatement(expression: PsiExpression): PsiStatement? {
val statement = createStatementFromText("x;", null) as? PsiExpressionStatement ?: return null
statement.expression.replace(expression)
return statement
}
override fun createReturnExpresion(expression: UExpression, inLambda: Boolean): UReturnExpression? {
val factory = JavaPsiFacade.getElementFactory(expression.sourcePsi?.project ?: return null)
val returnStatement = factory.createStatementFromText("return ;", null) as? PsiReturnStatement ?: return null
@@ -40,40 +185,38 @@ class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin {
return JavaUReturnExpression(returnStatement, null)
}
override fun createLocalVariable(name: String?, type: PsiType?, initializer: UExpression, immutable: Boolean): ULocalVariable? {
val initializerPsi = initializer.sourcePsi as? PsiExpression ?: return null
if (name == null) {
return createLocalVariableWithSuggestedName(type, initializer, immutable)
private fun PsiVariable.setMutability(immutable: Boolean) {
if (immutable && !this.hasModifier(JvmModifier.FINAL)) {
PsiUtil.setModifierProperty(this, PsiModifier.FINAL, true)
}
if (!immutable && this.hasModifier(JvmModifier.FINAL)) {
PsiUtil.setModifierProperty(this, PsiModifier.FINAL, false)
}
}
override fun createLocalVariable(suggestedName: String?, type: PsiType?, initializer: UExpression, immutable: Boolean): ULocalVariable? {
val initializerPsi = initializer.sourcePsi as? PsiExpression ?: return null
val name = createNameFromSuggested(
suggestedName,
initializerPsi.project,
VariableKind.LOCAL_VARIABLE,
type,
initializer = initializerPsi,
context = initializerPsi
) ?: return null
val factory = JavaPsiFacade.getElementFactory(initializerPsi.project)
val variable = (type ?: initializer.getExpressionType())?.let { variableType ->
factory.createVariableDeclarationStatement(name, variableType, initializerPsi,
initializerPsi.context).declaredElements.firstOrNull() as? PsiLocalVariable
} ?: return null
if (immutable && !variable.hasModifier(JvmModifier.FINAL)) {
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true)
}
if (!immutable && variable.hasModifier(JvmModifier.FINAL)) {
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, false)
}
variable.setMutability(immutable)
return JavaULocalVariable(variable, null)
}
private fun createLocalVariableWithSuggestedName(type: PsiType?, initializer: UExpression, immutable: Boolean): ULocalVariable? {
val initializerPsi = initializer.sourcePsi as? PsiExpression ?: return null
val codeStyleManager = JavaCodeStyleManager.getInstance(initializerPsi.project)
val resultType = type ?: initializerPsi.type ?: return null
var suggestedNameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, initializerPsi, resultType)
suggestedNameInfo = codeStyleManager.suggestUniqueVariableName(suggestedNameInfo, initializerPsi, true)
val name = suggestedNameInfo.names.firstOrNull() ?: return null
return createLocalVariable(name, type, initializer, immutable)
}
override fun createBlockExpression(expressions: List<UExpression>, project: Project): UBlockExpression? {
val factory = JavaPsiFacade.getElementFactory(project)
val blockStatement = BlockUtils.createBlockStatement(project)
@@ -96,30 +239,41 @@ class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin {
return JavaUBlockExpression(blockStatement, null)
}
override fun createLambdaExpression(parameters: List<UParameter>, body: UExpression): ULambdaExpression? {
val factory = JavaPsiFacade.getElementFactory(body.sourcePsi?.project ?: return null)
override fun createLambdaExpression(parameters: List<UParameterInfo>, body: UExpression): ULambdaExpression? {
//TODO: smart handling cases, when parameters types should exist in code
val project = body.sourcePsi?.project ?: return null
val factory = JavaPsiFacade.getElementFactory(project)
val lambda = factory.createExpressionFromText(
buildString {
parameters.joinTo(this, separator = ",", prefix = "(", postfix = ")") { "x" }
append("->")
append("{}")
parameters.joinTo(this, separator = ",", prefix = "(", postfix = ")") { "x x" }
append("->{}")
},
null
) as? PsiLambdaExpression ?: return null
lambda.parameterList.parameters.forEachIndexed { i, p ->
p.replace(parameters[i].sourcePsi ?: return null)
val needsType = parameters.all { it.type != null }
lambda.parameterList.parameters.forEachIndexed { i, parameter ->
parameters[i].also { parameterInfo ->
val name = createNameFromSuggested(
parameterInfo.suggestedName,
project,
VariableKind.PARAMETER,
parameterInfo.type,
context = body.sourcePsi
) ?: return null
parameter.nameIdentifier?.replace(factory.createIdentifier(name))
if (needsType) {
parameter.typeElement?.replace(factory.createTypeElement(parameterInfo.type!!))
}
else {
parameter.removeTypeElement()
}
}
}
if (lambda.parameterList.parameters.any { it.typeElement == null })
lambda.parameterList.parameters.forEach {
it.typeElement?.delete()
if (it.children.getOrNull(1) !is PsiIdentifier)
it.children.getOrNull(1)?.delete()
}
if (lambda.parameterList.parametersCount == 1 && lambda.parameterList.parameters[0].typeElement == null) {
if (lambda.parameterList.parametersCount == 1) {
lambda.parameterList.parameters[0].removeTypeElement()
lambda.parameterList.firstChild.delete()
lambda.parameterList.lastChild.delete()
}
@@ -136,10 +290,11 @@ class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin {
return JavaULambdaExpression(lambda, null)
}
private fun normalizeBlockForLambda(block: PsiCodeBlock): PsiElement = block
.takeIf { block.statementCount == 1 }
?.let { block.statements[0] as? PsiReturnStatement }
?.let { it.returnValue } ?: block
private fun normalizeBlockForLambda(block: PsiCodeBlock): PsiElement =
block
.takeIf { block.statementCount == 1 }
?.let { block.statements[0] as? PsiReturnStatement }
?.let { it.returnValue } ?: block
override fun createParenthesizedExpression(expression: UExpression): UParenthesizedExpression? {
val factory = JavaPsiFacade.getElementFactory(expression.sourcePsi?.project ?: return null)
@@ -175,7 +330,7 @@ class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin {
else -> return null
}
val psiBinaryExpression = factory.createExpressionFromText("a $operatorSymbol b", null) as? PsiBinaryExpression
?: return null
?: return null
psiBinaryExpression.lOperand.replace(leftPsi)
psiBinaryExpression.rOperand?.replace(rightPsi)
@@ -191,10 +346,45 @@ class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin {
val dummyParent = factory.createStatementFromText("a;", null)
dummyParent.firstChild.replace(binarySourcePsi)
ParenthesesUtils.removeParentheses(dummyParent.firstChild as PsiExpression, false)
return when(val result = dummyParent.firstChild) {
return when (val result = dummyParent.firstChild) {
is PsiBinaryExpression -> JavaUBinaryExpression(result, null)
is PsiPolyadicExpression -> JavaUPolyadicExpression(result, null)
else -> throw AssertionError("Unexpected type " + result.javaClass)
else -> error("Unexpected type " + result.javaClass)
}
}
}
}
private val PsiElement.branchStatement: PsiStatement?
get() = when (this) {
is PsiExpression -> JavaPsiFacade.getElementFactory(this.project).createExpresionStatement(this)
is PsiCodeBlock -> BlockUtils.createBlockStatement(this.project).also { it.codeBlock.replace(this) }
is PsiStatement -> this
else -> null
}
private fun PsiElementFactory.createExpresionStatement(expression: PsiExpression): PsiStatement? {
val statement = createStatementFromText("x;", null) as? PsiExpressionStatement ?: return null
statement.expression.replace(expression)
return statement
}
private fun PsiVariable.removeTypeElement() {
this.typeElement?.delete()
if (this.children.getOrNull(1) !is PsiIdentifier) {
this.children.getOrNull(1)?.delete()
}
}
private fun createNameFromSuggested(suggestedName: String?,
project: Project,
variableKind: VariableKind,
type: PsiType? = null,
initializer: PsiExpression? = null,
context: PsiElement? = null): String? {
val codeStyleManager = JavaCodeStyleManager.getInstance(project)
val name = suggestedName ?: codeStyleManager.generateVariableName(type, initializer, variableKind) ?: return null
return codeStyleManager.suggestUniqueVariableName(name, context, false)
}
private fun JavaCodeStyleManager.generateVariableName(type: PsiType?, initializer: PsiExpression?, kind: VariableKind) =
suggestVariableName(kind, null, initializer, type).names.firstOrNull()
@@ -5,6 +5,7 @@ import com.intellij.lang.java.JavaLanguage
import com.intellij.psi.*
import junit.framework.TestCase
import org.jetbrains.uast.*
import org.jetbrains.uast.generate.UParameterInfo
import org.jetbrains.uast.generate.UastCodeGenerationPlugin
import org.jetbrains.uast.generate.refreshed
import org.jetbrains.uast.generate.replace
@@ -111,11 +112,23 @@ class JavaUastGenerationTest : AbstractJavaUastLightTest() {
val expression = psiFactory.createExpressionFromText("b", null).toUElementOfType<UExpression>()
?: fail("cannot create variable declaration")
val declaration = generatePlugin.createLocalVariable("a", PsiType.DOUBLE, expression, true) ?: fail("cannot create variable")
val declaration = generatePlugin.createLocalVariable("a", PsiType.DOUBLE, expression, true)
?: fail("cannot create variable")
TestCase.assertEquals("final double a = b;", declaration.sourcePsi?.text)
}
fun `test final variable declaration with unique name`() {
val context = psiFactory.createVariableDeclarationStatement("a", PsiType.INT, null, null)
val expression = psiFactory.createExpressionFromText("b", context).toUElementOfType<UExpression>()
?: fail("cannot create variable declaration")
val declaration = generatePlugin.createLocalVariable("a", PsiType.DOUBLE, expression, true)
?: fail("cannot create variable")
TestCase.assertEquals("final double a1 = b;", declaration.sourcePsi?.text)
}
fun `test block expression`() {
val statement1 = psiFactory.createStatementFromText("System.out.println();", null).toUElementOfType<UExpression>()
?: fail("cannot create statement")
@@ -134,28 +147,51 @@ class JavaUastGenerationTest : AbstractJavaUastLightTest() {
val statement = psiFactory.createStatementFromText("System.out.println();", null).toUElementOfType<UExpression>()
?: fail("cannot create statement")
val parameter1 = psiFactory.createParameter("a", PsiType.INT).toUElementOfType<UParameter>() ?: fail("cannot create parameter")
val parameter2 = psiFactory.createParameter("b", PsiType.INT).toUElementOfType<UParameter>() ?: fail("cannot create parameter")
(parameter2.sourcePsi as PsiParameter).typeElement?.delete()
(parameter2.sourcePsi as PsiParameter).children[1].delete()
val lambda = generatePlugin.createLambdaExpression(listOf(parameter1, parameter2), statement) ?: fail("cannot create lambda")
val lambda = generatePlugin.createLambdaExpression(
listOf(
UParameterInfo(PsiType.INT, "a"),
UParameterInfo(null, "b")
),
statement
) ?: fail("cannot create lambda")
TestCase.assertEquals("(a,b)->System.out.println()", lambda.sourcePsi?.text)
}
fun `test lambda expression with explicit types`() {
val statement = psiFactory.createStatementFromText("System.out.println();", null).toUElementOfType<UExpression>()
?: fail("cannot create statement")
val lambda = generatePlugin.createLambdaExpression(
listOf(
UParameterInfo(PsiType.INT, "a"),
UParameterInfo(PsiType.DOUBLE, "b")
),
statement
) ?: fail("cannot create lambda")
TestCase.assertEquals("(int a,double b)->System.out.println()", lambda.sourcePsi?.text)
}
fun `test lambda expression with simplified block body`() {
val block = psiFactory.createStatementFromText("{ return \"10\"; }", null).toUElementOfType<UBlockExpression>()
?: fail("cannot create block")
val parameter1 = psiFactory.createParameter("a", PsiType.INT).toUElementOfType<UParameter>() ?: fail("cannot create parameter")
(parameter1.sourcePsi as PsiParameter).typeElement?.delete()
(parameter1.sourcePsi as PsiParameter).children[1].delete()
val lambda = generatePlugin.createLambdaExpression(listOf(parameter1), block) ?: fail("cannot create lambda")
val lambda = generatePlugin.createLambdaExpression(listOf(UParameterInfo(null, "a")), block)
?: fail("cannot create lambda")
TestCase.assertEquals("""a->"10"""", lambda.sourcePsi?.text)
}
fun `test lambda expression with simplified block body with context`() {
val context = psiFactory.createVariableDeclarationStatement("a", PsiType.INT, null)
val block = psiFactory.createStatementFromText("{ return \"10\"; }", context).toUElementOfType<UBlockExpression>()
?: fail("cannot create block")
val lambda = generatePlugin.createLambdaExpression(listOf(UParameterInfo(null, "a")), block)
?: fail("cannot create lambda")
TestCase.assertEquals("""a1->"10"""", lambda.sourcePsi?.text)
}
fun `test function argument replacement`() {
val expression = psiFactory.createExpressionFromText("f(a)", null).toUElementOfType<UCallExpression>()
?: fail("cannot create expression")
@@ -177,4 +213,127 @@ class JavaUastGenerationTest : AbstractJavaUastLightTest() {
TestCase.assertEquals("final int i = f(a) + 1;", variable.sourcePsi?.text)
}
fun `test method call generation with receiver`() {
val receiver = psiFactory.createExpressionFromText(""""10"""", null).toUElementOfType<UExpression>()
?: fail("cannot create receiver")
val arg1 = psiFactory.createExpressionFromText("1", null).toUElementOfType<UExpression>()
?: fail("cannot create arg1")
val arg2 = psiFactory.createExpressionFromText("2", null).toUElementOfType<UExpression>()
?: fail("cannot create arg2")
val methodCall = generatePlugin.createCallExpression(
receiver,
"substring",
listOf(arg1, arg2),
project,
null,
UastCallKind.METHOD_CALL) ?: fail("cannot create call")
TestCase.assertEquals(""""10".substring(1, 2)""", methodCall.sourcePsi?.text)
}
fun `test method call generation without receiver`() {
val arg1 = psiFactory.createExpressionFromText("1", null).toUElementOfType<UExpression>()
?: fail("cannot create arg1")
val arg2 = psiFactory.createExpressionFromText("2", null).toUElementOfType<UExpression>()
?: fail("cannot create arg2")
val methodCall = generatePlugin.createCallExpression(
null,
"substring",
listOf(arg1, arg2),
project,
null,
UastCallKind.METHOD_CALL) ?: fail("cannot create call")
TestCase.assertEquals("""substring(1, 2)""", methodCall.sourcePsi?.text)
}
fun `test method call generation with generics restoring`() {
val arrays = psiFactory.createExpressionFromText("java.util.Arrays", null).toUElementOfType<UExpression>()
?: fail("cannot create receiver")
val methodCall = generatePlugin.createCallExpression(arrays,
"asList",
listOf(),
project,
psiFactory.createTypeFromText("java.util.List<java.lang.String>", null),
UastCallKind.METHOD_CALL) ?: fail("cannot create call")
TestCase.assertEquals("java.util.Arrays.<String>asList()", methodCall.sourcePsi?.text)
}
fun `test method call generation with generics restoring 2 parameters`() {
val collections = psiFactory.createExpressionFromText("java.util.Collections", null).toUElementOfType<UExpression>()
?: fail("cannot create receiver")
val methodCall = generatePlugin.createCallExpression(collections,
"emptyMap",
listOf(),
project,
psiFactory.createTypeFromText(
"java.util.Map<java.lang.String, java.lang.Integer>",
null
),
UastCallKind.METHOD_CALL) ?: fail("cannot create call")
TestCase.assertEquals("java.util.Collections.<String, Integer>emptyMap()", methodCall.sourcePsi?.text)
}
fun `test method call generation with generics restoring 1 parameter with 1 existing`() {
val newClass = myFixture.addClass("""
class A {
public static <T1, T2> java.util.Map<T1, T2> kek(T2 a) {
return null;
}
}
""".trimIndent())
val a = psiFactory.createExpressionFromText("A", newClass).toUElementOfType<UExpression>()
?: fail("cannot create a receiver")
val param = psiFactory.createExpressionFromText("\"a\"", null).toUElementOfType<UExpression>()
?: fail("cannot create a parameter")
val methodCall = generatePlugin.createCallExpression(a,
"kek",
listOf(param),
project,
psiFactory.createTypeFromText(
"java.util.Map<java.lang.String, java.lang.Integer>",
null
),
UastCallKind.METHOD_CALL) ?: fail("cannot create call")
TestCase.assertEquals("A.<String, Integer>kek(\"a\")", methodCall.sourcePsi?.text)
}
fun `test method call generation with generics restoring 1 parameter with 1 unused `() {
val newClass = myFixture.addClass("""
class A {
public static <T1, T2, T3> java.util.Map<T1, T3> kek(T1 a) {
return null;
}
}
""".trimIndent())
val a = psiFactory.createExpressionFromText("A", newClass).toUElementOfType<UExpression>()
?: fail("cannot create a receiver")
val param = psiFactory.createExpressionFromText("\"a\"", null).toUElementOfType<UExpression>()
?: fail("cannot create a parameter")
val methodCall = generatePlugin.createCallExpression(a,
"kek",
listOf(param),
project,
psiFactory.createTypeFromText(
"java.util.Map<java.lang.String, java.lang.Integer>",
null
),
UastCallKind.METHOD_CALL) ?: fail("cannot create call")
TestCase.assertEquals("A.<String, Object, Integer>kek(\"a\")", methodCall.sourcePsi?.text)
}
fun `test create if`() {
val condition = psiFactory.createExpressionFromText("true", null).toUElementOfType<UExpression>()
?: fail("cannot create condition")
val thenBranch = psiFactory.createCodeBlockFromText("{a(b);}", null).toUElementOfType<UExpression>()
?: fail("cannot create then branch")
val elseBranch = psiFactory.createExpressionFromText("c++", null).toUElementOfType<UExpression>()
?: fail("cannot create else branch")
val ifExpression = generatePlugin.createIfExpression(condition, thenBranch, elseBranch)
?: fail("cannot create if expression")
TestCase.assertEquals("if (true) {a(b);} else c++;", ifExpression.sourcePsi?.text)
}
}