[light classes] move descriptors from KtLightClassForSourceDeclaration to KtLightClassImpl

^KT-53097
This commit is contained in:
Dmitry Gridin
2022-07-07 20:46:08 +02:00
committed by Space
parent afdf378b32
commit 47fa444792
4 changed files with 150 additions and 144 deletions
@@ -108,7 +108,7 @@ class CliKotlinAsJavaSupport(
).mapNotNull { member -> (member as? PackageViewDescriptor)?.fqName }
}
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? = KtLightClassForSourceDeclaration.create(classOrObject)
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? = KtLightClassImpl.create(classOrObject)
override fun getLightClassForScript(script: KtScript): KtLightClassForScript? = KtLightClassForScript.create(script)
@@ -85,7 +85,7 @@ abstract class KtLightClassForScript(val script: KtScript) : KtLightClassBase(sc
// inner classes with null names can't be searched for and can't be used from java anyway
// we can't prohibit creating light classes with null names either since they can contain members
.filter { it.name != null }
.mapNotNull { KtLightClassForSourceDeclaration.create(it) }
.mapNotNull { KtLightClassImpl.create(it) }
}
override fun getInitializers(): Array<PsiClassInitializer> = PsiClassInitializer.EMPTY_ARRAY
@@ -11,23 +11,15 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.source.PsiImmediateClassType
import com.intellij.psi.impl.source.tree.TreeUtil
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.search.SearchScope
import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiUtilCore
import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
import org.jetbrains.kotlin.asJava.hasInterfaceDefaultImpls
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
@@ -38,16 +30,6 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import javax.swing.Icon
private class KtLightClassModifierList(containingClass: KtLightClassForSourceDeclaration, computeModifiers: () -> Set<String>) :
KtLightModifierList<KtLightClassForSourceDeclaration>(containingClass) {
private val modifiers by lazyPub { computeModifiers() }
override fun hasModifierProperty(name: String): Boolean =
if (name != PsiModifier.FINAL) name in modifiers else owner.isFinal(PsiModifier.FINAL in modifiers)
}
abstract class KtLightClassForSourceDeclaration(
protected val classOrObject: KtClassOrObject,
protected val jvmDefaultMode: JvmDefaultMode,
@@ -75,7 +57,6 @@ abstract class KtLightClassForSourceDeclaration(
private val _extendsList by lazyPub { createExtendsList() }
private val _implementsList by lazyPub { createImplementsList() }
private val _deprecated by lazyPub { classOrObject.isDeprecated() }
protected abstract fun createExtendsList(): PsiReferenceList?
protected abstract fun createImplementsList(): PsiReferenceList?
@@ -86,35 +67,7 @@ abstract class KtLightClassForSourceDeclaration(
abstract override fun getParent(): PsiElement?
abstract override fun getQualifiedName(): String?
private val _containingFile: PsiFile by lazyPub {
object : FakeFileForLightClass(
classOrObject.containingKtFile,
{ if (classOrObject.isTopLevel()) this else create(getOutermostClassOrObject(classOrObject))!! },
) {
override fun findReferenceAt(offset: Int) = ktFile.findReferenceAt(offset)
override fun processDeclarations(
processor: PsiScopeProcessor,
state: ResolveState,
lastParent: PsiElement?,
place: PsiElement
): Boolean {
if (!super.processDeclarations(processor, state, lastParent, place)) return false
// We have to explicitly process package declarations if current file belongs to default package
// so that Java resolve can find classes located in that package
val packageName = packageName
if (!packageName.isEmpty()) return true
val aPackage = JavaPsiFacade.getInstance(myManager.project).findPackage(packageName)
if (aPackage != null && !aPackage.processDeclarations(processor, state, null, place)) return false
return true
}
}
}
override fun getContainingFile(): PsiFile? = _containingFile
abstract override fun getContainingFile(): PsiFile?
override fun getNavigationElement(): PsiElement = classOrObject
@@ -140,17 +93,6 @@ abstract class KtLightClassForSourceDeclaration(
override fun hashCode(): Int = classOrObject.hashCode() * 31 + jvmDefaultMode.hashCode()
override fun getContainingClass(): PsiClass? {
if (classOrObject.parent === classOrObject.containingFile) return null
val containingClassOrObject = (classOrObject.parent as? KtClassBody)?.parent as? KtClassOrObject
if (containingClassOrObject != null) {
return create(containingClassOrObject)
}
// TODO: should return null
return super.getContainingClass()
}
private val _typeParameterList: PsiTypeParameterList by lazyPub { buildTypeParameterList() }
@@ -162,15 +104,11 @@ abstract class KtLightClassForSourceDeclaration(
override fun getName(): String? = classOrObject.nameAsName?.asString()
private val _modifierList: PsiModifierList by lazyPub { KtLightClassModifierList(this) { computeModifiers() } }
override fun getModifierList(): PsiModifierList? = _modifierList
abstract override fun getModifierList(): PsiModifierList?
protected abstract fun computeModifiers(): Set<String>
override fun hasModifierProperty(@NonNls name: String): Boolean = modifierList?.hasModifierProperty(name) ?: false
override fun isDeprecated(): Boolean = _deprecated
abstract override fun isDeprecated(): Boolean
override fun isInterface(): Boolean {
if (classOrObject !is KtClass) return false
@@ -195,25 +133,7 @@ abstract class KtLightClassForSourceDeclaration(
override fun toString() = "${this::class.java.simpleName}:${classOrObject.getDebugText()}"
override fun getOwnInnerClasses(): List<PsiClass> {
val result = ArrayList<PsiClass>()
classOrObject.declarations.filterIsInstance<KtClassOrObject>()
// workaround for ClassInnerStuffCache not supporting classes with null names, see KT-13927
// inner classes with null names can't be searched for and can't be used from java anyway
// we can't prohibit creating light classes with null names either since they can contain members
.filter { it.name != null }
.mapNotNullTo(result) {
create(it)
}
if (classOrObject.hasInterfaceDefaultImpls && jvmDefaultMode != JvmDefaultMode.ALL_INCOMPATIBLE) {
result.add(createClassForInterfaceDefaultImpls())
}
return result
}
protected abstract fun createClassForInterfaceDefaultImpls(): PsiClass
abstract override fun getOwnInnerClasses(): List<PsiClass>
override fun getUseScope(): SearchScope = kotlinOrigin.useScope
@@ -225,32 +145,6 @@ abstract class KtLightClassForSourceDeclaration(
override fun getExtendsList(): PsiReferenceList? = _extendsList
override fun getImplementsList(): PsiReferenceList? = _implementsList
companion object {
fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? =
CachedValuesManager.getCachedValue(classOrObject) {
CachedValueProvider.Result.create(
createNoCache(classOrObject),
KotlinModificationTrackerService.getInstance(classOrObject.project).outOfBlockModificationTracker,
)
}
fun createNoCache(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
val containingFile = classOrObject.containingFile
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return null
}
if (classOrObject.shouldNotBeVisibleAsLightClass()) {
return null
}
return LightClassGenerationSupport.getInstance(classOrObject.project).run {
createUltraLightClass(classOrObject)
}
}
}
abstract override fun getSupers(): Array<PsiClass>
abstract override fun getSuperTypes(): Array<PsiClassType>
@@ -5,20 +5,35 @@
package org.jetbrains.kotlin.asJava.classes
import com.intellij.psi.CommonClassNames
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiModifier
import com.intellij.psi.*
import com.intellij.psi.impl.InheritanceImplUtil
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
import org.jetbrains.kotlin.asJava.hasInterfaceDefaultImpls
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtClassBody
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.resolve.DescriptorUtils
private class KtLightClassModifierList(containingClass: KtLightClassForSourceDeclaration, computeModifiers: () -> Set<String>) :
KtLightModifierList<KtLightClassForSourceDeclaration>(containingClass) {
private val modifiers by lazyPub { computeModifiers() }
override fun hasModifierProperty(name: String): Boolean =
if (name != PsiModifier.FINAL) name in modifiers else owner.isFinal(PsiModifier.FINAL in modifiers)
}
// light class for top level or (inner/nested of top level) source declarations
abstract class KtLightClassImpl @JvmOverloads constructor(
classOrObject: KtClassOrObject,
@@ -28,6 +43,15 @@ abstract class KtLightClassImpl @JvmOverloads constructor(
fun getDescriptor() =
LightClassGenerationSupport.getInstance(project).resolveToDescriptor(classOrObject) as? ClassDescriptor
private val _deprecated by lazyPub { classOrObject.isDeprecated() }
override fun isDeprecated(): Boolean = _deprecated
private val _modifierList: PsiModifierList by lazyPub { KtLightClassModifierList(this) { computeModifiers() } }
override fun getModifierList(): PsiModifierList? = _modifierList
override fun computeModifiers(): Set<String> {
val psiModifiers = hashSetOf<String>()
@@ -110,41 +134,129 @@ abstract class KtLightClassImpl @JvmOverloads constructor(
containingClass
abstract override fun copy(): PsiElement
}
private fun checkSuperTypeByFQName(classDescriptor: ClassDescriptor, qualifiedName: String, deep: Boolean): Boolean {
if (CommonClassNames.JAVA_LANG_OBJECT == qualifiedName) return true
private val _containingFile: PsiFile by lazyPub {
object : FakeFileForLightClass(
classOrObject.containingKtFile,
{ if (classOrObject.isTopLevel()) this else create(getOutermostClassOrObject(classOrObject))!! },
) {
override fun findReferenceAt(offset: Int) = ktFile.findReferenceAt(offset)
if (qualifiedName == DescriptorUtils.getFqName(classDescriptor).asString()) return true
override fun processDeclarations(
processor: PsiScopeProcessor,
state: ResolveState,
lastParent: PsiElement?,
place: PsiElement
): Boolean {
if (!super.processDeclarations(processor, state, lastParent, place)) return false
val fqName = FqNameUnsafe(qualifiedName)
val mappedQName =
if (fqName.isSafe)
JavaToKotlinClassMap.mapJavaToKotlin(fqName.toSafe())?.asSingleFqName()?.asString()
else null
if (qualifiedName == mappedQName) return true
// We have to explicitly process package declarations if current file belongs to default package
// so that Java resolve can find classes located in that package
val packageName = packageName
if (packageName.isNotEmpty()) return true
for (superType in classDescriptor.typeConstructor.supertypes) {
val superDescriptor = superType.constructor.declarationDescriptor
val aPackage = JavaPsiFacade.getInstance(myManager.project).findPackage(packageName)
if (aPackage != null && !aPackage.processDeclarations(processor, state, null, place)) return false
if (superDescriptor is ClassDescriptor) {
val superQName = DescriptorUtils.getFqName(superDescriptor).asString()
if (superQName == qualifiedName || superQName == mappedQName) return true
if (deep) {
if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) {
return true
}
return true
}
}
}
return false
}
override fun getContainingFile(): PsiFile? = _containingFile
private val jetTokenToPsiModifier = listOf(
KtTokens.PUBLIC_KEYWORD to PsiModifier.PUBLIC,
KtTokens.INTERNAL_KEYWORD to PsiModifier.PUBLIC,
KtTokens.PROTECTED_KEYWORD to PsiModifier.PROTECTED,
KtTokens.FINAL_KEYWORD to PsiModifier.FINAL,
)
override fun getOwnInnerClasses(): List<PsiClass> {
val result = ArrayList<PsiClass>()
classOrObject.declarations.filterIsInstance<KtClassOrObject>()
// workaround for ClassInnerStuffCache not supporting classes with null names, see KT-13927
// inner classes with null names can't be searched for and can't be used from java anyway
// we can't prohibit creating light classes with null names either since they can contain members
.filter { it.name != null }
.mapNotNullTo(result) {
create(it)
}
if (classOrObject.hasInterfaceDefaultImpls && jvmDefaultMode != JvmDefaultMode.ALL_INCOMPATIBLE) {
result.add(createClassForInterfaceDefaultImpls())
}
return result
}
protected abstract fun createClassForInterfaceDefaultImpls(): PsiClass
override fun getContainingClass(): PsiClass? {
if (classOrObject.parent === classOrObject.containingFile) return null
val containingClassOrObject = (classOrObject.parent as? KtClassBody)?.parent as? KtClassOrObject
if (containingClassOrObject != null) {
return create(containingClassOrObject)
}
// TODO: should return null
return super.getContainingClass()
}
companion object {
private fun checkSuperTypeByFQName(classDescriptor: ClassDescriptor, qualifiedName: String, deep: Boolean): Boolean {
if (CommonClassNames.JAVA_LANG_OBJECT == qualifiedName) return true
if (qualifiedName == DescriptorUtils.getFqName(classDescriptor).asString()) return true
val fqName = FqNameUnsafe(qualifiedName)
val mappedQName =
if (fqName.isSafe)
JavaToKotlinClassMap.mapJavaToKotlin(fqName.toSafe())?.asSingleFqName()?.asString()
else null
if (qualifiedName == mappedQName) return true
for (superType in classDescriptor.typeConstructor.supertypes) {
val superDescriptor = superType.constructor.declarationDescriptor
if (superDescriptor is ClassDescriptor) {
val superQName = DescriptorUtils.getFqName(superDescriptor).asString()
if (superQName == qualifiedName || superQName == mappedQName) return true
if (deep) {
if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) {
return true
}
}
}
}
return false
}
private val jetTokenToPsiModifier = listOf(
KtTokens.PUBLIC_KEYWORD to PsiModifier.PUBLIC,
KtTokens.INTERNAL_KEYWORD to PsiModifier.PUBLIC,
KtTokens.PROTECTED_KEYWORD to PsiModifier.PROTECTED,
KtTokens.FINAL_KEYWORD to PsiModifier.FINAL,
)
fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? =
CachedValuesManager.getCachedValue(classOrObject) {
CachedValueProvider.Result.create(
createNoCache(classOrObject),
KotlinModificationTrackerService.getInstance(classOrObject.project).outOfBlockModificationTracker,
)
}
private fun createNoCache(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
val containingFile = classOrObject.containingFile
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return null
}
if (classOrObject.shouldNotBeVisibleAsLightClass()) {
return null
}
return LightClassGenerationSupport.getInstance(classOrObject.project).run {
createUltraLightClass(classOrObject)
}
}
}
}