IDEA-CR-41634: [uast] convertToAlternatives for java PsiMethodCallExpression

(cherry picked from commit f6480f5ad7ae402974c6a5a35a558ccddc5302ae)
This commit is contained in:
Nicolay Mitropolsky
2018-12-28 17:10:44 +03:00
committed by Nicolay Mitropolsky
parent aedeb95e4e
commit 26a2e7e4dd
3 changed files with 71 additions and 22 deletions
@@ -114,6 +114,11 @@ interface UastLanguagePlugin {
?.takeIf { result -> requiredTypes.any { it.isAssignableFrom(result.javaClass) } }
} as? T
@JvmDefault
fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: List<Class<out T>>): Sequence<T> =
sequenceOf(convertElementWithParent(element, requiredTypes)).filterNotNull()
}
inline fun <reified T : UElement> UastLanguagePlugin.convertOpt(element: PsiElement?, parent: UElement?): T? {
@@ -100,7 +100,7 @@ class JavaUastLanguagePlugin : UastLanguagePlugin {
@Suppress("UNCHECKED_CAST")
fun <T : UElement> convertElement(element: PsiElement, parent: UElement?, requiredTypes: List<Class<out T>>): T? {
val nonEmptyRequiredTypes = requiredTypes.takeIf { it.isNotEmpty() } ?: DEFAULT_TYPES_LIST
val nonEmptyRequiredTypes = requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)
if (checkCanConvert && !canConvert(element.javaClass, nonEmptyRequiredTypes)) return null
return (convertDeclaration(element, parent, nonEmptyRequiredTypes)
@@ -111,6 +111,14 @@ class JavaUastLanguagePlugin : UastLanguagePlugin {
return convertElement(element, null, requiredTypes)
}
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: List<Class<out T>>) = when (element) {
is `PsiMethodCallExpression` ->
JavaConverter.psiMethodCallConversionAlternatives(element,
null,
requiredTypes.nonEmptyOr(DEFAULT_EXPRESSION_TYPES_LIST)) as Sequence<T>
else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull()
}
private fun convertDeclaration(element: PsiElement,
givenParent: UElement?,
requiredType: List<Class<out UElement>>): UElement? {
@@ -143,7 +151,7 @@ class JavaUastLanguagePlugin : UastLanguagePlugin {
}
}
internal inline fun <reified ActualT : UElement> Class<out UElement>?.el(f: () -> UElement?): UElement? {
internal inline fun <reified ActualT : UElement> Class<*>?.el(f: () -> UElement?): UElement? {
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
}
@@ -237,25 +245,7 @@ internal object JavaConverter {
else
expr<UCallExpression>(build(::JavaConstructorUCallExpression))
}
is PsiMethodCallExpression -> {
if (el.methodExpression.qualifierExpression != null) {
if (requiredType.isAssignableFrom(UQualifiedReferenceExpression::class.java) ||
requiredType.isAssignableFrom(UCallExpression::class.java)) {
val expr = JavaUCompositeQualifiedExpression(el, givenParent).apply {
receiverInitializer = { convertOrEmpty(el.methodExpression.qualifierExpression!!, this) }
selector = JavaUCallExpression(el, this)
}
if (!requiredType.isAssignableFrom(UQualifiedReferenceExpression::class.java))
expr.selector
else
expr
}
else
null
}
else
expr<UCallExpression>(build(::JavaUCallExpression))
}
is PsiMethodCallExpression -> psiMethodCallConversionAlternatives(el, givenParent, requiredType).firstOrNull()
is PsiArrayInitializerExpression -> expr<UCallExpression>(build(::JavaArrayInitializerUCallExpression))
is PsiBinaryExpression -> expr<UBinaryExpression>(build(::JavaUBinaryExpression))
// Should go after PsiBinaryExpression since it implements PsiPolyadicExpression
@@ -279,6 +269,28 @@ internal object JavaConverter {
}
}
internal fun psiMethodCallConversionAlternatives(element: PsiMethodCallExpression,
givenParent: UElement?,
requiredTypes: List<Class<out UElement>>): Sequence<UExpression> {
if (element.methodExpression.qualifierExpression == null) {
return sequenceOf(requiredTypes.expr<UCallExpression> { JavaUCallExpression(element, givenParent) }).filterNotNull()
}
if (!requiredTypes.isAssignableFrom(UQualifiedReferenceExpression::class.java) &&
!requiredTypes.isAssignableFrom(UCallExpression::class.java)) return emptySequence()
val expr = JavaUCompositeQualifiedExpression(element, givenParent).apply {
receiverInitializer = {
convertOrEmpty(element.methodExpression.qualifierExpression!!, this@apply)
}
selector = JavaUCallExpression(element, this@apply)
}
val results = sequenceOf(expr, expr.selector)
return requiredTypes.asSequence().flatMap { required -> results.filter { required.isInstance(it) } }.distinct()
}
internal fun convertStatement(el: PsiStatement,
givenParent: UElement?,
requiredType: List<Class<out UElement>> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? {
@@ -363,4 +375,6 @@ internal object JavaConverter {
private fun expressionTypes(requiredType: Class<out UElement>?) = requiredType?.let { listOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST
private fun elementTypes(requiredType: Class<out UElement>?) = requiredType?.let { listOf(it) } ?: DEFAULT_TYPES_LIST
private fun elementTypes(requiredType: Class<out UElement>?) = requiredType?.let { listOf(it) } ?: DEFAULT_TYPES_LIST
private fun <T : UElement> List<Class<out T>>.nonEmptyOr(default: List<Class<out UElement>>) = takeIf { it.isNotEmpty() } ?: default
@@ -75,6 +75,36 @@ class JavaUastApiTest : AbstractJavaUastTest() {
}
}
@Test
fun testCallExpressionAlternatives() {
doTest("Simple/CallExpression.java") { name, file ->
val index = file.psi.text.indexOf("format")
val callExpression = PsiTreeUtil.getParentOfType(file.psi.findElementAt(index), PsiCallExpression::class.java)!!
val javaUastLanguagePlugin = UastLanguagePlugin.byLanguage(callExpression.language)!!
javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UCallExpression::class.java)).let {
assertEquals("format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
}
javaUastLanguagePlugin.convertToAlternatives<UExpression>(callExpression, listOf(UQualifiedReferenceExpression::class.java,
UCallExpression::class.java)).let {
assertEquals("String.format(\"q\"), format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
}
javaUastLanguagePlugin.convertToAlternatives<UExpression>(callExpression, listOf(UCallExpression::class.java,
UQualifiedReferenceExpression::class.java)).let {
assertEquals("format(\"q\"), String.format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
}
javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UExpression::class.java)).let {
assertEquals("String.format(\"q\"), format(\"q\")", it.joinToString(transform = UExpression::asRenderString))
}
}
}
@Test
fun testCallExpressionArguments() {
doTest("Simple/CallExpression.java") { name, file ->