From 785778511be34badbd8bc45ac54f24cd1564c7c9 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Wed, 7 Jun 2023 22:51:52 +0200 Subject: [PATCH] [Analysis API] rework KDoc resolution 1. Optimize it by removing a tower data context collection (KT-59189) 2. Rework the behavior, so it simulates the tower resolve by returning the set of symbols only from the first tower level, which is not empty ^KT-59189 fixed --- .../fir/references/KDocReferenceResolver.kt | 139 ++++++++++++------ .../api/fir/references/KtFirKDocReference.kt | 6 +- .../ClassPrimaryConstructorValParameter.txt | 3 +- 3 files changed, 99 insertions(+), 49 deletions(-) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt index b73c5dc3c7e..9cb2cfd9194 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt @@ -6,55 +6,113 @@ package org.jetbrains.kotlin.analysis.api.fir.references import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.symbols.* +import org.jetbrains.kotlin.analysis.api.components.KtScopeContext +import org.jetbrains.kotlin.analysis.api.components.KtScopeKind +import org.jetbrains.kotlin.analysis.api.scopes.KtScope +import org.jetbrains.kotlin.analysis.api.symbols.KtDeclarationSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithMembers -import org.jetbrains.kotlin.name.CallableId -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty +import kotlin.reflect.KClass +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol internal object KDocReferenceResolver { context(KtAnalysisSession) - internal fun resolveKdocFqName(fqName: FqName, owner: KtDeclaration?): Collection = buildSet { - collectSymbolsByFullyQualifiedName(fqName) + internal fun resolveKdocFqName(fqName: FqName, contextElement: KtElement): Collection { + getSymbolsFromParentMemberScopes(fqName, contextElement).ifNotEmpty { return this } + val importScopeContext = contextElement.containingKtFile.getImportingScopeContext() + getSymbolsFromImportingScope(importScopeContext, fqName, KtScopeKind.ExplicitSimpleImportingScope::class).ifNotEmpty { return this } + getSymbolsFromPackageScope(fqName, contextElement).ifNotEmpty { return this } + getSymbolsFromImportingScope(importScopeContext, fqName, KtScopeKind.DefaultSimpleImportingScope::class).ifNotEmpty { return this } + getSymbolsFromImportingScope(importScopeContext, fqName, KtScopeKind.ExplicitStarImportingScope::class).ifNotEmpty { return this } + getSymbolsFromImportingScope(importScopeContext, fqName, KtScopeKind.DefaultStarImportingScope::class).ifNotEmpty { return this } + getNonImportedSymbolsByFullyQualifiedName(fqName).ifNotEmpty { return this } + return emptyList() + } - if (owner != null) { - collectSymbolsFromPositionScopes(owner, fqName) - val ownerSymbol = owner.getSymbol() - - val pathSegments = fqName.pathSegments() - if (pathSegments.size == 1) { - val singleSegment = pathSegments.single() - when (ownerSymbol) { - is KtConstructorSymbol -> { - collectConstructorParameterSymbols(ownerSymbol, singleSegment) - } - - is KtNamedClassOrObjectSymbol -> { - collectPrimaryConstructorParameterSymbols(ownerSymbol, singleSegment) - } - - else -> {} + context(KtAnalysisSession) + private fun getSymbolsFromDeclaration(name: Name, owner: KtDeclaration): List = buildList { + if (owner is KtNamedDeclaration) { + if (owner.nameAsName == name) { + add(owner.getSymbol()) + } + } + if (owner is KtTypeParameterListOwner) { + for (typeParameter in owner.typeParameters) { + if (typeParameter.nameAsName == name) { + add(typeParameter.getTypeParameterSymbol()) } } } + if (owner is KtCallableDeclaration) { + for (typeParameter in owner.valueParameters) { + if (typeParameter.nameAsName == name) { + add(typeParameter.getParameterSymbol()) + } + } + } + + if (owner is KtClassOrObject) { + owner.primaryConstructor?.let { addAll(getSymbolsFromDeclaration(name, it)) } + } } context(KtAnalysisSession) - private fun MutableCollection.collectPrimaryConstructorParameterSymbols(owner: KtNamedClassOrObjectSymbol, name: Name) { - val primaryConstructor = owner.getDeclaredMemberScope().getConstructors().firstOrNull { it.isPrimary } ?: return - collectConstructorParameterSymbols(primaryConstructor, name) + private fun getSymbolsFromParentMemberScopes(fqName: FqName, contextElement: KtElement): Collection { + for (ktDeclaration in contextElement.parentsOfType(withSelf = true)) { + if (fqName.pathSegments().size == 1) { + getSymbolsFromDeclaration(fqName.shortName(), ktDeclaration).ifNotEmpty { return this } + } + if (ktDeclaration is KtClassOrObject) { + val symbol = ktDeclaration.getClassOrObjectSymbol() ?: continue + + val scope = listOfNotNull( + symbol.getMemberScope(), + getCompanionObjectMemberScope(symbol), + ).asCompositeScope() + + val symbolsFromScope = getSymbolsFromMemberScope(fqName, scope) + if (symbolsFromScope.isNotEmpty()) return symbolsFromScope + } + } + return emptyList() } context(KtAnalysisSession) - private fun MutableCollection.collectSymbolsFromPositionScopes(owner: KtDeclaration, fqName: FqName) { - val initialScope = owner.containingKtFile.getScopeContextForPosition(owner).getCompositeScope() + private fun getCompanionObjectMemberScope(classSymbol: KtClassOrObjectSymbol): KtScope? { + val namedClassSymbol = classSymbol as? KtNamedClassOrObjectSymbol ?: return null + val companionSymbol = namedClassSymbol.companionObject ?: return null + return companionSymbol.getMemberScope() + } - val scope = fqName.pathSegments() + context(KtAnalysisSession) + private fun getSymbolsFromPackageScope(fqName: FqName, contextElement: KtElement): Collection { + val packageFqName = contextElement.containingKtFile.packageFqName + val packageSymbol = getPackageSymbolIfPackageExists(packageFqName) ?: return emptyList() + val packageScope = packageSymbol.getPackageScope() + return getSymbolsFromMemberScope(fqName, packageScope) + } + + context(KtAnalysisSession) + private fun getSymbolsFromImportingScope( + scopeContext: KtScopeContext, + fqName: FqName, + acceptScopeKind: KClass, + ): Collection { + val importingScope = scopeContext.getCompositeScope { acceptScopeKind.java.isAssignableFrom(it::class.java) } + return getSymbolsFromMemberScope(fqName, importingScope) + } + + context(KtAnalysisSession) + private fun getSymbolsFromMemberScope(fqName: FqName, scope: KtScope): Collection { + val finalScope = fqName.pathSegments() .dropLast(1) - .fold(initialScope) { currentScope, fqNamePart -> + .fold(scope) { currentScope, fqNamePart -> currentScope .getClassifierSymbols(fqNamePart) .mapNotNull { (it as? KtSymbolWithMembers)?.getDeclaredMemberScope() } @@ -62,22 +120,19 @@ internal object KDocReferenceResolver { .asCompositeScope() } - val shortName = fqName.shortName() - addAll(scope.getCallableSymbols(shortName)) - addAll(scope.getClassifierSymbols(shortName)) + return finalScope.getAllSymbolsFromScopeByShortName(fqName) } - context(KtAnalysisSession) - private fun MutableCollection.collectConstructorParameterSymbols(owner: KtFunctionLikeSymbol, name: Name) { - for (valueParameter in owner.valueParameters) { - if (valueParameter.name == name) { - add(valueParameter) - } + private fun KtScope.getAllSymbolsFromScopeByShortName(fqName: FqName): Collection { + val shortName = fqName.shortName() + return buildSet { + addAll(getCallableSymbols(shortName)) + addAll(getClassifierSymbols(shortName)) } } context(KtAnalysisSession) - private fun MutableCollection.collectSymbolsByFullyQualifiedName(fqName: FqName) { + private fun getNonImportedSymbolsByFullyQualifiedName(fqName: FqName): List = buildList { generateNameInterpretations(fqName).forEach { interpretation -> collectSymbolsByFqNameInterpretation(interpretation) } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirKDocReference.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirKDocReference.kt index cb527f19252..9f30746118a 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirKDocReference.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirKDocReference.kt @@ -6,18 +6,14 @@ package org.jetbrains.kotlin.analysis.api.fir.references import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol -import org.jetbrains.kotlin.analysis.utils.printer.parentOfType import org.jetbrains.kotlin.idea.references.KDocReference import org.jetbrains.kotlin.idea.references.KtFirReference import org.jetbrains.kotlin.kdoc.psi.impl.KDocName -import org.jetbrains.kotlin.psi.KtDeclaration internal class KtFirKDocReference(element: KDocName) : KDocReference(element), KtFirReference { override fun KtAnalysisSession.resolveToSymbols(): Collection { - val owner = element.parentOfType() - return KDocReferenceResolver.resolveKdocFqName(element.getQualifiedNameAsFqName(), owner) + return KDocReferenceResolver.resolveKdocFqName(element.getQualifiedNameAsFqName(), element) } } \ No newline at end of file diff --git a/analysis/analysis-api/testData/referenceResolve/kDoc/parameters/ClassPrimaryConstructorValParameter.txt b/analysis/analysis-api/testData/referenceResolve/kDoc/parameters/ClassPrimaryConstructorValParameter.txt index 489b68ad2d6..d346e9274fc 100644 --- a/analysis/analysis-api/testData/referenceResolve/kDoc/parameters/ClassPrimaryConstructorValParameter.txt +++ b/analysis/analysis-api/testData/referenceResolve/kDoc/parameters/ClassPrimaryConstructorValParameter.txt @@ -1,3 +1,2 @@ Resolved to: -0: (in C) val aa: kotlin.Int -1: aa: kotlin.Int \ No newline at end of file +0: aa: kotlin.Int \ No newline at end of file