[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 } ).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) 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 // 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 // we can't prohibit creating light classes with null names either since they can contain members
.filter { it.name != null } .filter { it.name != null }
.mapNotNull { KtLightClassForSourceDeclaration.create(it) } .mapNotNull { KtLightClassImpl.create(it) }
} }
override fun getInitializers(): Array<PsiClassInitializer> = PsiClassInitializer.EMPTY_ARRAY 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.*
import com.intellij.psi.impl.source.PsiImmediateClassType import com.intellij.psi.impl.source.PsiImmediateClassType
import com.intellij.psi.impl.source.tree.TreeUtil import com.intellij.psi.impl.source.tree.TreeUtil
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.stubs.StubElement 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.psi.util.PsiUtilCore
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE 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.KtLightIdentifier
import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
import org.jetbrains.kotlin.asJava.hasInterfaceDefaultImpls
import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind 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 org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import javax.swing.Icon 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( abstract class KtLightClassForSourceDeclaration(
protected val classOrObject: KtClassOrObject, protected val classOrObject: KtClassOrObject,
protected val jvmDefaultMode: JvmDefaultMode, protected val jvmDefaultMode: JvmDefaultMode,
@@ -75,7 +57,6 @@ abstract class KtLightClassForSourceDeclaration(
private val _extendsList by lazyPub { createExtendsList() } private val _extendsList by lazyPub { createExtendsList() }
private val _implementsList by lazyPub { createImplementsList() } private val _implementsList by lazyPub { createImplementsList() }
private val _deprecated by lazyPub { classOrObject.isDeprecated() }
protected abstract fun createExtendsList(): PsiReferenceList? protected abstract fun createExtendsList(): PsiReferenceList?
protected abstract fun createImplementsList(): PsiReferenceList? protected abstract fun createImplementsList(): PsiReferenceList?
@@ -86,35 +67,7 @@ abstract class KtLightClassForSourceDeclaration(
abstract override fun getParent(): PsiElement? abstract override fun getParent(): PsiElement?
abstract override fun getQualifiedName(): String? abstract override fun getQualifiedName(): String?
private val _containingFile: PsiFile by lazyPub { abstract override fun getContainingFile(): PsiFile?
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
override fun getNavigationElement(): PsiElement = classOrObject override fun getNavigationElement(): PsiElement = classOrObject
@@ -140,17 +93,6 @@ abstract class KtLightClassForSourceDeclaration(
override fun hashCode(): Int = classOrObject.hashCode() * 31 + jvmDefaultMode.hashCode() 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() } private val _typeParameterList: PsiTypeParameterList by lazyPub { buildTypeParameterList() }
@@ -162,15 +104,11 @@ abstract class KtLightClassForSourceDeclaration(
override fun getName(): String? = classOrObject.nameAsName?.asString() override fun getName(): String? = classOrObject.nameAsName?.asString()
private val _modifierList: PsiModifierList by lazyPub { KtLightClassModifierList(this) { computeModifiers() } } abstract override fun getModifierList(): PsiModifierList?
override fun getModifierList(): PsiModifierList? = _modifierList
protected abstract fun computeModifiers(): Set<String> protected abstract fun computeModifiers(): Set<String>
override fun hasModifierProperty(@NonNls name: String): Boolean = modifierList?.hasModifierProperty(name) ?: false override fun hasModifierProperty(@NonNls name: String): Boolean = modifierList?.hasModifierProperty(name) ?: false
abstract override fun isDeprecated(): Boolean
override fun isDeprecated(): Boolean = _deprecated
override fun isInterface(): Boolean { override fun isInterface(): Boolean {
if (classOrObject !is KtClass) return false if (classOrObject !is KtClass) return false
@@ -195,25 +133,7 @@ abstract class KtLightClassForSourceDeclaration(
override fun toString() = "${this::class.java.simpleName}:${classOrObject.getDebugText()}" override fun toString() = "${this::class.java.simpleName}:${classOrObject.getDebugText()}"
override fun getOwnInnerClasses(): List<PsiClass> { abstract 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 getUseScope(): SearchScope = kotlinOrigin.useScope override fun getUseScope(): SearchScope = kotlinOrigin.useScope
@@ -225,32 +145,6 @@ abstract class KtLightClassForSourceDeclaration(
override fun getExtendsList(): PsiReferenceList? = _extendsList override fun getExtendsList(): PsiReferenceList? = _extendsList
override fun getImplementsList(): PsiReferenceList? = _implementsList 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 getSupers(): Array<PsiClass>
abstract override fun getSuperTypes(): Array<PsiClassType> abstract override fun getSuperTypes(): Array<PsiClassType>
@@ -5,20 +5,35 @@
package org.jetbrains.kotlin.asJava.classes package org.jetbrains.kotlin.asJava.classes
import com.intellij.psi.CommonClassNames import com.intellij.psi.*
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiModifier
import com.intellij.psi.impl.InheritanceImplUtil 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.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.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtClassBody
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.resolve.DescriptorUtils 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 // light class for top level or (inner/nested of top level) source declarations
abstract class KtLightClassImpl @JvmOverloads constructor( abstract class KtLightClassImpl @JvmOverloads constructor(
classOrObject: KtClassOrObject, classOrObject: KtClassOrObject,
@@ -28,6 +43,15 @@ abstract class KtLightClassImpl @JvmOverloads constructor(
fun getDescriptor() = fun getDescriptor() =
LightClassGenerationSupport.getInstance(project).resolveToDescriptor(classOrObject) as? ClassDescriptor 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> { override fun computeModifiers(): Set<String> {
val psiModifiers = hashSetOf<String>() val psiModifiers = hashSetOf<String>()
@@ -110,41 +134,129 @@ abstract class KtLightClassImpl @JvmOverloads constructor(
containingClass containingClass
abstract override fun copy(): PsiElement abstract override fun copy(): PsiElement
}
private fun checkSuperTypeByFQName(classDescriptor: ClassDescriptor, qualifiedName: String, deep: Boolean): Boolean { private val _containingFile: PsiFile by lazyPub {
if (CommonClassNames.JAVA_LANG_OBJECT == qualifiedName) return true 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) // We have to explicitly process package declarations if current file belongs to default package
val mappedQName = // so that Java resolve can find classes located in that package
if (fqName.isSafe) val packageName = packageName
JavaToKotlinClassMap.mapJavaToKotlin(fqName.toSafe())?.asSingleFqName()?.asString() if (packageName.isNotEmpty()) return true
else null
if (qualifiedName == mappedQName) return true
for (superType in classDescriptor.typeConstructor.supertypes) { val aPackage = JavaPsiFacade.getInstance(myManager.project).findPackage(packageName)
val superDescriptor = superType.constructor.declarationDescriptor if (aPackage != null && !aPackage.processDeclarations(processor, state, null, place)) return false
if (superDescriptor is ClassDescriptor) { return true
val superQName = DescriptorUtils.getFqName(superDescriptor).asString()
if (superQName == qualifiedName || superQName == mappedQName) return true
if (deep) {
if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) {
return true
}
} }
} }
} }
return false override fun getContainingFile(): PsiFile? = _containingFile
}
private val jetTokenToPsiModifier = listOf( override fun getOwnInnerClasses(): List<PsiClass> {
KtTokens.PUBLIC_KEYWORD to PsiModifier.PUBLIC, val result = ArrayList<PsiClass>()
KtTokens.INTERNAL_KEYWORD to PsiModifier.PUBLIC, classOrObject.declarations.filterIsInstance<KtClassOrObject>()
KtTokens.PROTECTED_KEYWORD to PsiModifier.PROTECTED, // workaround for ClassInnerStuffCache not supporting classes with null names, see KT-13927
KtTokens.FINAL_KEYWORD to PsiModifier.FINAL, // 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)
}
}
}
}