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

^KT-53097
This commit is contained in:
Dmitry Gridin
2022-07-08 19:44:18 +02:00
committed by Space
parent f18c5ed75a
commit 83f4a9752b
3 changed files with 156 additions and 157 deletions
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isPrivate
import org.jetbrains.kotlin.psi.psiUtil.isTopLevelKtOrJavaMember
import org.jetbrains.kotlin.psi.psiUtil.*
/**
* Can be null in scripts and for elements from non-jvm modules.
@@ -48,6 +45,83 @@ fun KtFile.findFacadeClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(pr
fun KtScript.toLightClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(project).getLightClassForScript(this)
fun KtElement.toLightElements(): List<PsiNamedElement> = when (this) {
is KtClassOrObject -> listOfNotNull(toLightClass())
is KtNamedFunction,
is KtConstructor<*> -> LightClassUtil.getLightClassMethods(this as KtFunction)
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).allDeclarations
is KtPropertyAccessor -> listOfNotNull(LightClassUtil.getLightClassAccessorMethod(this))
is KtParameter -> mutableListOf<PsiNamedElement>().also { elements ->
toPsiParameters().toCollection(elements)
LightClassUtil.getLightClassPropertyMethods(this).toCollection(elements)
toAnnotationLightMethod()?.let(elements::add)
}
is KtTypeParameter -> toPsiTypeParameters()
is KtFile -> listOfNotNull(findFacadeClass())
else -> listOf()
}
fun PsiElement.toLightMethods(): List<PsiMethod> = when (this) {
is KtFunction -> LightClassUtil.getLightClassMethods(this)
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).toList()
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).toList()
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(this)
is KtClass -> listOfNotNull(toLightClass()?.constructors?.firstOrNull())
is PsiMethod -> listOf(this)
else -> listOf()
}
fun PsiElement.getRepresentativeLightMethod(): PsiMethod? = when (this) {
is KtFunction -> LightClassUtil.getLightClassMethod(this)
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).getter
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).getter
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethod(this)
is PsiMethod -> this
else -> null
}
fun KtParameter.toPsiParameters(): Collection<PsiParameter> {
val paramList = getNonStrictParentOfType<KtParameterList>() ?: return emptyList()
val paramIndex = paramList.parameters.indexOf(this)
if (paramIndex < 0) return emptyList()
val owner = paramList.parent
val lightParamIndex = if (owner is KtDeclaration && owner.isExtensionDeclaration()) paramIndex + 1 else paramIndex
val methods: Collection<PsiMethod> = when (owner) {
is KtFunction -> LightClassUtil.getLightClassMethods(owner)
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(owner)
else -> null
} ?: return emptyList()
return methods.mapNotNull { it.parameterList.parameters.getOrNull(lightParamIndex) }
}
private fun KtParameter.toAnnotationLightMethod(): PsiMethod? {
val parent = ownerFunction as? KtPrimaryConstructor ?: return null
val containingClass = parent.getContainingClassOrObject()
if (!containingClass.isAnnotation()) return null
return LightClassUtil.getLightClassMethod(this)
}
fun KtParameter.toLightGetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).getter
fun KtParameter.toLightSetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).setter
fun KtTypeParameter.toPsiTypeParameters(): List<PsiTypeParameter> {
val paramList = getNonStrictParentOfType<KtTypeParameterList>() ?: return listOf()
val paramIndex = paramList.parameters.indexOf(this)
val ktDeclaration = paramList.getNonStrictParentOfType<KtDeclaration>() ?: return listOf()
val lightOwners = ktDeclaration.toLightElements()
return lightOwners.mapNotNull { lightOwner ->
(lightOwner as? PsiTypeParameterListOwner)?.typeParameters?.getOrNull(paramIndex)
}
}
// Returns original declaration if given PsiElement is a Kotlin light element, and element itself otherwise
val PsiElement.unwrapped: PsiElement?
get() = when (this) {
@@ -60,11 +134,10 @@ val PsiElement.unwrapped: PsiElement?
val PsiElement.namedUnwrappedElement: PsiNamedElement?
get() = unwrapped?.getNonStrictParentOfType()
val KtClassOrObject.hasInterfaceDefaultImpls: Boolean
get() = this is KtClass && isInterface() && hasNonAbstractMembers(this)
private fun hasNonAbstractMembers(ktInterface: KtClass): Boolean = ktInterface.declarations.any(::isNonAbstractMember)
val KtClassOrObject.hasRepeatableAnnotationContainer: Boolean
get() = this is KtClass &&
isAnnotation() &&
@@ -81,6 +154,8 @@ val KtClassOrObject.hasRepeatableAnnotationContainer: Boolean
return hasRepeatableAnnotation
}
private fun hasNonAbstractMembers(ktInterface: KtClass): Boolean = ktInterface.declarations.any(::isNonAbstractMember)
private fun isNonAbstractMember(member: KtDeclaration?): Boolean =
(member is KtNamedFunction && member.hasBody()) ||
(member is KtProperty && (member.hasDelegateExpressionOrInitializer() || member.getter?.hasBody() ?: false || member.setter?.hasBody() ?: false))
@@ -91,6 +166,31 @@ fun FqName.defaultImplsChild() = child(DEFAULT_IMPLS_CLASS_NAME)
private val REPEATABLE_ANNOTATION_CONTAINER_NAME = Name.identifier(JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME)
fun FqName.repeatableAnnotationContainerChild() = child(REPEATABLE_ANNOTATION_CONTAINER_NAME)
@Suppress("unused")
fun KtElement.toLightAnnotation(): PsiAnnotation? {
val ktDeclaration = getStrictParentOfType<KtModifierList>()?.parent as? KtDeclaration ?: return null
for (lightElement in ktDeclaration.toLightElements()) {
if (lightElement !is PsiModifierListOwner) continue
for (rootAnnotation in lightElement.modifierList?.annotations ?: continue) {
for (annotation in rootAnnotation.withNestedAnnotations()) {
if (annotation is KtLightElement<*, *> && annotation.kotlinOrigin == this)
return annotation
}
}
}
return null
}
private fun PsiAnnotation.withNestedAnnotations(): Sequence<PsiAnnotation> {
fun handleValue(memberValue: PsiAnnotationMemberValue?): Sequence<PsiAnnotation> = when (memberValue) {
is PsiArrayInitializerMemberValue -> memberValue.initializers.asSequence().flatMap { handleValue(it) }
is PsiAnnotation -> memberValue.withNestedAnnotations()
else -> emptySequence()
}
return sequenceOf(this) + parameterList.attributes.asSequence().flatMap { handleValue(it.value) }
}
fun propertyNameByAccessor(name: String, accessor: KtLightMethod): String? {
val toRename = accessor.kotlinOrigin ?: return null
if (toRename !is KtProperty && toRename !is KtParameter) return null
@@ -148,6 +248,7 @@ fun fastCheckIsNullabilityApplied(lightElement: KtLightElement<*, PsiModifierLis
return true
}
private val PsiMethod.canBeGetter: Boolean
get() = JvmAbi.isGetterName(name) && parameters.isEmpty() && returnTypeElement?.textMatches("void") != true
@@ -1,151 +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.*
import org.jetbrains.kotlin.asJava.classes.runReadAction
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.types.TypeUtils
fun KtElement.toLightElements(): List<PsiNamedElement> = when (this) {
is KtClassOrObject -> listOfNotNull(toLightClass())
is KtNamedFunction,
is KtConstructor<*> -> LightClassUtil.getLightClassMethods(this as KtFunction)
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).allDeclarations
is KtPropertyAccessor -> listOfNotNull(LightClassUtil.getLightClassAccessorMethod(this))
is KtParameter -> mutableListOf<PsiNamedElement>().also { elements ->
toPsiParameters().toCollection(elements)
LightClassUtil.getLightClassPropertyMethods(this).toCollection(elements)
toAnnotationLightMethod()?.let(elements::add)
}
is KtTypeParameter -> toPsiTypeParameters()
is KtFile -> listOfNotNull(findFacadeClass())
else -> listOf()
}
fun PsiElement.toLightMethods(): List<PsiMethod> = when (this) {
is KtFunction -> LightClassUtil.getLightClassMethods(this)
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).toList()
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).toList()
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(this)
is KtClass -> listOfNotNull(toLightClass()?.constructors?.firstOrNull())
is PsiMethod -> listOf(this)
else -> listOf()
}
fun PsiElement.getRepresentativeLightMethod(): PsiMethod? = when (this) {
is KtFunction -> LightClassUtil.getLightClassMethod(this)
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this).getter
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(this).getter
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethod(this)
is PsiMethod -> this
else -> null
}
fun KtParameter.toPsiParameters(): Collection<PsiParameter> {
val paramList = getNonStrictParentOfType<KtParameterList>() ?: return emptyList()
val paramIndex = paramList.parameters.indexOf(this)
if (paramIndex < 0) return emptyList()
val owner = paramList.parent
val lightParamIndex = if (owner is KtDeclaration && owner.isExtensionDeclaration()) paramIndex + 1 else paramIndex
val methods: Collection<PsiMethod> = when (owner) {
is KtFunction -> LightClassUtil.getLightClassMethods(owner)
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethods(owner)
else -> null
} ?: return emptyList()
return methods.mapNotNull { it.parameterList.parameters.getOrNull(lightParamIndex) }
}
private fun KtParameter.toAnnotationLightMethod(): PsiMethod? {
val parent = ownerFunction as? KtPrimaryConstructor ?: return null
val containingClass = parent.getContainingClassOrObject()
if (!containingClass.isAnnotation()) return null
return LightClassUtil.getLightClassMethod(this)
}
fun KtParameter.toLightGetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).getter
fun KtParameter.toLightSetter(): PsiMethod? = LightClassUtil.getLightClassPropertyMethods(this).setter
fun KtTypeParameter.toPsiTypeParameters(): List<PsiTypeParameter> {
val paramList = getNonStrictParentOfType<KtTypeParameterList>() ?: return listOf()
val paramIndex = paramList.parameters.indexOf(this)
val ktDeclaration = paramList.getNonStrictParentOfType<KtDeclaration>() ?: return listOf()
val lightOwners = ktDeclaration.toLightElements()
return lightOwners.mapNotNull { lightOwner ->
(lightOwner as? PsiTypeParameterListOwner)?.typeParameters?.getOrNull(paramIndex)
}
}
@Suppress("unused")
fun KtElement.toLightAnnotation(): PsiAnnotation? {
val ktDeclaration = getStrictParentOfType<KtModifierList>()?.parent as? KtDeclaration ?: return null
for (lightElement in ktDeclaration.toLightElements()) {
if (lightElement !is PsiModifierListOwner) continue
for (rootAnnotation in lightElement.modifierList?.annotations ?: continue) {
for (annotation in rootAnnotation.withNestedAnnotations()) {
if (annotation is KtLightElement<*, *> && annotation.kotlinOrigin == this)
return annotation
}
}
}
return null
}
private fun PsiAnnotation.withNestedAnnotations(): Sequence<PsiAnnotation> {
fun handleValue(memberValue: PsiAnnotationMemberValue?): Sequence<PsiAnnotation> = when (memberValue) {
is PsiArrayInitializerMemberValue -> memberValue.initializers.asSequence().flatMap { handleValue(it) }
is PsiAnnotation -> memberValue.withNestedAnnotations()
else -> emptySequence()
}
return sequenceOf(this) + parameterList.attributes.asSequence().flatMap { handleValue(it.value) }
}
fun KtLightMethod.checkIsMangled(): Boolean {
val demangledName = KotlinTypeMapper.InternalNameMapper.demangleInternalName(name) ?: return false
val originalName = propertyNameByAccessor(demangledName, this) ?: demangledName
return originalName == kotlinOrigin?.name
}
fun computeExpression(expression: PsiElement): Any? {
fun evalConstantValue(constantValue: ConstantValue<*>): Any? =
if (constantValue is ArrayValue) {
val items = constantValue.value.map { evalConstantValue(it) }
items.singleOrNull() ?: items
} else {
constantValue.value
}
val expressionToCompute = when (expression) {
is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null
else -> return null
}
val generationSupport = LightClassGenerationSupport.getInstance(expressionToCompute.project)
val evaluator = generationSupport.createConstantEvaluator(expressionToCompute)
val constant = runReadAction {
val evaluatorTrace = DelegatingBindingTrace(generationSupport.analyze(expressionToCompute), "Evaluating annotation argument")
evaluator.evaluateExpression(expressionToCompute, evaluatorTrace)
} ?: return null
if (constant.isError) return null
return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE))
}
@@ -0,0 +1,49 @@
/*
* 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.PsiElement
import org.jetbrains.kotlin.asJava.classes.runReadAction
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.types.TypeUtils
fun KtLightMethod.checkIsMangled(): Boolean {
val demangledName = KotlinTypeMapper.InternalNameMapper.demangleInternalName(name) ?: return false
val originalName = propertyNameByAccessor(demangledName, this) ?: demangledName
return originalName == kotlinOrigin?.name
}
fun computeExpression(expression: PsiElement): Any? {
fun evalConstantValue(constantValue: ConstantValue<*>): Any? =
if (constantValue is ArrayValue) {
val items = constantValue.value.map { evalConstantValue(it) }
items.singleOrNull() ?: items
} else {
constantValue.value
}
val expressionToCompute = when (expression) {
is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null
else -> return null
}
val generationSupport = LightClassGenerationSupport.getInstance(expressionToCompute.project)
val evaluator = generationSupport.createConstantEvaluator(expressionToCompute)
val constant = runReadAction {
val evaluatorTrace = DelegatingBindingTrace(generationSupport.analyze(expressionToCompute), "Evaluating annotation argument")
evaluator.evaluateExpression(expressionToCompute, evaluatorTrace)
} ?: return null
if (constant.isError) return null
return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE))
}