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

^KT-53097
This commit is contained in:
Dmitry Gridin
2022-07-26 17:14:21 +02:00
committed by Space
parent 1b131332ab
commit 1708b4fe48
54 changed files with 11 additions and 11 deletions
@@ -0,0 +1,18 @@
/*
* 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
enum class ImpreciseResolveResult {
MATCH,
NO_MATCH,
UNSURE;
inline fun ifSure(body: (Boolean) -> Unit) = when (this) {
MATCH -> body(true)
NO_MATCH -> body(false)
UNSURE -> Unit
}
}
@@ -0,0 +1,70 @@
/*
* 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.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.classes.KtFakeLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
abstract class KotlinAsJavaSupport {
// Returns only immediately declared classes/objects, package classes are not included (they have no declarations)
abstract fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject>
/*
* Finds files whose package declaration is exactly {@code fqName}. For example, if a file declares
* package a.b.c
* it will not be returned for fqName "a.b"
*
* If the resulting collection is empty, it means that this package has not other declarations than sub-packages
*/
abstract fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile>
abstract fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject>
abstract fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean
abstract fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName>
abstract fun getLightClass(classOrObject: KtClassOrObject): KtLightClass?
abstract fun getLightClassForScript(script: KtScript): KtLightClass?
abstract fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String>
abstract fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile>
abstract fun getFakeLightClass(classOrObject: KtClassOrObject): KtFakeLightClass
abstract fun createFacadeForSyntheticFile(facadeClassFqName: FqName, file: KtFile): PsiClass
companion object {
@JvmStatic
fun getInstance(project: Project): KotlinAsJavaSupport {
return ServiceManager.getService(
project,
KotlinAsJavaSupport::class.java
)
}
}
}
@@ -0,0 +1,244 @@
/*
* 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")
}
}
@@ -0,0 +1,141 @@
/*
* 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.builder;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.impl.compiled.ClsEnumConstantImpl;
import com.intellij.psi.impl.compiled.ClsFieldImpl;
import com.intellij.psi.impl.java.stubs.*;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ClsWrapperStubPsiFactory extends StubPsiFactory {
public static final Key<LightElementOrigin> ORIGIN = Key.create("ORIGIN");
public static final ClsWrapperStubPsiFactory INSTANCE = new ClsWrapperStubPsiFactory();
private final StubPsiFactory delegate = new ClsStubPsiFactory();
private ClsWrapperStubPsiFactory() { }
@Override
public PsiClass createClass(@NotNull PsiClassStub stub) {
PsiElement origin = getOriginalElement(stub);
return new ClsClassImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
if (origin != null) return origin;
return super.getNavigationElement();
}
@Nullable
@Override
public PsiClass getSourceMirrorClass() {
return null;
}
};
}
@Nullable
public static PsiElement getOriginalElement(@NotNull StubElement stub) {
LightElementOrigin origin = ((StubBase) stub).getUserData(ORIGIN);
return origin != null ? origin.getOriginalElement() : null;
}
@Override
public PsiAnnotation createAnnotation(PsiAnnotationStub stub) {
return delegate.createAnnotation(stub);
}
@Override
public PsiClassInitializer createClassInitializer(PsiClassInitializerStub stub) {
return delegate.createClassInitializer(stub);
}
@Override
public PsiReferenceList createClassReferenceList(PsiClassReferenceListStub stub) {
return delegate.createClassReferenceList(stub);
}
@Override
public PsiField createField(PsiFieldStub stub) {
PsiElement origin = getOriginalElement(stub);
if (origin == null) return delegate.createField(stub);
if (stub.isEnumConstant()) {
return new ClsEnumConstantImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
else {
return new ClsFieldImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
}
@Override
public PsiImportList createImportList(PsiImportListStub stub) {
return delegate.createImportList(stub);
}
@Override
public PsiImportStatementBase createImportStatement(PsiImportStatementStub stub) {
return delegate.createImportStatement(stub);
}
@Override
public PsiMethod createMethod(PsiMethodStub stub) {
return delegate.createMethod(stub);
}
@Override
public PsiModifierList createModifierList(PsiModifierListStub stub) {
return delegate.createModifierList(stub);
}
@Override
public PsiParameter createParameter(PsiParameterStub stub) {
return delegate.createParameter(stub);
}
@Override
public PsiParameterList createParameterList(PsiParameterListStub stub) {
return delegate.createParameterList(stub);
}
@Override
public PsiTypeParameter createTypeParameter(PsiTypeParameterStub stub) {
return delegate.createTypeParameter(stub);
}
@Override
public PsiTypeParameterList createTypeParameterList(PsiTypeParameterListStub stub) {
return delegate.createTypeParameterList(stub);
}
@Override
public PsiAnnotationParameterList createAnnotationParameterList(PsiAnnotationParameterListStub stub) {
return delegate.createAnnotationParameterList(stub);
}
@Override
public PsiNameValuePair createNameValuePair(PsiNameValuePairStub stub) {
return delegate.createNameValuePair(stub);
}
}
@@ -0,0 +1,65 @@
/*
* 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.builder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
interface LightElementOrigin {
val originalElement: PsiElement?
val originKind: JvmDeclarationOriginKind?
object None : LightElementOrigin {
override val originalElement: PsiElement?
get() = null
override val originKind: JvmDeclarationOriginKind?
get() = null
override fun toString() = "NONE"
}
}
interface LightMemberOrigin : LightElementOrigin {
override val originalElement: KtDeclaration?
override val originKind: JvmDeclarationOriginKind
val parametersForJvmOverloads: List<KtParameter?>? get() = null
val auxiliaryOriginalElement: KtDeclaration? get() = null
fun isValid(): Boolean
fun isEquivalentTo(other: LightMemberOrigin?): Boolean
fun isEquivalentTo(other: PsiElement?): Boolean
fun copy(): LightMemberOrigin
}
data class LightMemberOriginForDeclaration(
override val originalElement: KtDeclaration,
override val originKind: JvmDeclarationOriginKind,
override val parametersForJvmOverloads: List<KtParameter?>? = null,
override val auxiliaryOriginalElement: KtDeclaration? = null
) : LightMemberOrigin {
override fun isValid(): Boolean = originalElement.isValid
override fun isEquivalentTo(other: LightMemberOrigin?): Boolean {
if (other !is LightMemberOriginForDeclaration) return false
return isEquivalentTo(other.originalElement)
}
override fun isEquivalentTo(other: PsiElement?): Boolean {
return originalElement.isEquivalentTo(other)
}
override fun copy(): LightMemberOrigin {
return LightMemberOriginForDeclaration(originalElement.copy() as KtDeclaration, originKind, parametersForJvmOverloads)
}
}
data class DefaultLightElementOrigin(override val originalElement: PsiElement?) : LightElementOrigin {
override val originKind: JvmDeclarationOriginKind? get() = null
}
@@ -0,0 +1,294 @@
/*
* 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.classes
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.PsiImplUtil
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.*
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.*
import com.intellij.util.ArrayUtil
import com.intellij.util.IncorrectOperationException
import gnu.trove.THashMap
import org.jetbrains.annotations.NotNull
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightParameter
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
class KotlinClassInnerStuffCache(
private val myClass: KtExtensibleLightClass,
private val dependencies: List<Any>,
private val lazyCreator: LazyCreator,
) {
abstract class LazyCreator {
abstract fun <T : Any> get(initializer: () -> T, dependencies: List<Any>): Lazy<T>
}
private fun <T : Any> cache(initializer: () -> T): Lazy<T> = lazyCreator.get(initializer, dependencies)
private val constructorsCache = cache { PsiImplUtil.getConstructors(myClass) }
val constructors: Array<PsiMethod>
get() = copy(constructorsCache.value)
private val fieldsCache = cache {
val own = myClass.ownFields
val ext = collectAugments(myClass, PsiField::class.java)
ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY)
}
val fields: Array<PsiField>
get() = copy(fieldsCache.value)
private val methodsCache = cache {
val own = myClass.ownMethods
var ext = collectAugments(myClass, PsiMethod::class.java)
if (myClass.isEnum) {
ext = ArrayList<PsiMethod>(ext.size + 2).also {
it += ext
it.addIfNotNull(getValuesMethod())
it.addIfNotNull(getValueOfMethod())
}
}
ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY)
}
val methods: Array<PsiMethod>
get() = copy(methodsCache.value)
private val innerClassesCache = cache {
val own = myClass.ownInnerClasses
val ext = collectAugments(myClass, PsiClass::class.java)
ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY)
}
val innerClasses: Array<out PsiClass>
get() = copy(innerClassesCache.value)
private val recordComponentsCache = cache {
val header = myClass.recordHeader
header?.recordComponents ?: PsiRecordComponent.EMPTY_ARRAY
}
val recordComponents: Array<PsiRecordComponent>
get() = copy(recordComponentsCache.value)
private val fieldByNameCache = cache {
val fields = this.fields.takeIf { it.isNotEmpty() } ?: return@cache emptyMap()
Collections.unmodifiableMap(THashMap<String, PsiField>(fields.size).apply {
for (field in fields) {
putIfAbsent(field.name, field)
}
})
}
fun findFieldByName(name: String, checkBases: Boolean): PsiField? {
return if (checkBases) {
PsiClassImplUtil.findFieldByName(myClass, name, true)
} else {
fieldByNameCache.value[name]
}
}
private val methodByNameCache = cache {
val methods = this.methods.takeIf { it.isNotEmpty() } ?: return@cache emptyMap()
Collections.unmodifiableMap(THashMap<String, Array<PsiMethod>>().apply {
for ((key, list) in methods.groupByTo(HashMap()) { it.name }) {
put(key, list.toTypedArray())
}
})
}
fun findMethodsByName(name: String, checkBases: Boolean): Array<PsiMethod> {
return if (checkBases) {
PsiClassImplUtil.findMethodsByName(myClass, name, true)
} else {
copy(methodByNameCache.value[name] ?: PsiMethod.EMPTY_ARRAY)
}
}
private val innerClassByNameCache = cache {
val classes = this.innerClasses.takeIf { it.isNotEmpty() } ?: return@cache emptyMap()
Collections.unmodifiableMap(THashMap<String, PsiClass>().apply {
for (psiClass in classes) {
val name = psiClass.name
if (name == null) {
Logger.getInstance(KotlinClassInnerStuffCache::class.java).error(psiClass)
} else if (psiClass !is ExternallyDefinedPsiElement || !containsKey(name)) {
put(name, psiClass)
}
}
})
}
fun findInnerClassByName(name: String, checkBases: Boolean): PsiClass? {
return if (checkBases) {
PsiClassImplUtil.findInnerByName(myClass, name, true)
} else {
innerClassByNameCache.value[name]
}
}
private val valuesMethodCache = cache { KotlinEnumSyntheticMethod(myClass, KotlinEnumSyntheticMethod.Kind.VALUES) }
private fun getValuesMethod(): PsiMethod? {
if (myClass.isEnum && !myClass.isAnonymous && !isClassNameSealed()) {
return valuesMethodCache.value
}
return null
}
private val valueOfMethodCache = cache { KotlinEnumSyntheticMethod(myClass, KotlinEnumSyntheticMethod.Kind.VALUE_OF) }
fun getValueOfMethod(): PsiMethod? {
if (myClass.isEnum && !myClass.isAnonymous) {
return valueOfMethodCache.value
}
return null
}
private fun isClassNameSealed(): Boolean {
return myClass.name == PsiKeyword.SEALED && PsiUtil.getLanguageLevel(myClass).toJavaVersion().feature >= 16
}
}
private class KotlinEnumSyntheticMethod(
private val enumClass: KtExtensibleLightClass,
private val kind: Kind
) : LightElement(enumClass.manager, enumClass.language), KtLightMethod, SyntheticElement {
enum class Kind(val methodName: String) {
VALUE_OF("valueOf"), VALUES("values")
}
private val returnType = run {
val enumType = JavaPsiFacade.getElementFactory(project).createType(enumClass)
.annotate { arrayOf(makeNotNullAnnotation(enumClass)) }
when (kind) {
Kind.VALUE_OF -> enumType
Kind.VALUES -> enumType.createArrayType().annotate { arrayOf(makeNotNullAnnotation(enumClass)) }
}
}
private val parameterList = LightParameterListBuilder(manager, language).apply {
if (kind == Kind.VALUE_OF) {
val stringType = PsiType.getJavaLangString(manager, GlobalSearchScope.allScope(project))
val nameParameter = object : LightParameter("name", stringType, this, language, false), KtLightParameter {
override val method: KtLightMethod get() = this@KotlinEnumSyntheticMethod
override val kotlinOrigin: KtParameter? get() = null
override fun getParent(): PsiElement = this@KotlinEnumSyntheticMethod
override fun getContainingFile(): PsiFile = this@KotlinEnumSyntheticMethod.containingFile
override fun getText(): String = name
override fun getTextRange(): TextRange = TextRange.EMPTY_RANGE
}
nameParameter.setModifierList(NotNullModifierList(manager))
addParameter(nameParameter)
}
}
private val modifierList = object : LightModifierList(manager, language, PsiModifier.PUBLIC, PsiModifier.STATIC) {
override fun getParent() = this@KotlinEnumSyntheticMethod
private val annotations = arrayOf(makeNotNullAnnotation(enumClass))
override fun findAnnotation(fqn: String): PsiAnnotation? = annotations.firstOrNull { it.hasQualifiedName(fqn) }
override fun getAnnotations(): Array<PsiAnnotation> = copy(annotations)
}
override fun getTextOffset(): Int = enumClass.textOffset
override fun toString(): String = enumClass.toString()
override fun equals(other: Any?): Boolean {
return this === other || (other is KotlinEnumSyntheticMethod && enumClass == other.enumClass && kind == other.kind)
}
override fun hashCode() = Objects.hash(enumClass, kind)
override fun isDeprecated(): Boolean = false
override fun getDocComment(): PsiDocComment? = null
override fun getReturnType(): PsiType = returnType
override fun getReturnTypeElement(): PsiTypeElement? = null
override fun getParameterList(): PsiParameterList = parameterList
override fun getThrowsList(): PsiReferenceList {
return LightReferenceListBuilder(manager, language, PsiReferenceList.Role.THROWS_LIST).apply {
if (kind == Kind.VALUE_OF) {
addReference("java.lang.IllegalArgumentException")
}
}
}
override fun getParent(): PsiElement = enumClass
override fun getContainingClass(): KtExtensibleLightClass = enumClass
override fun getContainingFile(): PsiFile = enumClass.containingFile
override fun getBody(): PsiCodeBlock? = null
override fun isConstructor(): Boolean = false
override fun isVarArgs(): Boolean = false
override fun getSignature(substitutor: PsiSubstitutor): MethodSignature = MethodSignatureBackedByPsiMethod.create(this, substitutor)
override fun getNameIdentifier(): PsiIdentifier = LightIdentifier(manager, name)
override fun getName() = kind.methodName
override fun findSuperMethods(): Array<PsiMethod> = PsiSuperMethodImplUtil.findSuperMethods(this)
override fun findSuperMethods(checkAccess: Boolean): Array<PsiMethod> = PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess)
override fun findSuperMethods(parentClass: PsiClass): Array<PsiMethod> = PsiSuperMethodImplUtil.findSuperMethods(this, parentClass)
override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean): List<MethodSignatureBackedByPsiMethod> {
return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess)
}
@Suppress("OVERRIDE_DEPRECATION")
override fun findDeepestSuperMethod(): PsiMethod? = PsiSuperMethodImplUtil.findDeepestSuperMethod(this)
override fun findDeepestSuperMethods(): Array<PsiMethod> = PsiMethod.EMPTY_ARRAY
override fun getModifierList(): PsiModifierList = modifierList
override fun hasModifierProperty(name: String) = name == PsiModifier.PUBLIC || name == PsiModifier.STATIC
override fun setName(name: String): PsiElement = throw IncorrectOperationException()
override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this)
override fun getDefaultValue(): PsiAnnotationMemberValue? = null
override fun hasTypeParameters(): Boolean = false
override fun getTypeParameterList(): PsiTypeParameterList? = null
override fun getTypeParameters(): Array<PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
override val isMangled: Boolean get() = false
override val lightMemberOrigin: LightMemberOrigin? get() = null
override val kotlinOrigin: KtDeclaration? get() = null
override fun getText(): String = ""
override fun getTextRange(): TextRange = TextRange.EMPTY_RANGE
private companion object {
private fun makeNotNullAnnotation(context: PsiClass): PsiAnnotation {
return PsiElementFactory.getInstance(context.project).createAnnotationFromText("@" + NotNull::class.java.name, context)
}
}
}
private val PsiClass.isAnonymous: Boolean
get() = name == null || this is PsiAnonymousClass
private class NotNullModifierList(manager: PsiManager) : LightModifierList(manager) {
private val annotation = PsiElementFactory.getInstance(project).createAnnotationFromText("@" + NotNull::class.java.name, context)
override fun getAnnotations() = arrayOf(annotation)
}
private fun <T> copy(value: Array<T>): Array<T> {
return if (value.isEmpty()) value else value.clone()
}
@@ -0,0 +1,44 @@
/*
* 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.
*/
@file:Suppress("unused")
package org.jetbrains.kotlin.asJava.classes
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
interface KotlinLightClassFactory {
fun createClass(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration?
fun createFacade(project: Project, facadeClassFqName: FqName, searchScope: GlobalSearchScope): KtLightClassForFacade?
fun createFacadeForSyntheticFile(facadeClassFqName: FqName, file: KtFile): KtLightClassForFacade
fun createScript(script: KtScript): KtLightClassForScript?
companion object {
private val instance: KotlinLightClassFactory
get() = ServiceManager.getService(KotlinLightClassFactory::class.java)
fun createClass(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
return instance.createClass(classOrObject)
}
fun createFacade(project: Project, facadeClassFqName: FqName, searchScope: GlobalSearchScope): KtLightClassForFacade? {
return instance.createFacade(project, facadeClassFqName, searchScope)
}
fun createFacadeForSyntheticFile(facadeClassFqName: FqName, file: KtFile): KtLightClassForFacade {
return instance.createFacadeForSyntheticFile(facadeClassFqName, file)
}
fun createScript(script: KtScript): KtLightClassForScript? {
return instance.createScript(script)
}
}
}
@@ -0,0 +1,84 @@
/*
* 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.classes;
import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightReferenceListBuilder;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Copy-pasted mostly from com.intellij.psi.impl.light.LightReferenceListBuilder
*/
public class KotlinLightReferenceListBuilder extends LightReferenceListBuilder implements PsiReferenceList {
private final List<PsiJavaCodeReferenceElement> myRefs = new ArrayList<>();
private volatile PsiJavaCodeReferenceElement[] myCachedRefs;
private volatile PsiClassType[] myCachedTypes;
private final Role myRole;
private final PsiElementFactory myFactory;
public KotlinLightReferenceListBuilder(PsiManager manager, Role role) {
this(manager, JavaLanguage.INSTANCE, role);
}
public KotlinLightReferenceListBuilder(PsiManager manager, Language language, Role role) {
super(manager, language, role);
myRole = role;
myFactory = JavaPsiFacade.getElementFactory(getProject());
}
@Override
public void addReference(PsiClass aClass) {
addReference(aClass.getQualifiedName());
}
@Override
public void addReference(String qualifiedName) {
final PsiJavaCodeReferenceElement ref = myFactory.createReferenceElementByFQClassName(qualifiedName, getResolveScope());
myRefs.add(ref);
}
@Override
public void addReference(PsiClassType type) {
final PsiJavaCodeReferenceElement ref = myFactory.createReferenceElementByType(type);
myRefs.add(ref);
}
@NotNull
@Override
public PsiJavaCodeReferenceElement[] getReferenceElements() {
PsiJavaCodeReferenceElement[] refs = myCachedRefs;
if (refs == null) {
myCachedRefs = refs = myRefs.toArray(PsiJavaCodeReferenceElement.EMPTY_ARRAY);
}
return refs;
}
@NotNull
@Override
public PsiClassType[] getReferencedTypes() {
PsiClassType[] types = myCachedTypes;
if (types == null) {
int size = myRefs.size();
types = size == 0 ? PsiClassType.EMPTY_ARRAY : new PsiClassType[size];
for (int i = 0; i < size; i++) {
types[i] = myFactory.createType(myRefs.get(i));
}
myCachedTypes = types;
}
return types;
}
@Override
public Role getRole() {
return myRole;
}
}
@@ -0,0 +1,57 @@
/*
* 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.classes
import com.intellij.lang.Language
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiJavaCodeReferenceElement
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiReferenceList
import org.jetbrains.kotlin.psi.KtSuperTypeList
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
class KotlinSuperTypeListBuilder(kotlinOrigin: KtSuperTypeList?, manager: PsiManager, language: Language, role: PsiReferenceList.Role) :
KotlinLightReferenceListBuilder(
manager,
language,
role
) {
private val myKotlinOrigin: KtSuperTypeList? = kotlinOrigin
inner class KotlinSuperTypeReference(private val element: PsiJavaCodeReferenceElement) : PsiJavaCodeReferenceElement by element {
override fun getParent() = this@KotlinSuperTypeListBuilder
val kotlinOrigin by lazyPub {
element.qualifiedName?.let { this@KotlinSuperTypeListBuilder.myKotlinOrigin?.findEntry(it) }
}
override fun delete() {
val superTypeList = this@KotlinSuperTypeListBuilder.myKotlinOrigin ?: return
val entry = kotlinOrigin ?: return
superTypeList.removeEntry(entry)
}
}
private val referenceElementsCache by lazyPub {
super.getReferenceElements().map { KotlinSuperTypeReference(it) }.toTypedArray()
}
override fun getReferenceElements() = referenceElementsCache
override fun add(element: PsiElement): PsiElement {
if (element !is KotlinSuperTypeReference) throw UnsupportedOperationException("Unexpected element: ${element.getElementTextWithContext()}")
val superTypeList = myKotlinOrigin ?: return element
val entry = element.kotlinOrigin ?: return element
this.addSuperTypeEntry(superTypeList, entry, element)
return element
}
}
@@ -0,0 +1,72 @@
/*
* 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.classes
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.light.AbstractLightClass
import com.intellij.psi.search.SearchScope
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.psi.KtClassOrObject
import javax.swing.Icon
// Used as a placeholder when actual light class does not exist (expect-classes, for example)
// The main purpose is to allow search of inheritors within hierarchies containing such classes
abstract class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) :
AbstractLightClass(kotlinOrigin.manager, KotlinLanguage.INSTANCE),
KtLightClass {
private val _delegate: PsiClass by lazy { DummyJavaPsiFactory.createDummyClass(kotlinOrigin.project) }
override val originKind get() = LightClassOriginKind.SOURCE
override fun getName(): String? = kotlinOrigin.name
override fun getDelegate(): PsiClass = _delegate
abstract override fun copy(): KtFakeLightClass
override fun getQualifiedName(): String? = kotlinOrigin.fqName?.asString()
abstract override fun getContainingClass(): KtFakeLightClass?
override fun getNavigationElement(): PsiElement = kotlinOrigin.navigationElement
override fun getIcon(flags: Int): Icon? = kotlinOrigin.getIcon(flags)
override fun getContainingFile(): PsiFile = kotlinOrigin.containingFile
override fun getUseScope(): SearchScope = kotlinOrigin.useScope
abstract override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean
override fun isEquivalentTo(another: PsiElement?): Boolean = PsiClassImplUtil.isClassEquivalentTo(this, another)
}
object DummyJavaPsiFactory {
fun createDummyVoidMethod(project: Project): PsiMethod {
// Can't use PsiElementFactory.createMethod() because of formatting in PsiElementFactoryImpl.
val name = "dummy"
val returnType = PsiType.VOID
val canonicalText = GenericsUtil.getVariableTypeByExpressionType(returnType).getCanonicalText(true)
val file = createDummyJavaFile(project, "class _Dummy_ { public $canonicalText $name() {\n} }")
val klass = file.classes.singleOrNull()
?: throw IncorrectOperationException("Class was not created. Method name: $name; return type: $canonicalText")
return klass.methods.singleOrNull()
?: throw IncorrectOperationException("Method was not created. Method name: $name; return type: $canonicalText")
}
fun createDummyClass(project: Project): PsiClass = PsiElementFactory.getInstance(project).createClass("dummy")
private fun createDummyJavaFile(project: Project, text: String): PsiJavaFile {
return PsiFileFactory.getInstance(project).createFileFromText(
DUMMY_FILE_NAME,
JavaFileType.INSTANCE,
text
) as PsiJavaFile
}
private val DUMMY_FILE_NAME = "_Dummy_." + JavaFileType.INSTANCE.defaultExtension
}
@@ -0,0 +1,16 @@
/*
* 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.classes
import com.intellij.psi.PsiClass
import com.intellij.psi.impl.source.PsiExtensibleClass
import org.jetbrains.kotlin.asJava.KtLightClassMarker
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject
interface KtLightClass : PsiClass, KtLightDeclaration<KtClassOrObject, PsiClass>, KtLightClassMarker
interface KtExtensibleLightClass : KtLightClass, PsiExtensibleClass
@@ -0,0 +1,70 @@
/*
* 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.classes
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiField
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.light.AbstractLightClass
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.idea.KotlinLanguage
abstract class KtLightClassBase protected constructor(
manager: PsiManager
) : AbstractLightClass(manager, KotlinLanguage.INSTANCE), KtExtensibleLightClass {
protected open val myInnersCache = KotlinClassInnerStuffCache(
myClass = this,
dependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker),
lazyCreator = LightClassesLazyCreator(project)
)
override fun getDelegate() =
throw UnsupportedOperationException("Cls delegate shouldn't be loaded for ultra-light classes!")
override fun getFields() = myInnersCache.fields
override fun getMethods() = myInnersCache.methods
override fun getConstructors() = myInnersCache.constructors
override fun getInnerClasses() = myInnersCache.innerClasses
override fun getAllFields() = PsiClassImplUtil.getAllFields(this)
override fun getAllMethods() = PsiClassImplUtil.getAllMethods(this)
override fun getAllInnerClasses() = PsiClassImplUtil.getAllInnerClasses(this)
override fun findFieldByName(name: String, checkBases: Boolean) = myInnersCache.findFieldByName(name, checkBases)
override fun findMethodsByName(name: String, checkBases: Boolean) = myInnersCache.findMethodsByName(name, checkBases)
override fun findInnerClassByName(name: String, checkBases: Boolean) = myInnersCache.findInnerClassByName(name, checkBases)
abstract override fun getOwnFields(): List<PsiField>
abstract override fun getOwnMethods(): List<PsiMethod>
override fun getText(): String {
val origin = kotlinOrigin
return if (origin == null) "" else origin.text
}
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun getPresentation() = ItemPresentationProviders.getItemPresentation(this)
abstract override fun equals(other: Any?): Boolean
abstract override fun hashCode(): Int
override fun getContext() = parent
override fun isEquivalentTo(another: PsiElement?): Boolean {
return PsiClassImplUtil.isClassEquivalentTo(this, another)
}
}
@@ -0,0 +1,16 @@
/*
* 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.classes
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
interface KtLightClassForFacade : KtLightClass {
val facadeClassFqName: FqName
val files: Collection<KtFile>
override fun getName() = facadeClassFqName.shortName().asString()
}
@@ -0,0 +1,209 @@
/*
* 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.classes
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.LightEmptyImplementsList
import com.intellij.psi.impl.light.LightModifierList
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.JvmNames.JVM_NAME_SHORT
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.siblings
import javax.swing.Icon
abstract class KtLightClassForFacadeBase constructor(
manager: PsiManager,
override val facadeClassFqName: FqName,
override val files: Collection<KtFile>
) : KtLightClassBase(manager), KtLightClassForFacade {
private val firstFileInFacade by lazyPub { files.iterator().next() }
private val modifierList: PsiModifierList =
LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL)
private val implementsList: LightEmptyImplementsList =
LightEmptyImplementsList(manager)
private val packageClsFile by lazyPub {
FakeFileForLightClass(
firstFileInFacade,
lightClass = { this },
packageFqName = facadeClassFqName.parent()
)
}
override fun getParent(): PsiElement = containingFile
override val kotlinOrigin: KtClassOrObject? get() = null
val fqName: FqName
get() = facadeClassFqName
override fun getModifierList() = modifierList
override fun hasModifierProperty(@NonNls name: String) = modifierList.hasModifierProperty(name)
override fun getExtendsList(): PsiReferenceList? = null
override fun isDeprecated() = false
override fun isInterface() = false
override fun isAnnotationType() = false
override fun isEnum() = false
override fun getContainingClass(): PsiClass? = null
override fun getContainingFile() = packageClsFile
override fun hasTypeParameters() = false
override fun getTypeParameters(): Array<out PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
override fun getTypeParameterList(): PsiTypeParameterList? = null
override fun getDocComment(): Nothing? = null
override fun getImplementsList() = implementsList
override fun getImplementsListTypes(): Array<out PsiClassType> = PsiClassType.EMPTY_ARRAY
override fun getInterfaces(): Array<out PsiClass> = PsiClass.EMPTY_ARRAY
override fun getInnerClasses(): Array<out PsiClass> = PsiClass.EMPTY_ARRAY
override fun getOwnInnerClasses(): List<PsiClass> = listOf()
override fun getAllInnerClasses(): Array<out PsiClass> = PsiClass.EMPTY_ARRAY
override fun getInitializers(): Array<out PsiClassInitializer> = PsiClassInitializer.EMPTY_ARRAY
override fun findInnerClassByName(@NonNls name: String, checkBases: Boolean): PsiClass? = null
override fun isInheritorDeep(baseClass: PsiClass?, classToByPass: PsiClass?): Boolean = false
override fun getLBrace(): PsiElement? = null
override fun getRBrace(): PsiElement? = null
override fun getName() = super<KtLightClassForFacade>.getName()
override fun setName(name: String): PsiElement? {
for (file in files) {
val jvmNameEntry = JvmFileClassUtil.findAnnotationEntryOnFileNoResolve(file, JVM_NAME_SHORT)
if (PackagePartClassUtils.getFilePartShortName(file.name) == name) {
jvmNameEntry?.delete()
continue
}
if (jvmNameEntry == null) {
val newFileName = PackagePartClassUtils.getFileNameByFacadeName(name)
val facadeDir = file.parent
if (newFileName != null && facadeDir != null && facadeDir.findFile(newFileName) == null) {
file.name = newFileName
continue
}
val psiFactory = KtPsiFactory(this)
val annotationText = "${JVM_NAME_SHORT}(\"$name\")"
val newFileAnnotationList = psiFactory.createFileAnnotationListWithAnnotation(annotationText)
val annotationList = file.fileAnnotationList
if (annotationList != null) {
annotationList.add(newFileAnnotationList.annotationEntries.first())
} else {
val anchor = file.firstChild.siblings().firstOrNull { it !is PsiWhiteSpace && it !is PsiComment }
file.addBefore(newFileAnnotationList, anchor)
}
continue
}
val jvmNameExpression = jvmNameEntry.valueArguments.firstOrNull()?.getArgumentExpression() as? KtStringTemplateExpression
?: continue
ElementManipulators.handleContentChange(jvmNameExpression, name)
}
return this
}
override fun getQualifiedName() = facadeClassFqName.asString()
override fun getNameIdentifier(): PsiIdentifier? = null
override fun isValid() = files.all { it.isValid && it.hasTopLevelCallables() && facadeClassFqName == it.javaFileFacadeFqName }
abstract override fun copy(): KtLightClassForFacade
override fun getNavigationElement() = firstFileInFacade
override fun isEquivalentTo(another: PsiElement?): Boolean =
equals(another) ||
(another is KtLightClassForFacade && another.facadeClassFqName == facadeClassFqName)
override fun getElementIcon(flags: Int): Icon? = throw UnsupportedOperationException("This should be done by JetIconProvider")
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean {
return baseClass.qualifiedName == CommonClassNames.JAVA_LANG_OBJECT
}
override fun getSuperClass(): PsiClass? {
return JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope)
}
override fun getSupers(): Array<PsiClass> {
return superClass?.let { arrayOf(it) } ?: arrayOf()
}
override fun getSuperTypes(): Array<PsiClassType> {
return arrayOf(PsiType.getJavaLangObject(manager, resolveScope))
}
override fun hashCode() = facadeClassFqName.hashCode()
override fun equals(other: Any?): Boolean {
if (other == null || this::class.java != other::class.java) {
return false
}
val lightClass = other as KtLightClassForFacadeBase
if (this === other) return true
if (facadeClassFqName != lightClass.facadeClassFqName) return false
if (files != lightClass.files) return false
return true
}
override fun toString() = "${KtLightClassForFacadeBase::class.java.simpleName}:$facadeClassFqName"
override val originKind: LightClassOriginKind
get() = LightClassOriginKind.SOURCE
override fun getText() = firstFileInFacade.text ?: ""
override fun getTextRange(): TextRange = firstFileInFacade.textRange ?: TextRange.EMPTY_RANGE
override fun getTextOffset() = firstFileInFacade.textOffset
override fun getStartOffsetInParent() = firstFileInFacade.startOffsetInParent
override fun isWritable() = files.all { it.isWritable }
override fun getVisibleSignatures(): MutableCollection<HierarchicalMethodSignature> = PsiSuperMethodImplUtil.getVisibleSignatures(this)
}
@@ -0,0 +1,146 @@
/*
* 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.classes
import com.intellij.psi.*
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.LightEmptyImplementsList
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtScript
import javax.swing.Icon
abstract class KtLightClassForScript(val script: KtScript) : KtLightClassBase(script.manager) {
private val modifierList: PsiModifierList = LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC)
private val scriptImplementsList: LightEmptyImplementsList = LightEmptyImplementsList(manager)
private val scriptExtendsList: PsiReferenceList by lazyPub {
KotlinLightReferenceListBuilder(manager, PsiReferenceList.Role.EXTENDS_LIST).also {
it.addReference("kotlin.script.templates.standard.ScriptTemplateWithArgs")
}
}
private val _containingFile by lazyPub {
FakeFileForLightClass(
script.containingKtFile,
lightClass = { this },
packageFqName = fqName.parent(),
)
}
override val kotlinOrigin: KtClassOrObject? get() = null
val fqName: FqName get() = script.fqName
override fun getModifierList() = modifierList
override fun hasModifierProperty(@NonNls name: String) = modifierList.hasModifierProperty(name)
override fun isDeprecated() = false
override fun isInterface() = false
override fun isAnnotationType() = false
override fun isEnum() = false
override fun getContainingClass() = null
override fun getContainingFile() = _containingFile
override fun hasTypeParameters() = false
override fun getTypeParameters(): Array<PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
override fun getTypeParameterList() = null
override fun getDocComment() = null
override fun getImplementsList(): PsiReferenceList = scriptImplementsList
override fun getExtendsList(): PsiReferenceList = scriptExtendsList
override fun getImplementsListTypes(): Array<PsiClassType> = PsiClassType.EMPTY_ARRAY
override fun getInterfaces(): Array<PsiClass> = PsiClass.EMPTY_ARRAY
override fun getInitializers(): Array<PsiClassInitializer> = PsiClassInitializer.EMPTY_ARRAY
override fun getName() = script.fqName.shortName().asString()
override fun getQualifiedName() = script.fqName.asString()
override fun isValid() = script.isValid
abstract override fun copy(): PsiElement
override fun getNavigationElement() = script
override fun isEquivalentTo(another: PsiElement?): Boolean =
equals(another) ||
(another is KtLightClassForScript && fqName == another.fqName)
override fun getElementIcon(flags: Int): Icon? =
throw UnsupportedOperationException("This should be done by JetIconProvider")
override val originKind: LightClassOriginKind get() = LightClassOriginKind.SOURCE
override fun getLBrace(): PsiElement? = null
override fun getRBrace(): PsiElement? = null
override fun getVisibleSignatures(): MutableCollection<HierarchicalMethodSignature> = PsiSuperMethodImplUtil.getVisibleSignatures(this)
override fun setName(name: String): PsiElement? = throw IncorrectOperationException()
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean {
return baseClass.qualifiedName == CommonClassNames.JAVA_LANG_OBJECT
}
override fun isInheritorDeep(baseClass: PsiClass?, classToByPass: PsiClass?): Boolean = false
override fun getSuperClass(): PsiClass? {
return JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope)
}
override fun getSupers(): Array<PsiClass> {
return superClass?.let { arrayOf(it) } ?: arrayOf()
}
override fun getSuperTypes(): Array<PsiClassType> {
return arrayOf(PsiType.getJavaLangObject(manager, resolveScope))
}
override fun getNameIdentifier(): PsiIdentifier? = null
override fun getParent(): PsiElement = containingFile
override fun getScope(): PsiElement = parent
override fun hashCode() = script.hashCode()
override fun equals(other: Any?): Boolean {
if (other == null || this::class.java != other::class.java) {
return false
}
val lightClass = other as? KtLightClassForScript ?: return false
if (this === other) return true
if (script != lightClass.script) return false
return true
}
override fun toString() = "${KtLightClassForScript::class.java.simpleName}:${script.fqName}"
}
@@ -0,0 +1,278 @@
/*
* 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.classes
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
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.search.SearchScope
import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.PsiUtilCore
import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.debugText.getDebugText
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import javax.swing.Icon
abstract class KtLightClassForSourceDeclaration(
protected val classOrObject: KtClassOrObject,
protected val jvmDefaultMode: JvmDefaultMode,
private val forceUsingOldLightClasses: Boolean = false
) : KtLightClassBase(classOrObject.manager),
StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
override val myInnersCache: KotlinClassInnerStuffCache = KotlinClassInnerStuffCache(
myClass = this,
dependencies = classOrObject.getExternalDependencies(),
lazyCreator = LightClassesLazyCreator(project)
)
private val lightIdentifier = KtLightIdentifier(this, classOrObject)
override fun getText() = kotlinOrigin.text ?: ""
override fun getTextRange(): TextRange? = kotlinOrigin.textRange ?: TextRange.EMPTY_RANGE
override fun getTextOffset() = kotlinOrigin.textOffset
override fun getStartOffsetInParent() = kotlinOrigin.startOffsetInParent
override fun isWritable() = kotlinOrigin.isWritable
private val _extendsList by lazyPub { createExtendsList() }
private val _implementsList by lazyPub { createImplementsList() }
protected abstract fun createExtendsList(): PsiReferenceList?
protected abstract fun createImplementsList(): PsiReferenceList?
override val kotlinOrigin: KtClassOrObject = classOrObject
abstract override fun copy(): PsiElement
abstract override fun getParent(): PsiElement?
abstract override fun getQualifiedName(): String?
abstract override fun getContainingFile(): PsiFile?
override fun getNavigationElement(): PsiElement = classOrObject
override fun isEquivalentTo(another: PsiElement?): Boolean =
kotlinOrigin.isEquivalentTo(another) ||
equals(another) ||
(qualifiedName != null && another is KtLightClassForSourceDeclaration && qualifiedName == another.qualifiedName)
override fun getElementIcon(flags: Int): Icon? =
throw UnsupportedOperationException("This should be done by JetIconProvider")
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class.java != other::class.java) return false
val aClass = other as KtLightClassForSourceDeclaration
if (jvmDefaultMode != aClass.jvmDefaultMode) return false
if (classOrObject != aClass.classOrObject) return false
return true
}
override fun hashCode(): Int = classOrObject.hashCode() * 31 + jvmDefaultMode.hashCode()
private val _typeParameterList: PsiTypeParameterList by lazyPub { buildTypeParameterList() }
protected abstract fun buildTypeParameterList(): PsiTypeParameterList
override fun getTypeParameterList(): PsiTypeParameterList? = _typeParameterList
override fun getTypeParameters(): Array<PsiTypeParameter> = _typeParameterList.typeParameters
override fun getName(): String? = classOrObject.nameAsName?.asString()
abstract override fun getModifierList(): PsiModifierList?
protected abstract fun computeModifiers(): Set<String>
override fun hasModifierProperty(@NonNls name: String): Boolean = modifierList?.hasModifierProperty(name) ?: false
abstract override fun isDeprecated(): Boolean
override fun isInterface(): Boolean {
if (classOrObject !is KtClass) return false
return classOrObject.isInterface() || classOrObject.isAnnotation()
}
override fun isAnnotationType(): Boolean = classOrObject is KtClass && classOrObject.isAnnotation()
override fun isEnum(): Boolean = classOrObject is KtClass && classOrObject.isEnum()
override fun hasTypeParameters(): Boolean = classOrObject is KtClass && classOrObject.typeParameters.isNotEmpty()
override fun isValid(): Boolean = classOrObject.isValid
abstract override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean
@Throws(IncorrectOperationException::class)
override fun setName(@NonNls name: String): PsiElement {
kotlinOrigin.setName(name)
return this
}
override fun toString() = "${this::class.java.simpleName}:${classOrObject.getDebugText()}"
abstract override fun getOwnInnerClasses(): List<PsiClass>
override fun getUseScope(): SearchScope = kotlinOrigin.useScope
override fun getElementType(): IStubElementType<out StubElement<*>, *>? = classOrObject.elementType
override fun getStub(): KotlinClassOrObjectStub<out KtClassOrObject>? = classOrObject.stub
override fun getNameIdentifier(): KtLightIdentifier? = lightIdentifier
override fun getExtendsList(): PsiReferenceList? = _extendsList
override fun getImplementsList(): PsiReferenceList? = _implementsList
abstract override fun getSupers(): Array<PsiClass>
abstract override fun getSuperTypes(): Array<PsiClassType>
private fun getSupertypeByPsi(): PsiImmediateClassType? {
return classOrObject.defaultJavaAncestorQualifiedName()?.let { ancestorFqName ->
JavaPsiFacade.getInstance(project).findClass(ancestorFqName, resolveScope)?.let {
PsiImmediateClassType(it, createSubstitutor(it))
}
}
}
private fun createSubstitutor(ancestor: PsiClass): PsiSubstitutor {
if (ancestor.qualifiedName != CommonClassNames.JAVA_LANG_ENUM) {
return PsiSubstitutor.EMPTY
}
val javaLangEnumsTypeParameter = ancestor.typeParameters.firstOrNull() ?: return PsiSubstitutor.EMPTY
return PsiSubstitutor.createSubstitutor(
mapOf(
javaLangEnumsTypeParameter to PsiImmediateClassType(this, PsiSubstitutor.EMPTY)
)
)
}
override val originKind: LightClassOriginKind
get() = LightClassOriginKind.SOURCE
abstract fun isFinal(isFinalByPsi: Boolean): Boolean
}
fun KtLightClassForSourceDeclaration.isPossiblyAffectedByAllOpen() =
!isAnnotationType && !isInterface && kotlinOrigin.annotationEntries.isNotEmpty()
fun getOutermostClassOrObject(classOrObject: KtClassOrObject): KtClassOrObject {
return KtPsiUtil.getOutermostClassOrObject(classOrObject)
?: throw IllegalStateException("Attempt to build a light class for a local class: " + classOrObject.text)
}
interface LightClassInheritanceHelper {
fun isInheritor(
lightClass: KtLightClass,
baseClass: PsiClass,
checkDeep: Boolean
): ImpreciseResolveResult
object NoHelp : LightClassInheritanceHelper {
override fun isInheritor(lightClass: KtLightClass, baseClass: PsiClass, checkDeep: Boolean) = UNSURE
}
companion object {
fun getService(project: Project): LightClassInheritanceHelper =
ServiceManager.getService(project, LightClassInheritanceHelper::class.java) ?: NoHelp
}
}
fun KtClassOrObject.defaultJavaAncestorQualifiedName(): String? {
if (this !is KtClass) return CommonClassNames.JAVA_LANG_OBJECT
return when {
isAnnotation() -> CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION
isEnum() -> CommonClassNames.JAVA_LANG_ENUM
isInterface() -> CommonClassNames.JAVA_LANG_OBJECT // see com.intellij.psi.impl.PsiClassImplUtil.getSuperClass
else -> CommonClassNames.JAVA_LANG_OBJECT
}
}
fun KtClassOrObject.shouldNotBeVisibleAsLightClass(): Boolean {
if (containingFile is KtCodeFragment) {
// Avoid building light classes for code fragments
return true
}
if (parentsWithSelf.filterIsInstance<KtClassOrObject>().any { it.hasExpectModifier() }) {
return true
}
if (isLocal) {
if (containingFile.virtualFile == null) return true
if (hasParseErrorsAround(this) || PsiUtilCore.hasErrorElementChild(this)) return true
if (classDeclaredInUnexpectedPosition(this)) return true
}
if (isEnumEntryWithoutBody(this)) {
return true
}
return false
}
/**
* If class is declared in some strange context (for example, in expression like `10 < class A`),
* we don't want to try to build a light class for it.
*
* The expression itself is incorrect and won't compile, but the parser is able the parse the class nonetheless.
*
* This does not concern objects, since object literals are expressions and can be used almost anywhere.
*/
private fun classDeclaredInUnexpectedPosition(classOrObject: KtClassOrObject): Boolean {
if (classOrObject is KtObjectDeclaration) return false
val classParent = classOrObject.parent
return classParent !is KtBlockExpression &&
classParent !is KtDeclarationContainer
}
private fun isEnumEntryWithoutBody(classOrObject: KtClassOrObject): Boolean {
if (classOrObject !is KtEnumEntry) {
return false
}
return classOrObject.getBody()?.declarations?.isEmpty() ?: true
}
private fun hasParseErrorsAround(psi: PsiElement): Boolean {
val node = psi.node ?: return false
TreeUtil.nextLeaf(node)?.let { nextLeaf ->
if (nextLeaf.elementType == TokenType.ERROR_ELEMENT || nextLeaf.treePrev?.elementType == TokenType.ERROR_ELEMENT) {
return true
}
}
TreeUtil.prevLeaf(node)?.let { prevLeaf ->
if (prevLeaf.elementType == TokenType.ERROR_ELEMENT || prevLeaf.treeNext?.elementType == TokenType.ERROR_ELEMENT) {
return true
}
}
return false
}
@@ -0,0 +1,18 @@
/*
* 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.classes
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.PsiType
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.psi.KtDeclaration
interface KtUltraLightElementWithNullabilityAnnotation<out T : KtDeclaration, out D : PsiModifierListOwner> : KtLightDeclaration<T, D>,
PsiModifierListOwner {
val qualifiedNameForNullabilityAnnotation: String?
val psiTypeForNullabilityAnnotation: PsiType?
}
@@ -0,0 +1,39 @@
/*
* 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.classes
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiTypeParameterListOwner
import com.intellij.psi.impl.light.LightReferenceListBuilder
import com.intellij.psi.impl.light.LightTypeParameterBuilder
import org.jetbrains.kotlin.asJava.elements.PsiElementWithOrigin
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.psi.KtTypeParameter
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
class KtUltraLightTypeParameter(
name: String,
private val myOwner: PsiTypeParameterListOwner,
private val myParent: PsiElement,
index: Int,
referenceListBuilder: (PsiElement) -> KotlinLightReferenceListBuilder
) :
LightTypeParameterBuilder(name, myOwner, index),
PsiElementWithOrigin<KtTypeParameter> {
private val superList: LightReferenceListBuilder by lazyPub { referenceListBuilder(this) }
override val origin: KtTypeParameter get() = (myOwner.unwrapped as KtTypeParameterListOwner).typeParameters[index]
override fun getExtendsList(): LightReferenceListBuilder = superList
override fun getParent(): PsiElement = myParent
override fun getContainingFile(): PsiFile = myOwner.containingFile
override fun getUseScope() = origin.useScope
}
@@ -0,0 +1,69 @@
/*
* 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.classes
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiCachedValueImpl
import com.intellij.psi.util.CachedValueProvider
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
class LightClassesLazyCreator(private val project: Project) : KotlinClassInnerStuffCache.LazyCreator() {
override fun <T : Any> get(initializer: () -> T, dependencies: List<Any>) = object : Lazy<T> {
private val lock = ReentrantLock()
private val holder = lazyPub {
PsiCachedValueImpl(PsiManager.getInstance(project)) {
val v = initializer()
CachedValueProvider.Result.create(v, dependencies)
}
}
private fun computeValue(): T = holder.value.value ?: error("holder has not null in initializer")
override val value: T
get() {
return if (holder.value.hasUpToDateValue()) {
computeValue()
} else {
// the idea behind this locking approach:
// Thread T1 starts to calculate value for A it acquires lock for A
//
// Assumption 1: Lets say A calculation requires another value e.g. B to be calculated
// Assumption 2: Thread T2 wants to calculate value for B
// to avoid dead-lock
// - we mark thread as doing calculation and acquire lock only once per thread
// as a trade-off to prevent dependent value could be calculated several time
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
// TODO: NOTE: acquire lock for a several seconds to avoid dead-lock via resolve is a WORKAROUND
if (!initIsRunning.get() && lock.tryLock(5, TimeUnit.SECONDS)) {
try {
initIsRunning.set(true)
try {
computeValue()
} finally {
initIsRunning.set(false)
}
} finally {
lock.unlock()
}
} else {
computeValue()
}
}
}
override fun isInitialized() = holder.isInitialized()
}
companion object {
@JvmStatic
private val initIsRunning: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
}
}
@@ -0,0 +1,70 @@
/*
* 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.classes
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiJavaCodeReferenceElement
import com.intellij.psi.PsiReferenceList
import com.intellij.psi.impl.light.LightElement
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.psi.*
fun KtSuperTypeList.findEntry(fqNameToFind: String): KtSuperTypeListEntry? {
val name = fqNameToFind.substringAfterLast(delimiter = '.', missingDelimiterValue = "")
if (name.isEmpty()) {
return entries.find { it.typeAsUserType?.textMatches(fqNameToFind) == true }
}
val qualifier = fqNameToFind.substringBeforeLast('.')
val entries = entries.mapNotNull { entry -> entry.typeAsUserType?.let { entry to it } }
.filter { (_, type) -> type.referencedName == name && (type.qualifier?.textMatches(qualifier) != false) }
.ifEmpty { return null }
return if (entries.size == 1) {
entries.first().first
} else {
val entry = entries.firstOrNull { it.second.qualifier != null } ?: entries.firstOrNull()
entry?.first
}
}
// NOTE: avoid using blocking lazy in light classes, it leads to deadlocks
fun <T> lazyPub(initializer: () -> T) = lazy(LazyThreadSafetyMode.PUBLICATION, initializer)
@Suppress("UnusedReceiverParameter")
fun LightElement.cannotModify(): Nothing {
throw IncorrectOperationException("Modification not implemented.")
}
fun PsiReferenceList.addSuperTypeEntry(
superTypeList: KtSuperTypeList,
entry: KtSuperTypeListEntry,
reference: PsiJavaCodeReferenceElement
) {
// Only classes may be mentioned in 'extends' list, thus create super call instead simple type reference
val entryToAdd =
if ((reference.parent as? PsiReferenceList)?.role == PsiReferenceList.Role.IMPLEMENTS_LIST && role == PsiReferenceList.Role.EXTENDS_LIST) {
KtPsiFactory(this).createSuperTypeCallEntry("${entry.text}()")
} else entry
// TODO: implement KtSuperListEntry qualification/shortening when inserting reference from another context
if (entry.parent != superTypeList) {
superTypeList.addEntry(entryToAdd)
} else {
// Preserve original entry order
entry.replace(entryToAdd)
}
}
fun KtClassOrObject.getExternalDependencies(): List<ModificationTracker> {
return with(KotlinModificationTrackerService.getInstance(project)) {
if (!this@getExternalDependencies.isLocal) return listOf(outOfBlockModificationTracker)
else when (val file = containingFile) {
is KtFile -> listOf(outOfBlockModificationTracker, fileModificationTracker(file))
else -> listOf(outOfBlockModificationTracker)
}
}
}
@@ -0,0 +1,91 @@
/*
* 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.classes
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.impl.light.LightClass
import com.intellij.psi.impl.light.LightMethod
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
internal fun <Psi : PsiElement> collectAugments(element: PsiElement, type: Class<out Psi>): List<Psi> {
return PsiAugmentProvider.collectAugments(element, type, null)
}
fun getParentForLocalDeclaration(classOrObject: KtClassOrObject): PsiElement? {
fun getParentByPsiMethod(method: PsiMethod?, name: String?, forceMethodWrapping: Boolean): PsiElement? {
if (method == null || name == null) return null
var containingClass: PsiClass? = method.containingClass ?: return null
val currentFileName = classOrObject.containingFile.name
var createWrapper = forceMethodWrapping
// Use PsiClass wrapper instead of package light class to avoid names like "FooPackage" in Type Hierarchy and related views
if (containingClass is KtLightClassForFacade) {
containingClass = object : LightClass(containingClass as KtLightClassForFacade, KotlinLanguage.INSTANCE) {
override fun getName(): String = currentFileName
}
createWrapper = true
}
if (createWrapper) {
return object : LightMethod(classOrObject.manager, method, containingClass!!, KotlinLanguage.INSTANCE) {
override fun getParent(): PsiElement = getContainingClass()
override fun getName(): String = name
}
}
return method
}
var declaration: PsiElement? = KtPsiUtil.getTopmostParentOfTypes(
classOrObject,
KtNamedFunction::class.java,
KtConstructor::class.java,
KtProperty::class.java,
KtAnonymousInitializer::class.java,
KtParameter::class.java
)
if (declaration is KtParameter) {
declaration = declaration.getStrictParentOfType<KtNamedDeclaration>()
}
if (declaration is KtFunction) {
return getParentByPsiMethod(
LightClassUtil.getLightClassMethod(declaration),
declaration.name,
forceMethodWrapping = false
)
}
// Represent the property as a fake method with the same name
if (declaration is KtProperty) {
return getParentByPsiMethod(
LightClassUtil.getLightClassPropertyMethods(declaration).getter,
declaration.name,
forceMethodWrapping = true
)
}
if (declaration is KtAnonymousInitializer) {
val parent = declaration.parent
val grandparent = parent.parent
if (parent is KtClassBody && grandparent is KtClassOrObject) {
return grandparent.toLightClass()
}
}
return if (declaration is KtClass) declaration.toLightClass() else null
}
@@ -0,0 +1,14 @@
/*
* 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.classes
const val METHOD_INDEX_FOR_GETTER = 1
const val METHOD_INDEX_FOR_SETTER = 2
const val METHOD_INDEX_FOR_DEFAULT_CTOR = 3
const val METHOD_INDEX_FOR_NO_ARG_OVERLOAD_CTOR = 4
const val METHOD_INDEX_FOR_NON_ORIGIN_METHOD = 5
const val METHOD_INDEX_FOR_SCRIPT_MAIN = 6
const val METHOD_INDEX_BASE = 7
@@ -0,0 +1,125 @@
/*
* 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.elements
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.ClsStubPsiFactory
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.psi.impl.source.PsiFileImpl
import com.intellij.psi.impl.source.SourceTreeToPsiMap
import com.intellij.psi.impl.source.tree.TreeElement
import com.intellij.psi.util.PsiUtil
import com.intellij.reference.SoftReference
import com.intellij.util.AstLoadingFilter
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import java.lang.ref.Reference
open class FakeFileForLightClass(
val ktFile: KtFile,
private val lightClass: () -> KtLightClass,
private val packageFqName: FqName = ktFile.packageFqName,
) : ClsFileImpl(ktFile.viewProvider) {
override fun getVirtualFile(): VirtualFile =
ktFile.virtualFile ?: ktFile.originalFile.virtualFile ?: super.getVirtualFile()
override fun getPackageName() = packageFqName.asString()
private fun createFakeJavaFileStub(): PsiJavaFileStub {
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /* compiled = */true)
javaFileStub.psiFactory = ClsStubPsiFactory.INSTANCE
javaFileStub.psi = this
return javaFileStub
}
override fun getStub() = createFakeJavaFileStub()
override fun getClasses() = arrayOf(lightClass())
override fun getNavigationElement() = ktFile
override fun accept(visitor: PsiElementVisitor) {
// Prevent access to compiled PSI
// TODO: More complex traversal logic may be implemented when necessary
}
@Volatile
private var myMirrorFileElement: Reference<TreeElement>? = null
private val myMirrorLock: Any = Any()
override fun getMirror(): PsiElement {
SoftReference.dereference(myMirrorFileElement)?.let { return it.psi }
val mirrorElement = synchronized(myMirrorLock) {
SoftReference.dereference(myMirrorFileElement)?.let { return@synchronized it }
val file = this.virtualFile
AstLoadingFilter.assertTreeLoadingAllowed(file)
val classes: Array<KtLightClass> = this.classes
val fileName = (if (classes.isNotEmpty()) classes[0].name else file.nameWithoutExtension) + ".java"
val document = FileDocumentManager.getInstance().getDocument(file) ?: error(file.url)
val factory = PsiFileFactory.getInstance(this.manager.project)
val mirror = factory.createFileFromText(fileName, JavaLanguage.INSTANCE, document.immutableCharSequence, false, false, true)
mirror.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, this.languageLevel)
val mirrorTreeElement = SourceTreeToPsiMap.psiToTreeNotNull(mirror)
if (mirror is PsiFileImpl) {
mirror.originalFile = this
}
mirrorTreeElement.also {
myMirrorFileElement = SoftReference(it)
}
}
return mirrorElement.psi
}
// this should be equal to current compiler target language level
override fun getLanguageLevel() = LanguageLevel.JDK_1_8
override fun hashCode(): Int {
val thisClass = lightClass()
if (thisClass is KtLightClassForSourceDeclaration) return ktFile.hashCode()
return thisClass.hashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FakeFileForLightClass) return false
val thisClass = lightClass()
val anotherClass = other.lightClass()
if (thisClass is KtLightClassForSourceDeclaration) {
return anotherClass is KtLightClassForSourceDeclaration && ktFile == other.ktFile
}
return thisClass == anotherClass
}
override fun isEquivalentTo(another: PsiElement?) = this == another
override fun setPackageName(packageName: String) {
if (lightClass() is KtLightClassForFacade) {
ktFile.packageDirective?.fqName = FqName(packageName)
} else {
super.setPackageName(packageName)
}
}
override fun isPhysical() = false
}
@@ -0,0 +1,17 @@
/*
* 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.elements
import com.intellij.psi.PsiTypeParameterListOwner
import com.intellij.psi.impl.light.LightTypeParameterBuilder
import org.jetbrains.kotlin.psi.KtTypeParameter
open class KotlinLightTypeParameterBuilder(
name: String,
owner: PsiTypeParameterListOwner,
index: Int,
override val origin: KtTypeParameter
) : LightTypeParameterBuilder(name, owner, index), PsiElementWithOrigin<KtTypeParameter>
@@ -0,0 +1,31 @@
/*
* 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.elements
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiTypeParameterListOwner
import com.intellij.psi.ResolveState
import com.intellij.psi.impl.light.LightTypeParameterListBuilder
import com.intellij.psi.scope.PsiScopeProcessor
import org.jetbrains.kotlin.idea.KotlinLanguage
class KotlinLightTypeParameterListBuilder(private val owner: PsiTypeParameterListOwner) :
LightTypeParameterListBuilder(owner.manager, KotlinLanguage.INSTANCE) {
override fun processDeclarations(
processor: PsiScopeProcessor,
state: ResolveState,
lastParent: PsiElement?,
place: PsiElement
): Boolean {
return typeParameters.all { processor.execute(it, state) }
}
override fun getParent(): PsiElement = owner
override fun getContainingFile(): PsiFile = owner.containingFile
override fun getText(): String? = ""
}
@@ -0,0 +1,21 @@
/*
* 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.elements
import com.intellij.psi.*
import org.jetbrains.kotlin.psi.KtCallElement
abstract class KtLightAbstractAnnotation(parent: PsiElement) :
KtLightElementBase(parent), PsiAnnotation, KtLightElement<KtCallElement, PsiAnnotation> {
abstract override fun getNameReferenceElement(): PsiJavaCodeReferenceElement?
override fun getOwner(): PsiAnnotationOwner? = parent as? PsiAnnotationOwner
abstract override fun getParameterList(): PsiAnnotationParameterList
open fun fqNameMatches(fqName: String): Boolean = qualifiedName == fqName
}
@@ -0,0 +1,77 @@
/*
* 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.elements
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightIdentifier
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtElement
class KtLightPsiArrayInitializerMemberValue(
override val kotlinOrigin: KtElement,
private val lightParent: PsiElement,
private val arguments: (KtLightPsiArrayInitializerMemberValue) -> List<PsiAnnotationMemberValue>
) : KtLightElementBase(lightParent), PsiArrayInitializerMemberValue {
override fun getInitializers(): Array<PsiAnnotationMemberValue> = arguments(this).toTypedArray()
override fun getParent(): PsiElement = lightParent
override fun isPhysical(): Boolean = false
}
fun psiType(kotlinFqName: String, context: PsiElement, boxPrimitiveType: Boolean = false): PsiType {
if (!boxPrimitiveType) {
when (kotlinFqName) {
"kotlin.Int" -> return PsiType.INT
"kotlin.Long" -> return PsiType.LONG
"kotlin.Short" -> return PsiType.SHORT
"kotlin.Boolean" -> return PsiType.BOOLEAN
"kotlin.Byte" -> return PsiType.BYTE
"kotlin.Char" -> return PsiType.CHAR
"kotlin.Double" -> return PsiType.DOUBLE
"kotlin.Float" -> return PsiType.FLOAT
}
}
when (kotlinFqName) {
"kotlin.IntArray" -> return PsiType.INT.createArrayType()
"kotlin.LongArray" -> return PsiType.LONG.createArrayType()
"kotlin.ShortArray" -> return PsiType.SHORT.createArrayType()
"kotlin.BooleanArray" -> return PsiType.BOOLEAN.createArrayType()
"kotlin.ByteArray" -> return PsiType.BYTE.createArrayType()
"kotlin.CharArray" -> return PsiType.CHAR.createArrayType()
"kotlin.DoubleArray" -> return PsiType.DOUBLE.createArrayType()
"kotlin.FloatArray" -> return PsiType.FLOAT.createArrayType()
}
val javaFqName = JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(kotlinFqName))?.asSingleFqName()?.asString() ?: kotlinFqName
return PsiType.getTypeByName(javaFqName, context.project, context.resolveScope)
}
class KtLightPsiNameValuePair(
override val kotlinOrigin: KtElement,
private val name: String,
lightParent: PsiElement,
private val argument: (KtLightPsiNameValuePair) -> PsiAnnotationMemberValue?
) : KtLightElementBase(lightParent),
PsiNameValuePair {
override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue = cannotModify()
override fun getNameIdentifier(): PsiIdentifier? = LightIdentifier(kotlinOrigin.manager, name)
override fun getName(): String? = name
private val _value: PsiAnnotationMemberValue? by lazyPub { argument(this) }
override fun getValue(): PsiAnnotationMemberValue? = _value
override fun getLiteralValue(): String? = (value as? PsiLiteralExpression)?.value?.toString()
}
@@ -0,0 +1,45 @@
/*
* 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.elements
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.light.LightElement
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtElement
abstract class KtLightElementBase(private var parent: PsiElement) : LightElement(parent.manager, KotlinLanguage.INSTANCE) {
override fun toString() = "${this.javaClass.simpleName} of $parent"
override fun getParent(): PsiElement = parent
abstract val kotlinOrigin: KtElement?
fun setParent(newParent: PsiElement) {
parent = newParent
}
override fun getText() = kotlinOrigin?.text ?: ""
override fun getTextRange() = kotlinOrigin?.textRange ?: TextRange.EMPTY_RANGE
override fun getTextOffset() = kotlinOrigin?.textOffset ?: 0
override fun getStartOffsetInParent() = kotlinOrigin?.startOffsetInParent ?: 0
override fun isWritable() = kotlinOrigin?.isWritable ?: false
override fun getNavigationElement() = kotlinOrigin?.navigationElement ?: this
override fun getUseScope() = kotlinOrigin?.useScope ?: super.getUseScope()
override fun getContainingFile() = parent.containingFile
override fun getPresentation() = (kotlinOrigin ?: this).let { ItemPresentationProviders.getItemPresentation(it) }
override fun isValid() = parent.isValid && (kotlinOrigin?.isValid != false)
override fun findElementAt(offset: Int) = kotlinOrigin?.findElementAt(offset)
override fun isEquivalentTo(another: PsiElement?): Boolean {
if (super.isEquivalentTo(another)) {
return true
}
val origin = kotlinOrigin ?: return false
return origin.isEquivalentTo(another) ||
(another is KtLightElementBase && origin.isEquivalentTo(another.kotlinOrigin))
}
}
@@ -0,0 +1,53 @@
/*
* 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.elements
import com.intellij.psi.*
import com.intellij.psi.impl.PsiVariableEx
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
interface KtLightElement<out T : KtElement, out D : PsiElement> : PsiElement {
val kotlinOrigin: T?
/**
* KtLightModifierList by default retrieves annotation from the relevant KtElement or from clsDelegate
* But we have none of them for KtUltraLightAnnotationForDescriptor built upon descriptor
* For that case, KtLightModifierList in the beginning checks `givenAnnotations` and uses them if it's not null
* Probably, it's a bit dirty solution. But, for now it's not clear how to make it better
*/
val givenAnnotations: List<KtLightAbstractAnnotation>? get() = null
}
interface KtLightDeclaration<out T : KtDeclaration, out D : PsiElement> : KtLightElement<T, D>, PsiNamedElement
interface KtLightMember<out D : PsiMember> : PsiMember, KtLightDeclaration<KtDeclaration, D>, PsiNameIdentifierOwner, PsiDocCommentOwner {
val lightMemberOrigin: LightMemberOrigin?
override fun getContainingClass(): KtLightClass
}
interface KtLightField : PsiField, KtLightMember<PsiField>, PsiVariableEx
interface KtLightParameter : PsiParameter, KtLightDeclaration<KtParameter, PsiParameter> {
val method: KtLightMethod
}
interface KtLightFieldForSourceDeclarationSupport : PsiField {
val kotlinOrigin: KtDeclaration?
}
interface KtLightMethod : PsiAnnotationMethod, KtLightMember<PsiMethod> {
val isDelegated: Boolean
get() = lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION
|| lightMemberOrigin?.originKind == JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL
val isMangled: Boolean
}
@@ -0,0 +1,40 @@
/*
* 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.elements
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.light.LightIdentifier
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
abstract class KtLightIdentifierBase(
private val lightOwner: PsiElement,
text: String?
) : LightIdentifier(lightOwner.manager, text), PsiElementWithOrigin<PsiElement> {
override fun isPhysical() = true
override fun getParent() = lightOwner
override fun getContainingFile() = lightOwner.containingFile
override fun getTextRange() = origin?.textRange ?: TextRange.EMPTY_RANGE
override fun getTextOffset(): Int = origin?.textOffset ?: -1
}
open class KtLightIdentifier(
lightOwner: PsiElement,
private val ktDeclaration: KtDeclaration?
) : KtLightIdentifierBase(lightOwner, ktDeclaration?.name) {
override val origin: PsiElement?
get() = when (ktDeclaration) {
is KtSecondaryConstructor -> ktDeclaration.getConstructorKeyword()
is KtPrimaryConstructor -> ktDeclaration.getConstructorKeyword()
?: ktDeclaration.valueParameterList
?: ktDeclaration.containingClassOrObject?.nameIdentifier
is KtPropertyAccessor -> ktDeclaration.namePlaceholder
is KtNamedDeclaration -> ktDeclaration.nameIdentifier
else -> null
}
}
@@ -0,0 +1,52 @@
/*
* 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.elements
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiIdentifier
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiModifierList
import com.intellij.psi.javadoc.PsiDocComment
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
abstract class KtLightMemberImpl<out D : PsiMember>(
override val lightMemberOrigin: LightMemberOriginForDeclaration?,
private val containingClass: KtLightClass,
) : KtLightElementBase(containingClass), PsiMember, KtLightMember<D> {
private val lightIdentifier by lazyPub { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) }
abstract override fun hasModifierProperty(name: String): Boolean
abstract override fun getModifierList(): PsiModifierList?
override fun toString(): String = "${this::class.java.simpleName}:$name"
override fun getContainingClass() = containingClass
abstract override fun getName(): String
override fun getNameIdentifier(): PsiIdentifier = lightIdentifier
override val kotlinOrigin: KtDeclaration? get() = lightMemberOrigin?.originalElement
abstract override fun getDocComment(): PsiDocComment?
abstract override fun isDeprecated(): Boolean
override fun isValid(): Boolean {
return parent.isValid && lightMemberOrigin?.isValid() != false
}
override fun isEquivalentTo(another: PsiElement?): Boolean {
return this == another ||
lightMemberOrigin?.isEquivalentTo(another) == true ||
another is KtLightMember<*> && lightMemberOrigin?.isEquivalentTo(another.lightMemberOrigin) == true
}
}
@@ -0,0 +1,165 @@
/*
* 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.elements
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.MethodSignature
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod
import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.psi.*
abstract class KtLightMethodImpl protected constructor(
lightMemberOrigin: LightMemberOriginForDeclaration?,
containingClass: KtLightClass,
) : KtLightMemberImpl<PsiMethod>(lightMemberOrigin, containingClass), KtLightMethod {
private val calculatingReturnType = ThreadLocal<Boolean>()
private val paramsList: PsiParameterList by lazyPub {
val parameters = buildParametersForList()
KtLightParameterList(this, parameters.size) {
parameters
}
}
protected abstract fun buildParametersForList(): List<PsiParameter>
private val typeParamsList: PsiTypeParameterList? by lazyPub { buildTypeParameterList() }
protected abstract fun buildTypeParameterList(): PsiTypeParameterList?
override fun accept(visitor: PsiElementVisitor) {
if (visitor is JavaElementVisitor) {
visitor.visitMethod(this)
} else {
visitor.visitElement(this)
}
}
override val isMangled: Boolean get() = checkIsMangled()
override fun setName(name: String): PsiElement? {
val jvmNameAnnotation = modifierList.findAnnotation(JvmFileClassUtil.JVM_NAME.asString())?.unwrapped as? KtAnnotationEntry
val demangledName = (if (isMangled) demangleInternalName(name) else null) ?: name
val newNameForOrigin = propertyNameByAccessor(demangledName, this) ?: demangledName
if (newNameForOrigin == kotlinOrigin?.name) {
jvmNameAnnotation?.delete()
return this
}
val nameExpression = jvmNameAnnotation?.let { JvmFileClassUtil.getLiteralStringEntryFromAnnotation(it) }
if (nameExpression != null) {
nameExpression.replace(KtPsiFactory(this).createLiteralStringTemplateEntry(name))
} else {
val toRename = kotlinOrigin as? PsiNamedElement ?: cannotModify()
toRename.setName(newNameForOrigin)
}
return this
}
override fun delete() {
kotlinOrigin?.let {
if (it.isValid) {
it.delete()
}
} ?: cannotModify()
}
abstract override fun getModifierList(): PsiModifierList
override fun getParameterList() = paramsList
override fun getTypeParameterList() = typeParamsList
override fun getTypeParameters(): Array<PsiTypeParameter> =
typeParameterList?.typeParameters ?: PsiTypeParameter.EMPTY_ARRAY
override fun hasTypeParameters() = typeParameters.isNotEmpty()
abstract override fun getSignature(substitutor: PsiSubstitutor): MethodSignature
override fun processDeclarations(
processor: PsiScopeProcessor,
state: ResolveState,
lastParent: PsiElement?,
place: PsiElement
): Boolean {
return typeParameters.all { processor.execute(it, state) }
}
/* comparing origin and member index should be enough to determine equality:
for compiled elements origin contains delegate
for source elements index is unique to each member
*/
override fun equals(other: Any?): Boolean = other === this ||
other is KtLightMethodImpl &&
other.javaClass == javaClass &&
other.containingClass == containingClass &&
other.lightMemberOrigin == lightMemberOrigin
override fun hashCode(): Int = name.hashCode().times(31).plus(containingClass.hashCode())
abstract override fun getDefaultValue(): PsiAnnotationMemberValue?
abstract override fun getReturnTypeElement(): PsiTypeElement?
override fun getReturnType(): PsiType? {
calculatingReturnType.set(true)
try {
return returnTypeElement?.type
} finally {
calculatingReturnType.set(false)
}
}
override fun getTextOffset(): Int {
val auxiliaryOrigin = lightMemberOrigin?.auxiliaryOriginalElement
if (auxiliaryOrigin is KtPropertyAccessor) {
return auxiliaryOrigin.textOffset
}
return super.getTextOffset()
}
override fun getTextRange(): TextRange {
val auxiliaryOrigin = lightMemberOrigin?.auxiliaryOriginalElement
if (auxiliaryOrigin is KtPropertyAccessor) {
return auxiliaryOrigin.textRange
}
return super.getTextRange()
}
abstract override fun getThrowsList(): PsiReferenceList
abstract override fun isVarArgs(): Boolean
abstract override fun isConstructor(): Boolean
abstract override fun getHierarchicalMethodSignature(): HierarchicalMethodSignature
abstract override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean): List<MethodSignatureBackedByPsiMethod>
override fun getBody() = null
@Suppress("DEPRECATION")
abstract override fun findDeepestSuperMethod(): PsiMethod?
abstract override fun findDeepestSuperMethods(): Array<out PsiMethod>
abstract override fun findSuperMethods(): Array<out PsiMethod>
abstract override fun findSuperMethods(checkAccess: Boolean): Array<out PsiMethod>
abstract override fun findSuperMethods(parentClass: PsiClass?): Array<out PsiMethod>
}
@@ -0,0 +1,57 @@
/*
* 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.elements
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiModifierList
import com.intellij.psi.PsiModifierListOwner
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.KtModifierListOwner
abstract class KtLightModifierList<out T : KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(
protected val owner: T
) : KtLightElementBase(owner), PsiModifierList, KtLightElement<KtModifierList, PsiModifierList> {
private val _annotations by lazyPub {
val annotations = computeAnnotations()
annotationsFilter?.let(annotations::filter) ?: annotations
}
protected open val annotationsFilter: ((KtLightAbstractAnnotation) -> Boolean)? = null
override val kotlinOrigin: KtModifierList?
get() = owner.kotlinOrigin?.modifierList
override fun getParent() = owner
override fun hasExplicitModifier(name: String) = hasModifierProperty(name)
private fun throwInvalidOperation(): Nothing = throw IncorrectOperationException()
override fun setModifierProperty(name: String, value: Boolean): Unit = throwInvalidOperation()
override fun checkSetModifierProperty(name: String, value: Boolean): Unit = throwInvalidOperation()
override fun addAnnotation(qualifiedName: String): PsiAnnotation = throwInvalidOperation()
override fun getApplicableAnnotations(): Array<out PsiAnnotation> = annotations
override fun getAnnotations(): Array<out PsiAnnotation> = _annotations.toTypedArray()
override fun findAnnotation(qualifiedName: String) = _annotations.firstOrNull { it.fqNameMatches(qualifiedName) }
override fun isEquivalentTo(another: PsiElement?) =
another is KtLightModifierList<*> && owner == another.owner
override fun isWritable() = false
override fun toString() = "Light modifier list of $owner"
open fun nonSourceAnnotationsForAnnotationType(sourceAnnotations: List<PsiAnnotation>): List<KtLightAbstractAnnotation> = emptyList()
abstract fun computeAnnotations(): List<KtLightAbstractAnnotation>
}
@@ -0,0 +1,48 @@
/*
* 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.elements
import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiParameter
import com.intellij.psi.PsiParameterList
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFunction
class KtLightParameterList(
private val parent: KtLightMethod,
private val parametersCount: Int,
computeParameters: () -> List<PsiParameter>
) : KtLightElementBase(parent), PsiParameterList {
override val kotlinOrigin: KtElement?
get() = (parent.kotlinOrigin as? KtFunction)?.valueParameterList
private val _parameters: Array<PsiParameter> by lazyPub { computeParameters().toTypedArray() }
override fun getParameters() = _parameters
override fun getParameterIndex(parameter: PsiParameter) = _parameters.indexOf(parameter)
override fun getParametersCount() = parametersCount
override fun accept(visitor: PsiElementVisitor) {
if (visitor is JavaElementVisitor) {
visitor.visitParameterList(this)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KtLightParameterList
if (parent != other.parent) return false
return true
}
override fun hashCode(): Int = parent.hashCode()
}
@@ -0,0 +1,71 @@
/*
* 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.elements
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.classes.lazyPub
class KtLightPsiJavaCodeReferenceElement(
private val ktElement: PsiElement,
reference: () -> PsiReference?,
private val customReferenceName: String? = null,
) :
PsiElement by ktElement,
PsiReference by LazyPsiReferenceDelegate(ktElement, reference),
PsiJavaCodeReferenceElement {
override fun advancedResolve(incompleteCode: Boolean): JavaResolveResult = JavaResolveResult.EMPTY
override fun getReferenceNameElement(): PsiElement? = null
override fun getTypeParameters(): Array<PsiType> = emptyArray()
override fun getReferenceName(): String? = customReferenceName
override fun isQualified(): Boolean = false
override fun processVariants(processor: PsiScopeProcessor) = Unit
override fun multiResolve(incompleteCode: Boolean): Array<JavaResolveResult> = emptyArray()
override fun getQualifiedName(): String? = null
override fun getQualifier(): PsiElement? = null
override fun getParameterList(): PsiReferenceParameterList? = null
}
private class LazyPsiReferenceDelegate(
private val psiElement: PsiElement,
referenceProvider: () -> PsiReference?
) : PsiReference {
private val delegate by lazyPub(referenceProvider)
override fun getElement(): PsiElement = psiElement
override fun resolve(): PsiElement? = delegate?.resolve()
override fun getRangeInElement(): TextRange = delegate?.rangeInElement ?: psiElement.textRange
override fun getCanonicalText(): String = delegate?.canonicalText ?: "<no-text>"
@Throws(IncorrectOperationException::class)
override fun handleElementRename(newElementName: String): PsiElement = delegate?.handleElementRename(newElementName) ?: element
@Throws(IncorrectOperationException::class)
override fun bindToElement(element: PsiElement): PsiElement =
delegate?.bindToElement(element) ?: throw IncorrectOperationException("can't rename LazyPsiReferenceDelegate")
override fun isSoft(): Boolean = delegate?.isSoft ?: false
override fun isReferenceTo(element: PsiElement): Boolean = delegate?.isReferenceTo(element) ?: false
override fun getVariants(): Array<Any> = delegate?.variants ?: emptyArray()
}
@@ -0,0 +1,38 @@
/*
* 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.elements
import com.intellij.lang.Language
import com.intellij.psi.*
// Based on com.intellij.psi.impl.light.LightParameter
open class LightParameter @JvmOverloads constructor(
private val myName: String,
type: PsiType,
val method: KtLightMethod,
language: Language?,
private val myVarArgs: Boolean = type is PsiEllipsisType
) : LightVariableBuilder(method.manager, myName, type, language),
PsiParameter {
override fun getDeclarationScope(): KtLightMethod = method
override fun accept(visitor: PsiElementVisitor) {
if (visitor is JavaElementVisitor) {
visitor.visitParameter(this)
}
}
override fun toString(): String = "Light Parameter"
override fun isVarArgs(): Boolean = myVarArgs
override fun getName(): String = myName
companion object {
val EMPTY_ARRAY = arrayOfNulls<LightParameter>(0)
}
}
@@ -0,0 +1,104 @@
/*
* 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.elements;
import com.intellij.lang.Language;
import com.intellij.navigation.NavigationItem;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.impl.light.LightModifierList;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinIconProviderService;
import javax.swing.*;
// Based on com.intellij.psi.impl.light.LightVariableBuilder
public class LightVariableBuilder extends LightElement implements PsiVariable, NavigationItem {
private final String myName;
private final PsiType myType;
private final LightModifierList myModifierList;
public LightVariableBuilder(PsiManager manager, @NotNull String name, @NotNull PsiType type, Language language) {
super(manager, language);
myName = name;
myType = type;
myModifierList = new LightModifierList(manager);
}
@Override
public String toString() {
return "LightVariableBuilder:" + getName();
}
@NotNull
@Override
public PsiType getType() {
return myType;
}
@Override
@NotNull
public PsiModifierList getModifierList() {
return myModifierList;
}
@Override
public boolean hasModifierProperty(@NonNls @NotNull String name) {
return myModifierList.hasModifierProperty(name);
}
@NotNull
@Override
public String getName() {
return myName;
}
@Override
public PsiTypeElement getTypeElement() {
return null;
}
@Override
public PsiExpression getInitializer() {
return null;
}
@Override
public boolean hasInitializer() {
return false;
}
@Override
public void normalizeDeclaration() throws IncorrectOperationException {
}
@Override
public Object computeConstantValue() {
return null;
}
@Override
public PsiIdentifier getNameIdentifier() {
return null;
}
@Override
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
throw new UnsupportedOperationException("setName is not implemented yet in org.jetbrains.kotlin.asJava.light.LightVariableBuilder");
}
@Override
protected boolean isVisibilitySupported() {
return true;
}
@Override
public Icon getElementIcon(int flags) {
return KotlinIconProviderService.getInstance().getLightVariableIcon(this, flags);
}
}
@@ -0,0 +1,12 @@
/*
* 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.elements
import com.intellij.psi.PsiElement
interface PsiElementWithOrigin<out T> : PsiElement where T : PsiElement {
val origin: T?
}
@@ -0,0 +1,35 @@
/*
* 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.elements
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.*
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)
@@ -0,0 +1,190 @@
/*
* 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.finder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Condition
import com.intellij.psi.*
import com.intellij.psi.impl.compiled.ClsClassImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiUtilCore
import com.intellij.util.SmartList
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.hasRepeatableAnnotationContainer
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isValidJavaFqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.KotlinFinderMarker
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
class JavaElementFinder(
private val project: Project,
) : PsiElementFinder(), KotlinFinderMarker {
private val psiManager = PsiManager.getInstance(project)
private val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(project)
override fun findClass(qualifiedName: String, scope: GlobalSearchScope) = findClasses(qualifiedName, scope).firstOrNull()
override fun findClasses(qualifiedNameString: String, scope: GlobalSearchScope): Array<PsiClass> {
if (!isValidJavaFqName(qualifiedNameString)) {
return PsiClass.EMPTY_ARRAY
}
val answer = SmartList<PsiClass>()
val qualifiedName = FqName(qualifiedNameString)
findClassesAndObjects(qualifiedName, scope, answer)
answer.addAll(kotlinAsJavaSupport.getFacadeClasses(qualifiedName, scope))
answer.addAll(kotlinAsJavaSupport.getKotlinInternalClasses(qualifiedName, scope))
sortByPreferenceToSourceFile(answer, scope)
return answer.toTypedArray()
}
// Finds explicitly declared classes and objects, not package classes
// Also DefaultImpls classes of interfaces, Container classes of repeatable annotations
private fun findClassesAndObjects(qualifiedName: FqName, scope: GlobalSearchScope, answer: MutableList<PsiClass>) {
findInterfaceDefaultImpls(qualifiedName, scope, answer)
findRepeatableAnnotationContainer(qualifiedName, scope, answer)
val classOrObjectDeclarations = kotlinAsJavaSupport.findClassOrObjectDeclarations(qualifiedName, scope)
for (declaration in classOrObjectDeclarations) {
if (declaration !is KtEnumEntry) {
val lightClass = kotlinAsJavaSupport.getLightClass(declaration)
if (lightClass != null) {
answer.add(lightClass)
}
}
}
}
private fun findInterfaceDefaultImpls(qualifiedName: FqName, scope: GlobalSearchScope, answer: MutableList<PsiClass>) =
findSyntheticInnerClass(qualifiedName, JvmAbi.DEFAULT_IMPLS_CLASS_NAME, scope, answer) {
it is KtClass && it.isInterface()
}
private fun findRepeatableAnnotationContainer(qualifiedName: FqName, scope: GlobalSearchScope, answer: MutableList<PsiClass>) =
findSyntheticInnerClass(qualifiedName, JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME, scope, answer) {
it.hasRepeatableAnnotationContainer
}
private fun findSyntheticInnerClass(
qualifiedName: FqName,
syntheticName: String,
scope: GlobalSearchScope,
answer: MutableList<PsiClass>,
predicate: (KtClassOrObject) -> Boolean,
) {
if (qualifiedName.isRoot || qualifiedName.shortName().asString() != syntheticName) return
for (classOrObject in kotlinAsJavaSupport.findClassOrObjectDeclarations(qualifiedName.parent(), scope)) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
if (predicate(classOrObject)) {
val interfaceClass = kotlinAsJavaSupport.getLightClass(classOrObject) ?: continue
val implsClass = interfaceClass.findInnerClassByName(syntheticName, false) ?: continue
answer.add(implsClass)
}
}
}
override fun getClassNames(psiPackage: PsiPackage, scope: GlobalSearchScope): Set<String> {
val packageFQN = FqName(psiPackage.qualifiedName)
val declarations = kotlinAsJavaSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope)
val answer = hashSetOf<String>()
answer.addAll(kotlinAsJavaSupport.getFacadeNames(packageFQN, scope))
for (declaration in declarations) {
val name = declaration.name ?: continue
answer.add(name)
}
return answer
}
override fun findPackage(qualifiedNameString: String): PsiPackage? {
if (!isValidJavaFqName(qualifiedNameString)) {
return null
}
val fqName = FqName(qualifiedNameString)
// allScope() because the contract says that the whole project
val allScope = GlobalSearchScope.allScope(project)
return if (kotlinAsJavaSupport.packageExists(fqName, allScope)) {
KtLightPackage(psiManager, fqName, allScope)
} else null
}
override fun getSubPackages(psiPackage: PsiPackage, scope: GlobalSearchScope): Array<PsiPackage> {
val subpackages = kotlinAsJavaSupport.getSubPackages(FqName(psiPackage.qualifiedName), scope)
return subpackages.map { KtLightPackage(psiManager, it, scope) }.toTypedArray()
}
override fun getClasses(psiPackage: PsiPackage, scope: GlobalSearchScope): Array<PsiClass> {
val answer = SmartList<PsiClass>()
val packageFQN = FqName(psiPackage.qualifiedName)
answer.addAll(kotlinAsJavaSupport.getFacadeClassesInPackage(packageFQN, scope))
val declarations = kotlinAsJavaSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope)
for (declaration in declarations) {
val aClass = kotlinAsJavaSupport.getLightClass(declaration) ?: continue
answer.add(aClass)
}
sortByPreferenceToSourceFile(answer, scope)
return answer.toTypedArray()
}
private fun sortByPreferenceToSourceFile(list: SmartList<PsiClass>, searchScope: GlobalSearchScope) {
if (list.size < 2) return
// NOTE: this comparator might violate the contract depending on the scope passed
ContainerUtil.quickSort(list, byClasspathComparator(searchScope))
list.sortBy { it !is ClsClassImpl }
}
// TODO: this does not take into account JvmPackageName annotation
override fun getPackageFiles(psiPackage: PsiPackage, scope: GlobalSearchScope): Array<PsiFile> =
kotlinAsJavaSupport.findFilesForPackage(FqName(psiPackage.qualifiedName), scope).toTypedArray()
override fun getPackageFilesFilter(psiPackage: PsiPackage, scope: GlobalSearchScope): Condition<PsiFile> = Condition { input ->
if (input !is KtFile) {
true
} else {
psiPackage.qualifiedName == input.packageFqName.asString()
}
}
companion object {
fun getInstance(project: Project): JavaElementFinder =
EP.getPoint(project).extensions.firstIsInstanceOrNull()
?: error(JavaElementFinder::class.java.simpleName + " is not found for project " + project)
fun byClasspathComparator(searchScope: GlobalSearchScope): Comparator<PsiElement> = Comparator { o1, o2 ->
val f1 = PsiUtilCore.getVirtualFile(o1)
val f2 = PsiUtilCore.getVirtualFile(o2)
when {
f1 === f2 -> 0
f1 == null -> -1
f2 == null -> 1
else -> searchScope.compare(f2, f1)
}
}
}
}
@@ -0,0 +1,37 @@
/*
* 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.finder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.file.PsiPackageImpl;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport;
import org.jetbrains.kotlin.name.FqName;
public class KtLightPackage extends PsiPackageImpl {
private final FqName fqName;
private final GlobalSearchScope scope;
public KtLightPackage(PsiManager manager, FqName qualifiedName, GlobalSearchScope scope) {
super(manager, qualifiedName.asString());
this.fqName = qualifiedName;
this.scope = scope;
}
@NotNull
@Override
public PsiElement copy() {
return new KtLightPackage(getManager(), fqName, scope);
}
@Override
public boolean isValid() {
return KotlinAsJavaSupport.getInstance(getProject()).packageExists(fqName, scope);
}
}
@@ -0,0 +1,315 @@
/*
* 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.lang.jvm.JvmModifier
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.classes.KtFakeLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
import org.jetbrains.kotlin.load.java.propertyNameBySetMethodName
import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
/**
* Can be null in scripts and for elements from non-jvm modules.
*/
fun KtClassOrObject.toLightClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(project).getLightClass(this)
fun KtClassOrObject.toLightClassWithBuiltinMapping(): PsiClass? {
toLightClass()?.let { return it }
val fqName = fqName ?: return null
val javaClassFqName = JavaToKotlinClassMap.mapKotlinToJava(fqName.toUnsafe())?.asSingleFqName() ?: return null
val searchScope = useScope as? GlobalSearchScope ?: return null
return JavaPsiFacade.getInstance(project).findClass(javaClassFqName.asString(), searchScope)
}
fun KtClassOrObject.toFakeLightClass(): KtFakeLightClass = KotlinAsJavaSupport.getInstance(project).getFakeLightClass(this)
fun KtFile.findFacadeClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(project)
.getFacadeClassesInPackage(packageFqName, this.useScope as? GlobalSearchScope ?: GlobalSearchScope.projectScope(project))
.firstOrNull { it is KtLightClassForFacade && this in it.files } as? KtLightClass
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) {
is PsiElementWithOrigin<*> -> origin
is KtLightElement<*, *> -> kotlinOrigin
is KtLightElementBase -> kotlinOrigin
else -> this
}
val PsiElement.namedUnwrappedElement: PsiNamedElement?
get() = unwrapped?.getNonStrictParentOfType()
val KtClassOrObject.hasInterfaceDefaultImpls: Boolean
get() = this is KtClass && isInterface() && hasNonAbstractMembers(this)
val KtClassOrObject.hasRepeatableAnnotationContainer: Boolean
get() = this is KtClass &&
isAnnotation() &&
run {
var hasRepeatableAnnotation = false
for (annotation in annotationEntries) when (annotation.shortName?.asString()) {
"JvmRepeatable" -> return false
"Repeatable" -> {
if (annotation.valueArgumentList != null) return false
hasRepeatableAnnotation = true
}
}
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))
private val DEFAULT_IMPLS_CLASS_NAME = Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)
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 demangleInternalName(name: String): String? {
val indexOfDollar = name.indexOf('$')
return if (indexOfDollar >= 0) name.substring(0, indexOfDollar) else null
}
fun mangleInternalName(name: String, moduleName: String): String {
return name + "$" + NameUtils.sanitizeAsJavaIdentifier(moduleName)
}
fun KtLightMethod.checkIsMangled(): Boolean {
val demangledName = demangleInternalName(name) ?: return false
val originalName = propertyNameByAccessor(demangledName, this) ?: demangledName
return originalName == kotlinOrigin?.name
}
fun propertyNameByAccessor(name: String, accessor: KtLightMethod): String? {
val toRename = accessor.kotlinOrigin ?: return null
if (toRename !is KtProperty && toRename !is KtParameter) return null
val methodName = Name.guessByFirstCharacter(name)
val propertyName = toRename.name ?: ""
return when {
JvmAbi.isGetterName(name) -> propertyNameByGetMethodName(methodName)
JvmAbi.isSetterName(name) -> propertyNameBySetMethodName(methodName, propertyName.startsWith("is"))
else -> methodName
}?.asString()
}
fun accessorNameByPropertyName(name: String, accessor: KtLightMethod): String? = accessor.name.let { methodName ->
when {
JvmAbi.isGetterName(methodName) -> JvmAbi.getterName(name)
JvmAbi.isSetterName(methodName) -> JvmAbi.setterName(name)
else -> null
}
}
fun getAccessorNamesCandidatesByPropertyName(name: String): List<String> {
return listOf(JvmAbi.setterName(name), JvmAbi.getterName(name))
}
fun fastCheckIsNullabilityApplied(lightElement: KtLightElement<*, PsiModifierListOwner>): Boolean {
val elementIsApplicable = lightElement is KtLightMember<*> || lightElement is LightParameter
if (!elementIsApplicable) return false
val annotatedElement = lightElement.kotlinOrigin ?: return true
// all data-class generated members are not-null
if (annotatedElement is KtClass && annotatedElement.isData()) return true
// backing fields for lateinit props are skipped
if (lightElement is KtLightField && annotatedElement is KtProperty && annotatedElement.hasModifier(KtTokens.LATEINIT_KEYWORD)) return false
if (lightElement is KtLightMethod && (annotatedElement as? KtModifierListOwner)?.isPrivate() == true) {
return false
}
if (annotatedElement is KtParameter) {
val containingClassOrObject = annotatedElement.containingClassOrObject
if (containingClassOrObject?.isAnnotation() == true) return false
if ((containingClassOrObject as? KtClass)?.isEnum() == true) {
if (annotatedElement.parent.parent is KtPrimaryConstructor) return false
}
when (val parent = annotatedElement.parent.parent) {
is KtConstructor<*> -> if (lightElement is KtLightParameter && parent.isPrivate()) return false
is KtNamedFunction -> return !parent.isPrivate()
is KtPropertyAccessor -> return (parent.parent as? KtProperty)?.isPrivate() != true
}
}
return true
}
private val PsiMethod.canBeGetter: Boolean
get() = JvmAbi.isGetterName(name) && parameters.isEmpty() && returnTypeElement?.textMatches("void") != true
private val PsiMethod.canBeSetter: Boolean
get() = JvmAbi.isSetterName(name) && parameters.size == 1 && returnTypeElement?.textMatches("void") != false
private val PsiMethod.probablyCanHaveSyntheticAccessors: Boolean
get() = canHaveOverride && !hasTypeParameters() && !isFinalProperty
private val PsiMethod.getterName: Name? get() = propertyNameByGetMethodName(Name.identifier(name))
private val PsiMethod.setterNames: Collection<Name>? get() = propertyNamesBySetMethodName(Name.identifier(name)).takeIf { it.isNotEmpty() }
private val PsiMethod.isFinalProperty: Boolean
get() {
val property = unwrapped as? KtProperty ?: return false
if (property.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false
val containingClassOrObject = property.containingClassOrObject ?: return true
return containingClassOrObject is KtObjectDeclaration
}
private val PsiMethod.isTopLevelDeclaration: Boolean get() = unwrapped?.isTopLevelKtOrJavaMember() == true
val PsiMethod.syntheticAccessors: Collection<Name>
get() {
if (!probablyCanHaveSyntheticAccessors) return emptyList()
return when {
canBeGetter -> listOfNotNull(getterName)
canBeSetter -> setterNames.orEmpty()
else -> emptyList()
}
}
val PsiMethod.canHaveSyntheticAccessors: Boolean get() = probablyCanHaveSyntheticAccessors && (canBeGetter || canBeSetter)
val PsiMethod.canHaveSyntheticGetter: Boolean get() = probablyCanHaveSyntheticAccessors && canBeGetter
val PsiMethod.canHaveSyntheticSetter: Boolean get() = probablyCanHaveSyntheticAccessors && canBeSetter
val PsiMethod.syntheticGetter: Name? get() = if (canHaveSyntheticGetter) getterName else null
val PsiMethod.syntheticSetters: Collection<Name>? get() = if (canHaveSyntheticSetter) setterNames else null
/**
* Attention: only language constructs are checked. For example: static member, constructor, top-level property
* @return `false` if constraints are found. Otherwise, `true`
*/
val PsiMethod.canHaveOverride: Boolean get() = !hasModifier(JvmModifier.STATIC) && !isConstructor && !isTopLevelDeclaration