From 0909b48f6731c8366b2dfa38078ede4cceb9cd5f Mon Sep 17 00:00:00 2001 From: Frederik Haselmeier Date: Fri, 11 Apr 2025 12:13:53 +0200 Subject: [PATCH] [kotlin] KTIJ-33746 use KotlinProjectStructureProvider to obtain implementing modules Also shares the code for checking if the expected declaration can be ignored with the FirCallableCompletionContributor (cherry picked from commit 8fcd7154c5e2965ca8f854f2ecf9dddd3fc03aad) (cherry picked from commit 16ad25550148d2211131212ce4e690ba1f350502) IJ-CR-160265 GitOrigin-RevId: 804064f1c723ade28c3e465b83b66df91c2e189a --- .../kotlin.base.analysis-api.utils.iml | 3 +- .../api/utils/KtSymbolFromIndexProvider.kt | 47 +++++++++++++++---- .../FirCallableCompletionContributor.kt | 13 +---- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/plugins/kotlin/base/analysis-api/analysis-api-utils/kotlin.base.analysis-api.utils.iml b/plugins/kotlin/base/analysis-api/analysis-api-utils/kotlin.base.analysis-api.utils.iml index 6c5433815f99..6f1a21de1126 100644 --- a/plugins/kotlin/base/analysis-api/analysis-api-utils/kotlin.base.analysis-api.utils.iml +++ b/plugins/kotlin/base/analysis-api/analysis-api-utils/kotlin.base.analysis-api.utils.iml @@ -30,6 +30,7 @@ + @@ -39,7 +40,5 @@ - - \ No newline at end of file diff --git a/plugins/kotlin/base/analysis-api/analysis-api-utils/src/org/jetbrains/kotlin/idea/base/analysis/api/utils/KtSymbolFromIndexProvider.kt b/plugins/kotlin/base/analysis-api/analysis-api-utils/src/org/jetbrains/kotlin/idea/base/analysis/api/utils/KtSymbolFromIndexProvider.kt index e52a64d8dd39..5b1f826c8b8e 100644 --- a/plugins/kotlin/base/analysis-api/analysis-api-utils/src/org/jetbrains/kotlin/idea/base/analysis/api/utils/KtSymbolFromIndexProvider.kt +++ b/plugins/kotlin/base/analysis-api/analysis-api-utils/src/org/jetbrains/kotlin/idea/base/analysis/api/utils/KtSymbolFromIndexProvider.kt @@ -11,15 +11,19 @@ import com.intellij.psi.PsiMethod import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.PsiShortNamesCache import com.intellij.util.Processor +import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaPlatformInterface import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.components.KaBuiltinTypes +import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinProjectStructureProvider +import org.jetbrains.kotlin.analysis.api.projectStructure.KaDanglingFileModule +import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule +import org.jetbrains.kotlin.analysis.api.projectStructure.KaSourceModule import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.types.* import org.jetbrains.kotlin.base.analysis.isExcludedFromAutoImport -import org.jetbrains.kotlin.idea.base.facet.implementingModules import org.jetbrains.kotlin.idea.base.psi.kotlinFqName -import org.jetbrains.kotlin.idea.base.util.module import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.isMultiPlatform @@ -46,7 +50,7 @@ class KtSymbolFromIndexProvider( return true } - if (isIgnoredExpectDeclaration(file)) { + if (isIgnoredExpectDeclaration()) { // filter out expect declarations outside of common modules return false } @@ -483,15 +487,42 @@ private fun MutableSet.createNamesProcessor( true } +/** + * Returns whether the module can declare expect declarations that could be implemented by an implementing module. + */ +private fun KaModule.canHaveExpectDeclarations(): Boolean { + if (targetPlatform.isMultiPlatform()) return true + + @OptIn(KaPlatformInterface::class) + val contextModule = (this as? KaDanglingFileModule)?.contextModule ?: this + // We return true in this case out of caution, because we do not know for sure. + if (contextModule !is KaSourceModule) return true + + // We can assume that only modules that have some implementing module can have expect declarations because otherwise + // they do not make sense. + return KotlinProjectStructureProvider.getInstance(project) + .getImplementingModules(contextModule) + .isNotEmpty() +} + /** * We ignore expect declarations within completion in leaf modules because they will already be filled by their (more relevant) * actual counterpart. */ context(KaSession) -private fun KtDeclaration.isIgnoredExpectDeclaration(contextFile: KtFile): Boolean { +@ApiStatus.Internal +fun KaDeclarationSymbol.isIgnoredExpectDeclaration(): Boolean { + if (!isExpect) return false + return !useSiteModule.canHaveExpectDeclarations() +} + + +/** + * See [KaDeclarationSymbol.isIgnoredExpectDeclaration]. + */ +context(KaSession) +@ApiStatus.Internal +fun KtDeclaration.isIgnoredExpectDeclaration(): Boolean { if (!isExpectDeclaration()) return false - // Modules that have multiple targetPlatforms might have expect declarations without actuals in libraries, so we cannot ignore them. - if (useSiteModule.targetPlatform.isMultiPlatform()) return false - // We can only safely ignore expect declarations if we are in a leaf module without implementing modules. - return contextFile.module?.implementingModules?.isEmpty() == true + return !useSiteModule.canHaveExpectDeclarations() } \ No newline at end of file diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirCallableCompletionContributor.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirCallableCompletionContributor.kt index e3d07c8648c7..33e09af98caf 100644 --- a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirCallableCompletionContributor.kt +++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirCallableCompletionContributor.kt @@ -22,13 +22,8 @@ import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.types.KaErrorType import org.jetbrains.kotlin.analysis.api.types.KaType import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability -import org.jetbrains.kotlin.idea.base.analysis.api.utils.collectReceiverTypesForExplicitReceiverExpression -import org.jetbrains.kotlin.idea.base.analysis.api.utils.isJavaSourceOrLibrary -import org.jetbrains.kotlin.idea.base.analysis.api.utils.isPossiblySubTypeOf -import org.jetbrains.kotlin.idea.base.analysis.api.utils.resolveToExpandedSymbol -import org.jetbrains.kotlin.idea.base.facet.implementingModules +import org.jetbrains.kotlin.idea.base.analysis.api.utils.* import org.jetbrains.kotlin.idea.base.psi.isInsideAnnotationEntryArgumentList -import org.jetbrains.kotlin.idea.base.util.module import org.jetbrains.kotlin.idea.codeinsight.utils.canBeUsedAsExtension import org.jetbrains.kotlin.idea.codeinsight.utils.isEnum import org.jetbrains.kotlin.idea.completion.KotlinFirCompletionParameters @@ -49,7 +44,6 @@ import org.jetbrains.kotlin.idea.util.positionContext.KotlinNameReferencePositio import org.jetbrains.kotlin.idea.util.positionContext.KotlinSimpleNameReferencePositionContext import org.jetbrains.kotlin.kdoc.psi.impl.KDocName import org.jetbrains.kotlin.name.StandardClassIds -import org.jetbrains.kotlin.platform.isMultiPlatform import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.nextSiblingOfSameType import org.jetbrains.kotlin.resolve.ArrayFqNames @@ -93,10 +87,7 @@ internal open class FirCallableCompletionContributor( ) context(KaSession) - protected open fun filter(symbol: KaCallableSymbol): Boolean = - targetPlatform.isMultiPlatform() - || !symbol.isExpect - || symbol.psi?.module?.implementingModules?.isNotEmpty() == true + protected open fun filter(symbol: KaCallableSymbol): Boolean = !symbol.isIgnoredExpectDeclaration() // todo replace with a sealed hierarchy; too many arguments protected data class CallableWithMetadataForCompletion(