diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliKotlinAsJavaSupport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliKotlinAsJavaSupport.kt index 1b44d6ecd42..32295123a61 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliKotlinAsJavaSupport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliKotlinAsJavaSupport.kt @@ -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) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForScript.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForScript.kt index 3156753cf09..e15536e0e1f 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForScript.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForScript.kt @@ -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.EMPTY_ARRAY diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt index 8e8853bb58f..f01544e90df 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt @@ -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) : - KtLightModifierList(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 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 { - val result = ArrayList() - classOrObject.declarations.filterIsInstance() - // 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 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 abstract override fun getSuperTypes(): Array diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt index a3948780bf3..9731eccfb7c 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt @@ -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) : + KtLightModifierList(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 { val psiModifiers = hashSetOf() @@ -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 { + val result = ArrayList() + classOrObject.declarations.filterIsInstance() + // 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) + } + } + } +}