[light classes] move LightClassUtil to light-classes-base module

^KT-53097
This commit is contained in:
Dmitry Gridin
2022-07-08 19:39:58 +02:00
committed by Space
parent e5519064f4
commit f18c5ed75a
3 changed files with 35 additions and 27 deletions
@@ -1,244 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.asJava
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.impl.java.stubs.PsiClassStub
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.PsiFileStub
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.isSetter
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.utils.checkWithAttachment
object LightClassUtil {
fun findClass(stub: StubElement<*>, predicate: (PsiClassStub<*>) -> Boolean): PsiClass? {
if (stub is PsiClassStub<*> && predicate(stub)) {
return stub.psi
}
if (stub is PsiClassStub<*> || stub is PsiFileStub<*>) {
for (child in stub.childrenStubs) {
val answer = findClass(child, predicate)
if (answer != null) return answer
}
}
return null
}
fun getLightClassAccessorMethod(accessor: KtPropertyAccessor): PsiMethod? =
getLightClassAccessorMethods(accessor).firstOrNull()
fun getLightClassAccessorMethods(accessor: KtPropertyAccessor): List<PsiMethod> {
val property = accessor.getNonStrictParentOfType<KtProperty>() ?: return emptyList()
val wrappers = getPsiMethodWrappers(property)
return wrappers.filter { wrapper ->
(accessor.isGetter && !JvmAbi.isSetterName(wrapper.name)) ||
(accessor.isSetter && JvmAbi.isSetterName(wrapper.name))
}.toList()
}
fun getLightFieldForCompanionObject(companionObject: KtClassOrObject): PsiField? {
val outerPsiClass = getWrappingClass(companionObject)
if (outerPsiClass != null) {
for (fieldOfParent in outerPsiClass.fields) {
if ((fieldOfParent is KtLightElement<*, *>) && fieldOfParent.kotlinOrigin === companionObject.originalElement) {
return fieldOfParent
}
}
}
return null
}
fun getLightClassPropertyMethods(property: KtProperty): PropertyAccessorsPsiMethods {
val getter = property.getter
val setter = property.setter
val getterWrapper = if (getter != null) getLightClassAccessorMethod(getter) else null
val setterWrapper = if (setter != null) getLightClassAccessorMethod(setter) else null
return extractPropertyAccessors(property, getterWrapper, setterWrapper)
}
fun getLightClassBackingField(declaration: KtDeclaration): PsiField? {
var psiClass: PsiClass = getWrappingClass(declaration) ?: return null
if (psiClass is KtLightClass) {
val origin = psiClass.kotlinOrigin
if (origin is KtObjectDeclaration && origin.isCompanion()) {
val containingClass = PsiTreeUtil.getParentOfType(origin, KtClass::class.java)
if (containingClass != null) {
val containingLightClass = containingClass.toLightClass()
if (containingLightClass != null) {
psiClass = containingLightClass
}
}
}
}
for (field in psiClass.fields) {
if (field is KtLightField && field.kotlinOrigin === declaration) {
return field
}
}
return null
}
fun getLightClassPropertyMethods(parameter: KtParameter): PropertyAccessorsPsiMethods {
return extractPropertyAccessors(parameter, null, null)
}
fun getLightClassMethod(function: KtFunction): PsiMethod? {
return getPsiMethodWrapper(function)
}
/**
* Returns the light method generated from the parameter of an annotation class.
*/
fun getLightClassMethod(parameter: KtParameter): PsiMethod? {
return getPsiMethodWrapper(parameter)
}
fun getLightClassMethods(function: KtFunction): List<PsiMethod> {
return getPsiMethodWrappers(function).toList()
}
fun getLightClassMethodsByName(function: KtFunction, name: String): Sequence<KtLightMethod> {
return getPsiMethodWrappers(function, name)
}
private fun getPsiMethodWrapper(declaration: KtDeclaration): PsiMethod? {
return getPsiMethodWrappers(declaration).firstOrNull()
}
private fun getPsiMethodWrappers(declaration: KtDeclaration, name: String? = null): Sequence<KtLightMethod> =
getWrappingClasses(declaration).flatMap { it.methods.asSequence() }
.filterIsInstance<KtLightMethod>()
.filter { name == null || name == it.name }
.filter { it.kotlinOrigin === declaration || it.navigationElement === declaration }
private fun getWrappingClass(declaration: KtDeclaration): PsiClass? {
if (declaration is KtParameter) {
val constructorClass = KtPsiUtil.getClassIfParameterIsProperty(declaration)
if (constructorClass != null) {
return constructorClass.toLightClass()
}
}
var ktDeclaration = declaration
if (ktDeclaration is KtPropertyAccessor) {
ktDeclaration = ktDeclaration.property
}
if (ktDeclaration is KtConstructor<*>) {
return ktDeclaration.getContainingClassOrObject().toLightClass()
}
val parent = ktDeclaration.parent
if (parent is KtFile) {
// top-level declaration
return findFileFacade(parent)
} else if (parent is KtClassBody) {
checkWithAttachment(parent.parent is KtClassOrObject, {
"Bad parent: ${parent.parent?.javaClass}"
}) {
it.withAttachment("parent", parent.text)
}
return (parent.parent as KtClassOrObject).toLightClass()
}
return null
}
private fun findFileFacade(ktFile: KtFile): PsiClass? {
val fqName = ktFile.javaFileFacadeFqName
val project = ktFile.project
val classesWithMatchingFqName =
JavaElementFinder.getInstance(project).findClasses(fqName.asString(), GlobalSearchScope.allScope(project))
return classesWithMatchingFqName.singleOrNull() ?: classesWithMatchingFqName.find { psiClass ->
psiClass is KtLightClassForFacade && psiClass.files.any { it.virtualFile == ktFile.virtualFile } ||
psiClass.containingFile?.virtualFile == ktFile.virtualFile
}
}
private fun getWrappingClasses(declaration: KtDeclaration): Sequence<PsiClass> {
val wrapperClass = getWrappingClass(declaration) ?: return emptySequence()
val wrapperClassOrigin = (wrapperClass as KtLightClass).kotlinOrigin
if (wrapperClassOrigin is KtObjectDeclaration && wrapperClassOrigin.isCompanion() && wrapperClass.parent is PsiClass) {
return sequenceOf(wrapperClass, wrapperClass.parent as PsiClass)
}
return sequenceOf(wrapperClass)
}
fun canGenerateLightClass(declaration: KtDeclaration): Boolean {
//noinspection unchecked
return PsiTreeUtil.getParentOfType(declaration, KtFunction::class.java, KtProperty::class.java) == null
}
private fun extractPropertyAccessors(
ktDeclaration: KtDeclaration,
specialGetter: PsiMethod?, specialSetter: PsiMethod?
): PropertyAccessorsPsiMethods {
val (setters, getters) = getPsiMethodWrappers(ktDeclaration).partition { it.isSetter }
val allGetters = listOfNotNull(specialGetter) + getters.filterNot { it == specialGetter }
val allSetters = listOfNotNull(specialSetter) + setters.filterNot { it == specialSetter }
val backingField = getLightClassBackingField(ktDeclaration)
val additionalAccessors = allGetters.drop(1) + allSetters.drop(1)
return PropertyAccessorsPsiMethods(
allGetters.firstOrNull(),
allSetters.firstOrNull(),
backingField,
additionalAccessors
)
}
class PropertyAccessorsPsiMethods(
val getter: PsiMethod?,
val setter: PsiMethod?,
val backingField: PsiField?,
additionalAccessors: List<PsiMethod>
) : Iterable<PsiMethod> {
private val allMethods: List<PsiMethod>
val allDeclarations: List<PsiNamedElement>
init {
allMethods = arrayListOf()
arrayOf(getter, setter).filterNotNullTo(allMethods)
additionalAccessors.filterIsInstanceTo<PsiMethod, MutableList<PsiMethod>>(allMethods)
allDeclarations = arrayListOf()
arrayOf<PsiNamedElement?>(getter, setter, backingField).filterNotNullTo(allDeclarations)
allDeclarations.addAll(additionalAccessors)
}
override fun iterator(): Iterator<PsiMethod> = allMethods.iterator()
}
}
fun KtNamedDeclaration.getAccessorLightMethods(): LightClassUtil.PropertyAccessorsPsiMethods {
return when (this) {
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this)
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this)
else -> throw IllegalStateException("Unexpected property type: $this")
}
}
@@ -10,7 +10,6 @@ import com.intellij.psi.*
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.MethodSignature
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KtLightClass
@@ -163,29 +162,3 @@ abstract class KtLightMethodImpl protected constructor(
abstract override fun findSuperMethods(parentClass: PsiClass?): Array<out PsiMethod>
}
fun KtLightMethod.isTraitFakeOverride(): Boolean {
val methodOrigin = this.kotlinOrigin
if (!(methodOrigin is KtNamedFunction || methodOrigin is KtPropertyAccessor || methodOrigin is KtProperty)) {
return false
}
val parentOfMethodOrigin = PsiTreeUtil.getParentOfType(methodOrigin, KtClassOrObject::class.java)
val thisClassDeclaration = this.containingClass.kotlinOrigin
// Method was generated from declaration in some other trait
return (parentOfMethodOrigin != null && thisClassDeclaration !== parentOfMethodOrigin && KtPsiUtil.isTrait(parentOfMethodOrigin))
}
fun KtLightMethod.isAccessor(getter: Boolean): Boolean {
val origin = kotlinOrigin as? KtCallableDeclaration ?: return false
if (origin !is KtProperty && origin !is KtParameter) return false
val expectedParametersCount = (if (getter) 0 else 1) + (if (origin.receiverTypeReference != null) 1 else 0)
return parameterList.parametersCount == expectedParametersCount
}
val KtLightMethod.isGetter: Boolean
get() = isAccessor(true)
val KtLightMethod.isSetter: Boolean
get() = isAccessor(false)