From 956294821b1856db976db97287d4de7c064e1bf9 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 21 Mar 2023 20:27:36 +0100 Subject: [PATCH] [psi] don't load text for compiled code use stubs when possible, when stub doesn't contain anything, decompiled text won't contain it either, no sense to load --- .../decompiler/psi/file/KtDecompiledFile.kt | 8 - .../psi/text/ByDescriptorIndexer.kt | 182 ++++++++++++++---- ...clarationInCompiledFileSearcherFE10Impl.kt | 14 +- ...KotlinDeclarationInCompiledFileSearcher.kt | 159 ++++++++++++++- ...ractSymbolLightClassesStructureTestBase.kt | 26 ++- .../jetbrains/kotlin/psi/KtNamedFunction.java | 14 +- .../org/jetbrains/kotlin/psi/KtProperty.java | 4 +- .../kotlin/psi/KtPropertyAccessor.java | 14 +- .../jetbrains/kotlin/psi/psiUtil/psiUtils.kt | 9 + 9 files changed, 344 insertions(+), 86 deletions(-) diff --git a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/file/KtDecompiledFile.kt b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/file/KtDecompiledFile.kt index 635dcb9cb7a..76c0dc2e1f0 100644 --- a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/file/KtDecompiledFile.kt +++ b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/file/KtDecompiledFile.kt @@ -31,12 +31,4 @@ open class KtDecompiledFile( decompiledText.drop() } - fun getDeclaration(indexer: DecompiledTextIndexer, key: T): KtDeclaration? { - val range = decompiledText.get().index.getRange(indexer, key) ?: return null - return PsiTreeUtil.findElementOfClassAtRange(this@KtDecompiledFile, range.startOffset, range.endOffset, KtDeclaration::class.java) - } - - fun hasDeclarationWithKey(indexer: DecompiledTextIndexer, key: T): Boolean { - return decompiledText.get().index.getRange(indexer, key) != null - } } diff --git a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/ByDescriptorIndexer.kt b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/ByDescriptorIndexer.kt index f3839352112..b7d89d6ea1e 100644 --- a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/ByDescriptorIndexer.kt +++ b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/ByDescriptorIndexer.kt @@ -6,22 +6,26 @@ package org.jetbrains.kotlin.analysis.decompiler.psi.text import com.intellij.openapi.diagnostic.Logger +import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile import org.jetbrains.kotlin.analysis.decompiler.stub.COMPILED_DEFAULT_INITIALIZER -import org.jetbrains.kotlin.analysis.decompiler.stub.COMPILED_DEFAULT_PARAMETER_VALUE -import org.jetbrains.kotlin.builtins.DefaultBuiltIns -import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsSignatures +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.psi.KtCallableDeclaration -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtDeclarationContainer +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier +import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.AbbreviatedType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isNullableAny +import org.jetbrains.kotlin.utils.addIfNotNull object ByDescriptorIndexer : DecompiledTextIndexer { @@ -60,47 +64,117 @@ object ByDescriptorIndexer : DecompiledTextIndexer { return getDeclarationForDescriptor(original.containingDeclaration, file) } - val descriptorKey = original.toStringKey() + if (original is MemberDescriptor) { + val declarationContainer: KtDeclarationContainer? = when { + DescriptorUtils.isTopLevelDeclaration(original) -> file + original.containingDeclaration is ClassDescriptor -> + getDeclarationForDescriptor(original.containingDeclaration as ClassDescriptor, file) as? KtClassOrObject + else -> null + } - if (!file.isContentsLoaded && original is MemberDescriptor) { - val hasDeclarationByKey = file.hasDeclarationWithKey(this, descriptorKey) || (getBuiltinsDescriptorKey(descriptor)?.let { - file.hasDeclarationWithKey( - this, - it - ) - } ?: false) - if (hasDeclarationByKey) { - val declarationContainer: KtDeclarationContainer? = when { - DescriptorUtils.isTopLevelDeclaration(original) -> file - original.containingDeclaration is ClassDescriptor -> - getDeclarationForDescriptor(original.containingDeclaration as ClassDescriptor, file) as? KtClassOrObject - else -> null + if (declarationContainer != null) { + val descriptorName = original.name.asString() + val declarations = when { + original is ConstructorDescriptor && declarationContainer is KtClass -> declarationContainer.allConstructors + else -> declarationContainer.declarations.filter { it.name == descriptorName } } - - if (declarationContainer != null) { - val descriptorName = original.name.asString() - val singleOrNull = declarationContainer.declarations.singleOrNull { it.name == descriptorName } - if (singleOrNull != null) { - return singleOrNull + return declarations + .firstOrNull { declaration -> + if (original is CallableDescriptor) { + declaration is KtCallableDeclaration && isSameCallable(declaration, original) + } else declaration !is KtCallableDeclaration } - } } } - return file.getDeclaration(this, descriptorKey) ?: run { - return getBuiltinsDescriptorKey(descriptor)?.let { file.getDeclaration(this, it) } - } + error("Should not be reachable") } - fun getBuiltinsDescriptorKey(descriptor: DeclarationDescriptor): String? { - if (descriptor !is ClassDescriptor) return null + fun isSameCallable( + declaration: KtCallableDeclaration, + original: CallableDescriptor + ): Boolean { + if (!receiverTypesMatch(declaration.receiverTypeReference, original.extensionReceiverParameter)) return false - val classFqName = descriptor.fqNameUnsafe - if (!JvmBuiltInsSignatures.isSerializableInJava(classFqName)) return null + if (!returnTypesMatch(declaration, original)) return false + if (!typeParametersMatch(declaration, original)) return false - val builtInDescriptor = - DefaultBuiltIns.Instance.builtInsModule.resolveTopLevelClass(classFqName.toSafe(), NoLookupLocation.FROM_IDE) - return builtInDescriptor?.toStringKey() + if (!parametersMatch(declaration, original)) return false + return true + } + + private fun returnTypesMatch(declaration: KtCallableDeclaration, descriptor: CallableDescriptor): Boolean { + if (declaration is KtConstructor<*>) return true + return areTypesTheSame(descriptor.returnType!!, declaration.typeReference!!) + } + + private fun typeParametersMatch(declaration: KtCallableDeclaration, descriptor: CallableDescriptor): Boolean { + if (declaration.typeParameters.size != declaration.typeParameters.size) return false + val boundsByName = declaration.typeConstraints.groupBy { it.subjectTypeParameterName?.getReferencedName() } + descriptor.typeParameters.zip(declaration.typeParameters) { descriptorTypeParam, psiTypeParameter -> + if (descriptorTypeParam.name.toString() != psiTypeParameter.name) return false + val psiBounds = mutableListOf() + psiBounds.addIfNotNull(psiTypeParameter.extendsBound) + boundsByName[psiTypeParameter.name]?.forEach { + psiBounds.addIfNotNull(it.boundTypeReference) + } + val expectedBounds = descriptorTypeParam.upperBounds.filter { !it.isNullableAny() } + if (psiBounds.size != expectedBounds.size) return false + expectedBounds.zip(psiBounds) { expectedBound, candidateBound -> + if (!areTypesTheSame(expectedBound, candidateBound)) { + return false + } + } + } + return true + } + + private fun parametersMatch( + declaration: KtCallableDeclaration, + original: CallableDescriptor + ): Boolean { + if (declaration.valueParameters.size != original.valueParameters.size) { + return false + } + declaration.valueParameters.zip(original.valueParameters).forEach { (ktParam, paramDesc) -> + val isVarargs = ktParam.isVarArg + if (isVarargs != (paramDesc.varargElementType != null)) { + return false + } + if (!areTypesTheSame(if (isVarargs) paramDesc.varargElementType!! else paramDesc.type, ktParam.typeReference!!)) { + return false + } + } + return true + } + + private fun receiverTypesMatch( + ktTypeReference: KtTypeReference?, + receiverParameter: ReceiverParameterDescriptor?, + ): Boolean { + if (ktTypeReference != null) { + if (receiverParameter == null) return false + val receiverType = receiverParameter.type + if (!areTypesTheSame(receiverType, ktTypeReference)) { + return false + } + } else if (receiverParameter != null) return false + return true + } + + private fun areTypesTheSame( + kotlinType: KotlinType, + ktTypeReference: KtTypeReference + ): Boolean { + val qualifiedName = getQualifiedName( + ktTypeReference.typeElement, + ktTypeReference.getAllModifierLists().any { it.hasSuspendModifier() }) ?: return false + val declarationDescriptor = + ((kotlinType as? AbbreviatedType)?.abbreviation ?: kotlinType).constructor.declarationDescriptor ?: return false + if (declarationDescriptor is TypeParameterDescriptor) { + return declarationDescriptor.name.asString() == qualifiedName + } + return declarationDescriptor.fqNameSafe.asString() == qualifiedName } private fun DeclarationDescriptor.toStringKey(): String { @@ -118,3 +192,29 @@ object ByDescriptorIndexer : DecompiledTextIndexer { private val LOG = Logger.getInstance(this::class.java) } + +fun getQualifiedName(typeElement: KtTypeElement?, isSuspend: Boolean): String? { + val referencedName = when (typeElement) { + is KtUserType -> getQualifiedName(typeElement) + is KtFunctionType -> { + var parametersCount = typeElement.parameters.size + typeElement.receiverTypeReference?.let { parametersCount++ } + if (isSuspend) { + StandardNames.getSuspendFunctionClassId(parametersCount).asFqNameString() + } else { + StandardNames.getFunctionClassId(parametersCount).asFqNameString() + } + } + is KtNullableType -> getQualifiedName(typeElement.unwrapNullability(), isSuspend) + else -> null + } + return referencedName +} + +private fun getQualifiedName(userType: KtUserType): String? { + val qualifier = userType.qualifier ?: return userType.referencedName + return getQualifiedName(qualifier) + "." + userType.referencedName +} + +fun KtElementImplStub<*>.getAllModifierLists(): Array = + getStubOrPsiChildren(KtStubElementTypes.MODIFIER_LIST, KtStubElementTypes.MODIFIER_LIST.arrayFactory) diff --git a/analysis/decompiled/light-classes-for-decompiled-fe10/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/fe10/KotlinDeclarationInCompiledFileSearcherFE10Impl.kt b/analysis/decompiled/light-classes-for-decompiled-fe10/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/fe10/KotlinDeclarationInCompiledFileSearcherFE10Impl.kt index 192cc51832f..872f48ba57c 100644 --- a/analysis/decompiled/light-classes-for-decompiled-fe10/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/fe10/KotlinDeclarationInCompiledFileSearcherFE10Impl.kt +++ b/analysis/decompiled/light-classes-for-decompiled-fe10/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/fe10/KotlinDeclarationInCompiledFileSearcherFE10Impl.kt @@ -19,18 +19,8 @@ import org.jetbrains.kotlin.psi.KtDeclaration class KotlinDeclarationInCompiledFileSearcherFE10Impl : KotlinDeclarationInCompiledFileSearcher() { override fun findDeclarationInCompiledFile(file: KtClsFile, member: PsiMember, signature: MemberSignature): KtDeclaration? { val relativeClassName = member.relativeClassName() - val key = ClassNameAndSignature(relativeClassName, signature) - val memberName = member.name - if (memberName != null && !file.isContentsLoaded && file.hasDeclarationWithKey(BySignatureIndexer, key)) { - findByStubs(file, relativeClassName, member, memberName)?.let { return it } - } - - val declaration = file.getDeclaration(BySignatureIndexer, key) ?: return null - return if (member is PsiMethod && member.isConstructor && declaration is KtClassOrObject) { - declaration.primaryConstructor ?: declaration - } else { - declaration - } + val memberName = member.name ?: return null + return findByStubs(file, relativeClassName, member, memberName) } } diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/origin/KotlinDeclarationInCompiledFileSearcher.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/origin/KotlinDeclarationInCompiledFileSearcher.kt index 068aba89ff9..86b349d2722 100644 --- a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/origin/KotlinDeclarationInCompiledFileSearcher.kt +++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/origin/KotlinDeclarationInCompiledFileSearcher.kt @@ -6,15 +6,25 @@ package org.jetbrains.kotlin.analysis.decompiled.light.classes.origin import com.intellij.openapi.application.ApplicationManager -import com.intellij.psi.PsiField -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiMethod +import com.intellij.psi.* import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile -import org.jetbrains.kotlin.asJava.syntheticAccessors +import org.jetbrains.kotlin.analysis.decompiler.psi.text.getAllModifierLists +import org.jetbrains.kotlin.analysis.decompiler.psi.text.getQualifiedName +import org.jetbrains.kotlin.asJava.elements.psiType +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.constant.StringValue +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName +import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName import org.jetbrains.kotlin.load.kotlin.MemberSignature +import org.jetbrains.kotlin.name.JvmNames import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier +import org.jetbrains.kotlin.psi.stubs.impl.KotlinAnnotationEntryStubImpl import org.jetbrains.kotlin.type.MapPsiToAsmDesc +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs abstract class KotlinDeclarationInCompiledFileSearcher { @@ -49,26 +59,157 @@ abstract class KotlinDeclarationInCompiledFileSearcher { classOrFile else { relativeClassName.fold(classOrFile) { declaration: KtDeclarationContainer?, name: Name -> - declaration?.declarations?.singleOrNull() { it is KtClassOrObject && it.name == name.asString() } as? KtClassOrObject + declaration?.declarations?.singleOrNull { it is KtClassOrObject && it.name == name.asString() } as? KtClassOrObject } } ?: return null if (member is PsiMethod && member.isConstructor) { - return container.safeAs()?.takeIf { it.name == memberName }?.allConstructors?.singleOrNull() + return container.safeAs() + ?.takeIf { it.name == memberName } + ?.allConstructors + ?.firstOrNull { doParametersMatch(member, it) } } val declarations = container.declarations return when (member) { is PsiMethod -> { - val names = member.syntheticAccessors(withoutOverrideCheck = true).map(Name::asString) + memberName - declarations.singleOrNull { it.name in names } + val names = SmartList(memberName) + val setter = if (JvmAbi.isGetterName(memberName) && !PsiType.VOID.equals(member.returnType)) { + propertyNameByGetMethodName(Name.identifier(memberName))?.let { names.add(it.identifier) } + false + } else if (JvmAbi.isSetterName(memberName) && PsiType.VOID.equals(member.returnType)) { + propertyNamesBySetMethodName(Name.identifier(memberName)).forEach { names.add(it.identifier) } + true + } else true + declarations + .firstOrNull { declaration -> + nameMatches(declaration, names) && + (declaration is KtNamedFunction && doParametersMatch(member, declaration) || + declaration is KtProperty && doPropertyMatch(member, declaration, setter)) + } } - is PsiField -> declarations.singleOrNull { it !is KtNamedFunction && it.name == memberName } + is PsiField -> { + if (container is KtObjectDeclaration && memberName == "INSTANCE") { + return container + } + declarations.singleOrNull { it !is KtNamedFunction && it.name == memberName } + } else -> declarations.singleOrNull { it.name == memberName } } } + private fun nameMatches(declaration: KtDeclaration?, names: MutableList): Boolean { + if (getJvmName(declaration) in names) return true + return declaration is KtProperty && (getJvmName(declaration.getter) in names || getJvmName(declaration.setter) in names) + } + + private fun getJvmName(declaration: KtDeclaration?): String? { + if (declaration == null) return null + val annotationEntry = declaration.annotationEntries.firstOrNull { + it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == JvmNames.JVM_NAME_SHORT + } + if (annotationEntry != null) { + val constantValue = (annotationEntry.stub as? KotlinAnnotationEntryStubImpl)?.valueArguments?.get(Name.identifier("name")) + if (constantValue is StringValue) { + return constantValue.value + } + } + return declaration.name + } + + private fun doPropertyMatch(member: PsiMethod, property: KtProperty, setter: Boolean): Boolean { + val ktTypes = mutableListOf() + property.contextReceivers.forEach { ktTypes.add(it.typeReference()!!) } + property.receiverTypeReference?.let { ktTypes.add(it) } + property.typeReference?.let { ktTypes.add(it) } + + val psiTypes = mutableListOf() + member.parameterList.parameters.forEach { psiTypes.add(it.type) } + if (!setter) { + val returnType = member.returnType ?: return false + psiTypes.add(returnType) + } + + if (ktTypes.size != psiTypes.size) return false + ktTypes.zip(psiTypes).forEach { (ktType, psiType) -> + if (!areTypesTheSame(ktType, psiType, false)) return false + } + return true + } + + private fun doParametersMatch(member: PsiMethod, ktNamedFunction: KtFunction): Boolean { + if (!doTypeParameters(member, ktNamedFunction)) { + return false + } + val ktTypes = mutableListOf() + ktNamedFunction.contextReceivers.forEach { ktTypes.add(it.typeReference()!!) } + ktNamedFunction.receiverTypeReference?.let { ktTypes.add(it) } + val parametersCount = member.parameterList.parametersCount + val isJvmOverloads = ktNamedFunction.annotationEntries.any { + it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == + JvmNames.JVM_OVERLOADS_FQ_NAME.shortName().asString() + } + val firstDefaultParametersToPass = if (isJvmOverloads) { + val totalNumberOfParametersWithDefaultValues = ktNamedFunction.valueParameters.filter { it.hasDefaultValue() }.size + val numberOfSkippedParameters = ktNamedFunction.valueParameters.size + ktTypes.size - parametersCount + totalNumberOfParametersWithDefaultValues - numberOfSkippedParameters + } else 0 + var defaultParamIdx = 0 + for (valueParameter in ktNamedFunction.valueParameters) { + if (isJvmOverloads && valueParameter.hasDefaultValue()) { + if (defaultParamIdx >= firstDefaultParametersToPass) { + continue + } + defaultParamIdx++ + } + + ktTypes.add(valueParameter.typeReference!!) + } + if (parametersCount != ktTypes.size) return false + member.parameterList.parameters.map { it.type } + .zip(ktTypes) + .forEach { (psiType, ktTypeRef) -> + if (!areTypesTheSame(ktTypeRef, psiType, (ktTypeRef.parent as? KtParameter)?.isVarArg == true)) return false + } + return true + } + + private fun doTypeParameters(member: PsiMethod, ktNamedFunction: KtFunction): Boolean { + if (member.typeParameters.size != ktNamedFunction.typeParameters.size) return false + val boundsByName = ktNamedFunction.typeConstraints.groupBy { it.subjectTypeParameterName?.getReferencedName() } + member.typeParameters.zip(ktNamedFunction.typeParameters) { psiTypeParam, ktTypeParameter -> + if (psiTypeParam.name.toString() != ktTypeParameter.name) return false + val psiBounds = mutableListOf() + psiBounds.addIfNotNull(ktTypeParameter.extendsBound) + boundsByName[ktTypeParameter.name]?.forEach { + psiBounds.addIfNotNull(it.boundTypeReference) + } + val expectedBounds = psiTypeParam.extendsListTypes + if (psiBounds.size != expectedBounds.size) return false + expectedBounds.zip(psiBounds) { expectedBound, candidateBound -> + if (!areTypesTheSame(candidateBound, expectedBound, false)) { + return false + } + } + } + return true + } + + /** + * Compare erased types + */ + private fun areTypesTheSame(ktTypeRef: KtTypeReference, psiType: PsiType, varArgs: Boolean): Boolean { + val qualifiedName = + getQualifiedName(ktTypeRef.typeElement, ktTypeRef.getAllModifierLists().any { it.hasSuspendModifier() }) ?: return false + if (psiType is PsiArrayType && psiType.componentType !is PsiPrimitiveType) { + return qualifiedName == StandardNames.FqNames.array.asString() || + varArgs && areTypesTheSame(ktTypeRef, psiType.componentType, false) + } + //currently functional types are unresolved and thus type comparison doesn't work + return psiType.canonicalText.takeWhile { it != '<' } == psiType(qualifiedName, ktTypeRef).canonicalText + } + companion object { fun getInstance(): KotlinDeclarationInCompiledFileSearcher = ApplicationManager.getApplication().getService(KotlinDeclarationInCompiledFileSearcher::class.java) diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesStructureTestBase.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesStructureTestBase.kt index de90d0f8d55..4dd0882db27 100644 --- a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesStructureTestBase.kt +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesStructureTestBase.kt @@ -12,7 +12,9 @@ import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter import org.jetbrains.kotlin.asJava.toLightClass +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.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset @@ -101,9 +103,27 @@ open class AbstractSymbolLightClassesStructureTestBase( private fun wrongInheritorStructure(line: String): Nothing = error("Can't parse '$line' line correctly") protected fun PrettyPrinter.handleFile(ktFile: KtFile) { val text = ktFile.text - ktFile.forEachDescendantOfType { classOrObject -> - handleClassDeclaration(classOrObject, text) - appendLine() + if (ktFile.isCompiled) { + ktFile.declarations.forEach { classOrObject -> + if (classOrObject is KtClassOrObject) { + if (classOrObject is KtClass && classOrObject.isEnum()) { + classOrObject.declarations.forEach { declaration -> + if (declaration is KtEnumEntry) { + handleClassDeclaration(declaration, text) + appendLine() + } + } + } + + handleClassDeclaration(classOrObject, text) + appendLine() + } + } + } else { + ktFile.forEachDescendantOfType { classOrObject -> + handleClassDeclaration(classOrObject, text) + appendLine() + } } } diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtNamedFunction.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtNamedFunction.java index 333baf254fa..4c113c36e58 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtNamedFunction.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtNamedFunction.java @@ -125,13 +125,17 @@ public class KtNamedFunction extends KtTypeParameterListOwnerStub - findChildByClass(KtExpression.class) - ); + return findChildByClass(KtExpression.class); } @Nullable diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java index 1500acde1e0..ead122eef79 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java @@ -279,9 +279,7 @@ public class KtProperty extends KtTypeParameterListOwnerStub } } - return AstLoadingFilter.forceAllowTreeLoading(this.getContainingFile(), () -> - PsiTreeUtil.getNextSiblingOfType(findChildByType(EQ), KtExpression.class) - ); + return PsiTreeUtil.getNextSiblingOfType(findChildByType(EQ), KtExpression.class); } public boolean hasDelegateExpressionOrInitializer() { diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPropertyAccessor.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPropertyAccessor.java index 4787b4f7964..439bfdfb665 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPropertyAccessor.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPropertyAccessor.java @@ -89,13 +89,17 @@ public class KtPropertyAccessor extends KtDeclarationStub - findChildByClass(KtExpression.class) - ); + return findChildByClass(KtExpression.class); } @Nullable diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index 787f82d974c..6d65f5fb034 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -225,6 +225,7 @@ inline fun PsiElement.forEachDescendantOfType( crossinline canGoInside: (PsiElement) -> Boolean, noinline action: (T) -> Unit ) { + checkDecompiledText() this.accept(object : PsiRecursiveElementVisitor() { override fun visitElement(element: PsiElement) { if (canGoInside(element)) { @@ -257,6 +258,7 @@ inline fun PsiElement.findDescendantOfType( crossinline canGoInside: (PsiElement) -> Boolean, noinline predicate: (T) -> Boolean = { true } ): T? { + checkDecompiledText() var result: T? = null this.accept(object : PsiRecursiveElementWalkingVisitor() { override fun visitElement(element: PsiElement) { @@ -274,6 +276,13 @@ inline fun PsiElement.findDescendantOfType( return result } +fun PsiElement.checkDecompiledText() { + val file = containingFile + if (file is KtFile && file.isCompiled) { + error("Attempt to load decompiled text, please use stubs instead. Decompile process might be slow and should be avoided") + } +} + inline fun PsiElement.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List { return collectDescendantsOfType({ true }, predicate) }