mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
4e8a2ef411
commit
876df1daa5
@@ -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) }
|
||||
|
||||
|
||||
+245
-55
@@ -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()
|
||||
+171
-12
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user