diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirDeserializedDeclarationSourceProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirDeserializedDeclarationSourceProvider.kt index 383a04ac647..887c90b7e7a 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirDeserializedDeclarationSourceProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirDeserializedDeclarationSourceProvider.kt @@ -12,10 +12,15 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.KtDeclarationAndFirDe import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData import org.jetbrains.kotlin.analysis.project.structure.KtBuiltinsModule import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.isData +import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass +import org.jetbrains.kotlin.fir.resolve.getContainingClass import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.unwrapFakeOverrides import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -25,6 +30,7 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.util.OperatorNameConventions //todo introduce LibraryModificationTracker based cache? internal object FirDeserializedDeclarationSourceProvider { @@ -40,6 +46,34 @@ internal object FirDeserializedDeclarationSourceProvider { } } + fun findClassPsiForGeneratedMembers(fir: FirElement, project: Project): PsiElement? { + if (fir !is FirCallableDeclaration) { + return null + } + val containingClass = fir.getContainingClass(fir.moduleData.session) ?: return null + if (isGeneratedMemberNotWrittenToMetadata(fir.symbol, containingClass)) { + return provideSourceForClass(containingClass, project) + } + return null + } + + private fun isGeneratedMemberNotWrittenToMetadata(symbol: FirCallableSymbol<*>, containingClass: FirRegularClass): Boolean { + return when { + containingClass.isEnumClass -> symbol.name in setOf( + StandardNames.ENUM_VALUES, + StandardNames.ENUM_VALUE_OF, + StandardNames.ENUM_ENTRIES, + ) + containingClass.isData -> symbol.name in setOf( + OperatorNameConventions.EQUALS, + OperatorNameConventions.TO_STRING, + StandardNames.HASHCODE_NAME, + StandardNames.DATA_CLASS_COPY, + ) + else -> false + } + } + private fun provideSourceForFunction( function: FirSimpleFunction, project: Project diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/psiUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/psiUtils.kt index 10d3cef99f3..3ecf8b3554b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/psiUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/psiUtils.kt @@ -51,6 +51,9 @@ fun FirElement.findPsi(session: FirSession): PsiElement? = * For data classes & enums generated members like `copy` `componentN`, `values` it will return corresponding enum/data class * Otherwise, behaves the same way as [findPsi] returns exact PSI declaration corresponding to passed [FirDeclaration] */ -fun FirDeclaration.findReferencePsi(): PsiElement? = - psi - ?: FirDeserializedDeclarationSourceProvider.findPsi(this, (moduleData.session as LLFirSession).project) +fun FirDeclaration.findReferencePsi(): PsiElement? { + psi?.let { return it } + val project = (moduleData.session as LLFirSession).project + return FirDeserializedDeclarationSourceProvider.findPsi(this, project) + ?: FirDeserializedDeclarationSourceProvider.findClassPsiForGeneratedMembers(this, project) +} 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 7784f468ea9..f962c68b484 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 @@ -52,6 +52,12 @@ object ByDescriptorIndexer : DecompiledTextIndexer { return classOrObject?.primaryConstructor ?: classOrObject } + if ((original as? CallableMemberDescriptor)?.mustNotBeWrittenToDecompiledText() == true && + original.containingDeclaration is ClassDescriptor + ) { + return getDeclarationForDescriptor(original.containingDeclaration, file) + } + val descriptorKey = original.toStringKey() if (!file.isContentsLoaded && original is MemberDescriptor) { diff --git a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/buildDecompiledText.kt b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/buildDecompiledText.kt index c80b75ab555..87ad3f2b002 100644 --- a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/buildDecompiledText.kt +++ b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/text/buildDecompiledText.kt @@ -34,6 +34,20 @@ fun DescriptorRendererOptions.defaultDecompilerRendererOptions() { parameterNamesInFunctionalTypes = false // to support parameters names in decompiled text we need to load annotation arguments } +internal fun CallableMemberDescriptor.mustNotBeWrittenToDecompiledText(): Boolean { + return when (kind) { + CallableMemberDescriptor.Kind.DECLARATION -> false + + CallableMemberDescriptor.Kind.FAKE_OVERRIDE, + CallableMemberDescriptor.Kind.DELEGATION -> true + + CallableMemberDescriptor.Kind.SYNTHESIZED -> { + // Of all synthesized functions, only `component*` functions are rendered (for historical reasons) + !DataClassDescriptorResolver.isComponentLike(name) + } + } +} + fun buildDecompiledText( packageFqName: FqName, descriptors: List, @@ -56,20 +70,6 @@ fun buildDecompiledText( textIndex.addToIndex(descriptor, TextRange(startOffset, endOffset)) } - fun CallableMemberDescriptor.isConsideredSynthetic(): Boolean { - return when (kind) { - CallableMemberDescriptor.Kind.DECLARATION -> false - - CallableMemberDescriptor.Kind.FAKE_OVERRIDE, - CallableMemberDescriptor.Kind.DELEGATION -> true - - CallableMemberDescriptor.Kind.SYNTHESIZED -> { - // Of all synthesized functions, only `component*` functions are rendered (for historical reasons) - !DataClassDescriptorResolver.isComponentLike(name) - } - } - } - fun appendDescriptor(descriptor: DeclarationDescriptor, indent: String, lastEnumEntry: Boolean? = null) { val startOffset = builder.length if (isEnumEntry(descriptor)) { @@ -144,7 +144,7 @@ fun buildDecompiledText( if (member == companionObject) { continue } - if (member is CallableMemberDescriptor && member.isConsideredSynthetic()) { + if (member is CallableMemberDescriptor && member.mustNotBeWrittenToDecompiledText()) { continue } newlineExceptFirst()