From 6fe989b4b45308d58c95a847b5a95385d3378636 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 28 Oct 2022 17:39:30 +0900 Subject: [PATCH] [LC] Provide proper parent element for private properties (KTIJ-23449) Before, no parent element was returned for an object literal inside a private property initializer, as only the getter was considered as a potential parent. Relevant tests are added on the IDE side (see 'LightClassBehaviorTest'). --- .../asJava/classes/lightClassPlatformUtils.kt | 128 ++++++++++-------- 1 file changed, 74 insertions(+), 54 deletions(-) diff --git a/analysis/light-classes-base/src/org/jetbrains/kotlin/asJava/classes/lightClassPlatformUtils.kt b/analysis/light-classes-base/src/org/jetbrains/kotlin/asJava/classes/lightClassPlatformUtils.kt index d9c3a23be7b..a0888c5f06f 100644 --- a/analysis/light-classes-base/src/org/jetbrains/kotlin/asJava/classes/lightClassPlatformUtils.kt +++ b/analysis/light-classes-base/src/org/jetbrains/kotlin/asJava/classes/lightClassPlatformUtils.kt @@ -7,85 +7,105 @@ package org.jetbrains.kotlin.asJava.classes import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement +import com.intellij.psi.PsiField +import com.intellij.psi.PsiMember import com.intellij.psi.PsiMethod import com.intellij.psi.augment.PsiAugmentProvider import com.intellij.psi.impl.light.LightClass +import com.intellij.psi.impl.light.LightField import com.intellij.psi.impl.light.LightMethod import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.parents internal fun collectAugments(element: PsiElement, type: Class): List { return PsiAugmentProvider.collectAugments(element, type, null) } fun getParentForLocalDeclaration(classOrObject: KtClassOrObject): PsiElement? { - fun getParentByPsiMethod(method: PsiMethod?, name: String?, forceMethodWrapping: Boolean): PsiElement? { - if (method == null || name == null) return null + fun wrapMember(member: T, forceWrapping: Boolean, wrapper: (T, PsiClass) -> T): T? { + val containingClass = member.containingClass ?: return null - var containingClass: PsiClass? = method.containingClass ?: return null - - val currentFileName = classOrObject.containingFile.name - - var createWrapper = forceMethodWrapping - // Use PsiClass wrapper instead of package light class to avoid names like "FooPackage" in Type Hierarchy and related views if (containingClass is KtLightClassForFacade) { - containingClass = object : LightClass(containingClass as KtLightClassForFacade, KotlinLanguage.INSTANCE) { - override fun getName(): String = currentFileName + val facadeClassName = classOrObject.containingFile.name + val wrappedClass = object : LightClass(containingClass, KotlinLanguage.INSTANCE) { + override fun getName(): String = facadeClassName } - createWrapper = true + + return wrapper(member, wrappedClass) } - if (createWrapper) { - return object : LightMethod(classOrObject.manager, method, containingClass!!, KotlinLanguage.INSTANCE) { - override fun getParent(): PsiElement = getContainingClass() + return if (forceWrapping) wrapper(member, containingClass) else member + } + + fun wrapMethod(member: PsiMethod, name: String = member.name, forceWrapping: Boolean): PsiMethod? { + return wrapMember(member, forceWrapping) { method, containingClass -> + object : LightMethod(containingClass.manager, method, containingClass, KotlinLanguage.INSTANCE) { + override fun getParent(): PsiElement = containingClass override fun getName(): String = name } } - - return method } - var declaration: PsiElement? = KtPsiUtil.getTopmostParentOfTypes( - classOrObject, - KtNamedFunction::class.java, - KtConstructor::class.java, - KtProperty::class.java, - KtAnonymousInitializer::class.java, - KtParameter::class.java - ) - - if (declaration is KtParameter) { - declaration = declaration.getStrictParentOfType() - } - - if (declaration is KtFunction) { - return getParentByPsiMethod( - LightClassUtil.getLightClassMethod(declaration), - declaration.name, - forceMethodWrapping = false - ) - } - - // Represent the property as a fake method with the same name - if (declaration is KtProperty) { - return getParentByPsiMethod( - LightClassUtil.getLightClassPropertyMethods(declaration).getter, - declaration.name, - forceMethodWrapping = true - ) - } - - if (declaration is KtAnonymousInitializer) { - val parent = declaration.parent - val grandparent = parent.parent - - if (parent is KtClassBody && grandparent is KtClassOrObject) { - return grandparent.toLightClass() + fun wrapField(member: PsiField, forceWrapping: Boolean): PsiField? { + return wrapMember(member, forceWrapping) { field, containingClass -> + object : LightField(containingClass.manager, field, containingClass) { + override fun getParent(): PsiElement = containingClass + } } } - return if (declaration is KtClass) declaration.toLightClass() else null + fun map(declaration: KtElement): PsiElement? { + when (declaration) { + is KtFunction -> { + val psiMethod = LightClassUtil.getLightClassMethod(declaration) + if (psiMethod != null) { + return wrapMethod(psiMethod, declaration.name ?: psiMethod.name, forceWrapping = false) + } + } + + is KtPropertyAccessor -> { + val psiMethod = LightClassUtil.getLightClassAccessorMethod(declaration) + if (psiMethod != null) { + return wrapMethod(psiMethod, forceWrapping = true) + } + } + + is KtProperty -> { + if (!declaration.isLocal) { + val propertyMethods = LightClassUtil.getLightClassPropertyMethods(declaration) + val psiMethod = propertyMethods.getter + if (psiMethod != null) { + return wrapMethod(psiMethod, forceWrapping = true) + } + + val psiField = propertyMethods.backingField + if (psiField != null) { + return wrapField(psiField, forceWrapping = false) + } + } + } + + is KtAnonymousInitializer -> { + val parent = declaration.parent + val grandparent = parent.parent + + if (parent is KtClassBody && grandparent is KtClassOrObject) { + return grandparent.toLightClass() + } + } + + is KtClass -> { + return declaration.toLightClass() + } + } + + return null + } + + return classOrObject.parents + .filterIsInstance() + .firstNotNullOfOrNull { map(it) } } \ No newline at end of file