From a281ccf722c587329c18edfb26f8470e0dfe84b0 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Mon, 14 Jan 2019 22:00:57 +0300 Subject: [PATCH] getting rid of `ULiteralExpression` to make it possible to work in both supporting and not supporting `UInjectionHost` Uast-plugins --- .../intellij/patterns/uast/UastPatterns.kt | 25 +++++++++++-------- .../uast/expressions/uastLiteralUtils.kt | 22 ++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/uast/uast-common/src/com/intellij/patterns/uast/UastPatterns.kt b/uast/uast-common/src/com/intellij/patterns/uast/UastPatterns.kt index 4ee443d3c959..b8cc17f0b475 100644 --- a/uast/uast-common/src/com/intellij/patterns/uast/UastPatterns.kt +++ b/uast/uast-common/src/com/intellij/patterns/uast/UastPatterns.kt @@ -35,6 +35,8 @@ fun injectionHostUExpression(strict: Boolean = true): UExpressionPattern = + uExpression().filter { it is UReferenceExpression || it.isInjectionHost() } fun callExpression(): UCallExpressionPattern = UCallExpressionPattern() @@ -94,12 +96,12 @@ open class UElementPattern>(clazz: private val constructorOrMethodCall = setOf(UastCallKind.CONSTRUCTOR_CALL, UastCallKind.METHOD_CALL) -private fun isCallExpressionParameter(argumentExpression: UElement, +private fun isCallExpressionParameter(argumentExpression: UExpression, parameterIndex: Int, callPattern: ElementPattern): Boolean { val call = argumentExpression.uastParent.getUCallExpression(searchLimit = 2) as? UCallExpressionEx ?: return false if (call.kind !in constructorOrMethodCall) return false - return call.getArgumentForParameter(parameterIndex) == argumentExpression && callPattern.accepts(call) + return call.getArgumentForParameter(parameterIndex) == unwrapPolyadic(argumentExpression) && callPattern.accepts(call) } private val GUARD = RecursionManager.createGuard("isPropertyAssignCall") @@ -128,11 +130,13 @@ class UCallExpressionPattern : UElementPattern): UCallExpressionPattern = filter { uCallExpression -> - when (uCallExpression) { - is UMultiResolvable -> uCallExpression.multiResolve().any { method.accepts(it.element) } - else -> uCallExpression.resolve().let { method.accepts(it) } - } + fun withAnyResolvedMethod(method: ElementPattern): UCallExpressionPattern = withResolvedMethod(method, true) + + fun withResolvedMethod(method: ElementPattern, multiResolve: Boolean): UCallExpressionPattern = filter { uCallExpression -> + if (multiResolve && uCallExpression is UMultiResolvable) + uCallExpression.multiResolve().any { method.accepts(it.element) } + else + uCallExpression.resolve().let { method.accepts(it) } } fun withMethodName(namePattern: ElementPattern): UCallExpressionPattern = filter { it.methodName?.let { namePattern.accepts(it) } ?: false } @@ -186,11 +190,12 @@ open class UExpressionPattern): Self = - callParameter(parameterIndex, callExpression().withAnyResolvedMethod(methodPattern)) + @JvmOverloads + fun methodCallParameter(parameterIndex: Int, methodPattern: ElementPattern, multiResolve: Boolean = true): Self = + callParameter(parameterIndex, callExpression().withResolvedMethod(methodPattern, multiResolve)) override fun arrayAccessParameterOf(receiverClassPattern: ElementPattern): Self = filter { self -> - val aae: UArrayAccessExpression = self.uastParent as? UArrayAccessExpression ?: return@filter false + val aae: UArrayAccessExpression = unwrapPolyadic(self).uastParent as? UArrayAccessExpression ?: return@filter false val receiverClass = (aae.receiver.getExpressionType() as? PsiClassType)?.resolve() ?: return@filter false receiverClassPattern.accepts(receiverClass) } diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt index c992bd134524..351d4e4c774f 100644 --- a/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt @@ -20,6 +20,8 @@ package org.jetbrains.uast import com.intellij.psi.PsiLanguageInjectionHost import com.intellij.psi.PsiReference import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.uast.expressions.UInjectionHost /** * Checks if the [UElement] is a null literal. @@ -54,13 +56,22 @@ fun UElement.isFalseLiteral(): Boolean = this is ULiteralExpression && this.isBo * * @return true if the receiver is a [String] literal, false otherwise. */ +@Deprecated("doesn't support UInjectionHost, most probably it is not what you want", ReplaceWith("isInjectionHost()")) fun UElement.isStringLiteral(): Boolean = this is ULiteralExpression && this.isString +/** + * Checks if the [UElement] is a [PsiLanguageInjectionHost] holder. + * + * NOTE: It is a transitional function until everything will migrate to [UInjectionHost] + */ +fun UElement?.isInjectionHost(): Boolean = this is UInjectionHost || (this is UExpression && this.sourceInjectionHost != null) + /** * Returns the [String] literal value. * * @return literal text if the receiver is a valid [String] literal, null otherwise. */ +@Deprecated("doesn't support UInjectionHost, most probably it is not what you want", ReplaceWith("UExpression.evaluateString()")) fun UElement.getValueIfStringLiteral(): String? = if (isStringLiteral()) (this as ULiteralExpression).value as String else null @@ -126,6 +137,17 @@ val UExpression.sourceInjectionHost: PsiLanguageInjectionHost? val ULiteralExpression.psiLanguageInjectionHost: PsiLanguageInjectionHost? get() = this.psi?.let { PsiTreeUtil.getParentOfType(it, PsiLanguageInjectionHost::class.java, false) } +// Workaround until everything will migrate to `UInjectionHost` from `ULiteralExpression`, see KT-27283 +@ApiStatus.Experimental +fun unwrapPolyadic(uElement: UExpression): UExpression { + if (uElement is ULiteralExpression) { + val parent = uElement.uastParent + if (parent is UPolyadicExpression) + return parent + } + return uElement +} + /** * @return all references injected into this [ULiteralExpression] *