Uast: handling reified methods receivers (KT-37613)

This commit is contained in:
Nicolay Mitropolsky
2020-03-27 12:51:43 +03:00
parent d6d19b563d
commit 0ca67c1bac
13 changed files with 110 additions and 34 deletions
@@ -54,6 +54,7 @@ import org.jetbrains.uast.kotlin.declarations.KotlinUMethodWithFakeLightDelegate
import org.jetbrains.uast.kotlin.expressions.* import org.jetbrains.uast.kotlin.expressions.*
import org.jetbrains.uast.kotlin.psi.UastFakeLightMethod import org.jetbrains.uast.kotlin.psi.UastFakeLightMethod
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameterBase
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable
interface KotlinUastResolveProviderService { interface KotlinUastResolveProviderService {
@@ -251,14 +252,18 @@ internal object KotlinConverter {
is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, expectedTypes) } ?: expr<UExpression> { UastEmptyExpression(givenParent) } is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, expectedTypes) } ?: expr<UExpression> { UastEmptyExpression(givenParent) }
is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry)) is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry))
is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes) is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes)
is KtTypeReference -> el<UTypeReferenceExpression> { LazyKotlinUTypeReferenceExpression(element, givenParent) } is KtTypeReference ->
expectedTypes.accommodate(
alternative { LazyKotlinUTypeReferenceExpression(element, givenParent) },
alternative { convertReceiverParameter(element) }
).firstOrNull()
is KtConstructorDelegationCall -> is KtConstructorDelegationCall ->
el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) } el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) }
is KtSuperTypeCallEntry -> is KtSuperTypeCallEntry ->
el<UExpression> { el<UExpression> {
(element.getParentOfType<KtClassOrObject>(true)?.parent as? KtObjectLiteralExpression) (element.getParentOfType<KtClassOrObject>(true)?.parent as? KtObjectLiteralExpression)
?.toUElementOfType<UExpression>() ?.toUElementOfType<UExpression>()
?: KotlinUFunctionCallExpression(element, givenParent) ?: KotlinUFunctionCallExpression(element, givenParent)
} }
is KtImportDirective -> el<UImportStatement>(build(::KotlinUImportStatement)) is KtImportDirective -> el<UImportStatement>(build(::KotlinUImportStatement))
else -> { else -> {
@@ -285,6 +290,11 @@ internal object KotlinConverter {
}} }}
} }
internal fun convertReceiverParameter(receiver: KtTypeReference): UParameter? {
val call = (receiver.parent as? KtCallableDeclaration) ?: return null
if (call.receiverTypeReference != receiver) return null
return call.toUElementOfType<UMethod>()?.uastParameters?.firstOrNull()
}
internal fun convertEntry(entry: KtStringTemplateEntry, internal fun convertEntry(entry: KtStringTemplateEntry,
givenParent: UElement?, givenParent: UElement?,
@@ -504,6 +514,9 @@ internal object KotlinConverter {
el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField))
is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter))
is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter)) is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter))
is UastKotlinPsiParameterBase<*> -> el<UParameter> {
original.ktOrigin.safeAs<KtTypeReference>()?.let { convertReceiverParameter(it) }
}
is UastKotlinPsiVariable -> el<UVariable>(buildKt(original.ktElement, ::KotlinUVariable)) is UastKotlinPsiVariable -> el<UVariable>(buildKt(original.ktElement, ::KotlinUVariable))
is KtEnumEntry -> el<UEnumConstant> { is KtEnumEntry -> el<UEnumConstant> {
@@ -63,19 +63,18 @@ open class KotlinUMethod(
private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference } private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference }
override val uastParameters by lz { override val uastParameters by lz {
val lightParams = psi.parameterList.parameters
val receiver = receiver ?: return@lz lightParams.map { fun parameterOrigin(psiParameter: PsiParameter?): KtElement? = when (psiParameter) {
KotlinUParameter( is KtLightElement<*, *> -> psiParameter.kotlinOrigin
it, when (it) { is UastKotlinPsiParameter -> psiParameter.ktParameter
is KtLightElement<*, *> -> it.kotlinOrigin else -> null
is UastKotlinPsiParameter -> it.ktParameter
else -> null
}, this
)
} }
val lightParams = psi.parameterList.parameters
val receiver = receiver ?: return@lz lightParams.map { KotlinUParameter(it, parameterOrigin(it), this) }
val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList<UParameter>() val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList<UParameter>()
val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, this)) val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, this))
lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) } lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, parameterOrigin(it), this) }
uParameters uParameters
} }
@@ -64,19 +64,18 @@ open class KotlinUMethod(
private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference } private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference }
override val uastParameters by lz { override val uastParameters by lz {
val lightParams = psi.parameterList.parameters
val receiver = receiver ?: return@lz lightParams.map { fun parameterOrigin(psiParameter: PsiParameter?): KtElement? = when (psiParameter) {
KotlinUParameter( is KtLightElement<*, *> -> psiParameter.kotlinOrigin
it, when (it) { is UastKotlinPsiParameter -> psiParameter.ktParameter
is KtLightElement<*, *> -> it.kotlinOrigin else -> null
is UastKotlinPsiParameter -> it.ktParameter
else -> null
}, this
)
} }
val lightParams = psi.parameterList.parameters
val receiver = receiver ?: return@lz lightParams.map { KotlinUParameter(it, parameterOrigin(it), this) }
val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList<UParameter>() val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList<UParameter>()
val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, this)) val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, this))
lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) } lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, parameterOrigin(it), this) }
uParameters uParameters
} }
@@ -13,6 +13,7 @@ import com.intellij.psi.impl.light.LightTypeParameterBuilder
import org.jetbrains.kotlin.asJava.elements.KotlinLightTypeParameterListBuilder import org.jetbrains.kotlin.asJava.elements.KotlinLightTypeParameterListBuilder
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.uast.UastErrorType import org.jetbrains.uast.UastErrorType
@@ -53,6 +54,19 @@ internal class UastFakeLightMethod(internal val original: KtFunction, containing
init { init {
val parameterList = this val parameterList = this
original.receiverTypeReference?.let { receiver ->
this.addParameter(
UastKotlinPsiParameterBase(
"\$this\$${original.name}",
receiver.getType()
?.toPsiType(this@UastFakeLightMethod, original, false)
?: UastErrorType,
parameterList, receiver
)
)
}
for ((i, p) in original.valueParameters.withIndex()) { for ((i, p) in original.valueParameters.withIndex()) {
this.addParameter( this.addParameter(
UastKotlinPsiParameter( UastKotlinPsiParameter(
@@ -8,6 +8,7 @@ import com.intellij.psi.PsiType
import com.intellij.psi.impl.light.LightParameter import com.intellij.psi.impl.light.LightParameter
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -21,12 +22,12 @@ import org.jetbrains.uast.kotlin.toPsiType
class UastKotlinPsiParameter( class UastKotlinPsiParameter(
name: String, name: String,
type: PsiType, type: PsiType,
private val parent: PsiElement, parent: PsiElement,
language: Language, language: Language,
isVarArgs: Boolean, isVarArgs: Boolean,
val ktDefaultValue: KtExpression?, ktDefaultValue: KtExpression?,
val ktParameter: KtParameter ktParameter: KtParameter
) : LightParameter(name, type, parent, language, isVarArgs) { ) : UastKotlinPsiParameterBase<KtParameter>(name, type, parent, ktParameter, language, isVarArgs, ktDefaultValue) {
companion object { companion object {
fun create(parameter: KtParameter, parent: PsiElement, containingElement: UElement, index: Int): PsiParameter { fun create(parameter: KtParameter, parent: PsiElement, containingElement: UElement, index: Int): PsiParameter {
val psiParent = containingElement.getParentOfType<UDeclaration>()?.javaPsi ?: parent val psiParent = containingElement.getParentOfType<UDeclaration>()?.javaPsi ?: parent
@@ -42,15 +43,29 @@ class UastKotlinPsiParameter(
} }
} }
val ktParameter: KtParameter get() = ktOrigin
}
open class UastKotlinPsiParameterBase<T : KtElement>(
name: String,
type: PsiType,
private val parent: PsiElement,
val ktOrigin: T,
language: Language = ktOrigin.language,
isVarArgs: Boolean = false,
val ktDefaultValue: KtExpression? = null,
) : LightParameter(name, type, parent, language, isVarArgs) {
override fun getParent(): PsiElement = parent override fun getParent(): PsiElement = parent
override fun getContainingFile(): PsiFile? = ktParameter.containingFile override fun getContainingFile(): PsiFile? = ktOrigin.containingFile
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other == null || other::class.java != this::class.java) return false if (other == null || other::class.java != this::class.java) return false
return ktParameter == (other as? UastKotlinPsiParameter)?.ktParameter return ktOrigin == (other as? UastKotlinPsiParameterBase<*>)?.ktOrigin
} }
override fun hashCode() = ktParameter.hashCode() override fun hashCode(): Int = ktOrigin.hashCode()
} }
+2
View File
@@ -12,6 +12,8 @@ UFile (package = test.pkg)
UClassLiteralExpression UClassLiteralExpression
USimpleNameReferenceExpression (identifier = java) USimpleNameReferenceExpression (identifier = java)
UMethod (name = systemService1) UMethod (name = systemService1)
UParameter (name = $this$systemService1)
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
UBlockExpression UBlockExpression
UReturnExpression UReturnExpression
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
+1 -1
View File
@@ -4,7 +4,7 @@ public final class ReifiedKt {
public static final fun systemService2(@org.jetbrains.annotations.NotNull $this$systemService2: test.pkg.Context) : error.NonExistentClass { public static final fun systemService2(@org.jetbrains.annotations.NotNull $this$systemService2: test.pkg.Context) : error.NonExistentClass {
return <anonymous class>(java.lang.String.java) return <anonymous class>(java.lang.String.java)
} }
fun systemService1() : <ErrorType> { fun systemService1(@org.jetbrains.annotations.NotNull $this$systemService1: test.pkg.Context) : <ErrorType> {
return <anonymous class>(T.java) return <anonymous class>(T.java)
} }
} }
+4
View File
@@ -2,6 +2,10 @@ inline fun <reified T> functionWithLambda(t: T, process: (T) -> Int): Int = proc
inline fun <reified T> functionWithVararg(i: Int?, vararg t: T): T = t[0] inline fun <reified T> functionWithVararg(i: Int?, vararg t: T): T = t[0]
inline fun <reified T> T.functionWithReceiver(i: Int?): T = this
inline fun <reified T> T.`name with spaces`(i: Int?): T = this
inline fun <reified T> functionWithParamAnnotation(@Suppress("s") t: T): T = t inline fun <reified T> functionWithParamAnnotation(@Suppress("s") t: T): T = t
inline fun <reified T> functionUnresolved(@Suppress("s") t: Unresolved<T>): T = t inline fun <reified T> functionUnresolved(@Suppress("s") t: Unresolved<T>): T = t
+16
View File
@@ -21,6 +21,22 @@ UFile (package = )
UArrayAccessExpression UArrayAccessExpression
USimpleNameReferenceExpression (identifier = t) USimpleNameReferenceExpression (identifier = t)
ULiteralExpression (value = 0) ULiteralExpression (value = 0)
UMethod (name = functionWithReceiver)
UParameter (name = $this$functionWithReceiver)
UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UParameter (name = i)
UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UBlockExpression
UReturnExpression
UThisExpression (label = null)
UMethod (name = name with spaces)
UParameter (name = $this$name with spaces)
UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UParameter (name = i)
UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UBlockExpression
UReturnExpression
UThisExpression (label = null)
UMethod (name = functionWithParamAnnotation) UMethod (name = functionWithParamAnnotation)
UParameter (name = t) UParameter (name = t)
UAnnotation (fqName = org.jetbrains.annotations.Nullable) UAnnotation (fqName = org.jetbrains.annotations.Nullable)
@@ -5,6 +5,12 @@ public final class ReifiedParametersKt {
fun functionWithVararg(@org.jetbrains.annotations.Nullable i: int, @org.jetbrains.annotations.Nullable t: T) : T { fun functionWithVararg(@org.jetbrains.annotations.Nullable i: int, @org.jetbrains.annotations.Nullable t: T) : T {
return t[0] return t[0]
} }
fun functionWithReceiver(@org.jetbrains.annotations.Nullable $this$functionWithReceiver: T, @org.jetbrains.annotations.Nullable i: int) : T {
return this
}
fun name with spaces(@org.jetbrains.annotations.Nullable $this$name with spaces: T, @org.jetbrains.annotations.Nullable i: int) : T {
return this
}
fun functionWithParamAnnotation(@org.jetbrains.annotations.Nullable @kotlin.Suppress(names = "s") t: T) : T { fun functionWithParamAnnotation(@org.jetbrains.annotations.Nullable @kotlin.Suppress(names = "s") t: T) : T {
return t return t
} }
@@ -47,10 +47,14 @@ UFile (package = )
UReturnExpression UReturnExpression
ULiteralExpression (value = 42) ULiteralExpression (value = 42)
UMethod (name = function6) UMethod (name = function6)
UParameter (name = $this$function6)
UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UParameter (name = t) UParameter (name = t)
UAnnotation (fqName = org.jetbrains.annotations.Nullable) UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UParameter (name = i) UParameter (name = i)
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
UParameter (name = s) UParameter (name = s)
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
UBlockExpression UBlockExpression
UReturnExpression UReturnExpression
USimpleNameReferenceExpression (identifier = t) USimpleNameReferenceExpression (identifier = t)
@@ -95,10 +99,14 @@ UFile (package = )
UReturnExpression UReturnExpression
USimpleNameReferenceExpression (identifier = t) USimpleNameReferenceExpression (identifier = t)
UMethod (name = function11) UMethod (name = function11)
UParameter (name = $this$function11)
UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UParameter (name = t) UParameter (name = t)
UAnnotation (fqName = org.jetbrains.annotations.Nullable) UAnnotation (fqName = org.jetbrains.annotations.Nullable)
UParameter (name = i) UParameter (name = i)
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
UParameter (name = s) UParameter (name = s)
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
UBlockExpression UBlockExpression
UReturnExpression UReturnExpression
USimpleNameReferenceExpression (identifier = t) USimpleNameReferenceExpression (identifier = t)
+2 -2
View File
@@ -12,7 +12,7 @@ public final class ReifiedReturnTypeKt {
fun function5(@org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : int { fun function5(@org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : int {
return 42 return 42
} }
fun function6(@org.jetbrains.annotations.Nullable t: T, i: int, s: java.lang.String) : T { fun function6(@org.jetbrains.annotations.Nullable $this$function6: T, @org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : T {
return t return t
} }
fun function7(@org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : T { fun function7(@org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : T {
@@ -27,7 +27,7 @@ public final class ReifiedReturnTypeKt {
fun function10(@org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : T { fun function10(@org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : T {
return t return t
} }
fun function11(@org.jetbrains.annotations.Nullable t: T, i: int, s: java.lang.String) : T { fun function11(@org.jetbrains.annotations.Nullable $this$function11: T, @org.jetbrains.annotations.Nullable t: T, @org.jetbrains.annotations.NotNull i: int, @org.jetbrains.annotations.NotNull s: java.lang.String) : T {
return t return t
} }
} }
@@ -659,7 +659,7 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
assertEquals( assertEquals(
"parameter ${parameter.name} of method ${method.name} should be equals to converted from sourcePsi", "parameter ${parameter.name} of method ${method.name} should be equals to converted from sourcePsi",
parameter, parameter,
parameter.sourcePsi.toUElement() parameter.sourcePsi.toUElementOfType<UParameter>()
) )
assertEquals( assertEquals(
"parameter ${parameter.name} of method ${method.name} should be equals to converted from javaPsi", "parameter ${parameter.name} of method ${method.name} should be equals to converted from javaPsi",