[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').
This commit is contained in:
+74
-54
@@ -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 <Psi : PsiElement> collectAugments(element: PsiElement, type: Class<out Psi>): List<Psi> {
|
||||
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 <T : PsiMember> 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<KtNamedDeclaration>()
|
||||
}
|
||||
|
||||
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<KtElement>()
|
||||
.firstNotNullOfOrNull { map(it) }
|
||||
}
|
||||
Reference in New Issue
Block a user