[light classes] move light-classes-base module to analysis
^KT-53097
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
+141
@@ -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);
|
||||
}
|
||||
}
|
||||
+65
@@ -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
|
||||
}
|
||||
+294
@@ -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()
|
||||
}
|
||||
+44
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -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;
|
||||
}
|
||||
}
|
||||
+57
@@ -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
|
||||
}
|
||||
}
|
||||
+72
@@ -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
|
||||
+70
@@ -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)
|
||||
}
|
||||
}
|
||||
+16
@@ -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()
|
||||
}
|
||||
+209
@@ -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)
|
||||
}
|
||||
+146
@@ -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}"
|
||||
}
|
||||
+278
@@ -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
|
||||
}
|
||||
+18
@@ -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?
|
||||
}
|
||||
+39
@@ -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
|
||||
}
|
||||
+69
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -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
|
||||
+125
@@ -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
|
||||
}
|
||||
+17
@@ -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>
|
||||
+31
@@ -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? = ""
|
||||
}
|
||||
+21
@@ -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
|
||||
}
|
||||
+77
@@ -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()
|
||||
|
||||
}
|
||||
+45
@@ -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))
|
||||
}
|
||||
}
|
||||
+53
@@ -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
|
||||
}
|
||||
+40
@@ -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
|
||||
}
|
||||
}
|
||||
+52
@@ -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
|
||||
}
|
||||
}
|
||||
+165
@@ -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>
|
||||
}
|
||||
+57
@@ -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>
|
||||
}
|
||||
+48
@@ -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()
|
||||
|
||||
}
|
||||
+71
@@ -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()
|
||||
}
|
||||
+38
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
+104
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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)
|
||||
+190
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -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
|
||||
Reference in New Issue
Block a user