diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt index d2ae6dd1890..747e2d33835 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.analysis.api.fir +import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationInfo import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.expressions.arguments import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.references.FirErrorNamedReference @@ -94,3 +96,21 @@ internal fun FirAnnotation.toKtAnnotationInfo( isCallWithArguments = this is FirAnnotationCall && arguments.isNotEmpty(), index = index, ) + +/** + * Implicit dispatch receiver is present when an extension function declared in object + * is imported somewhere else and used without directly referencing the object instance + * itself: + * + * ```kt + * import Foo.bar + * + * object Foo { fun String.bar() {} } + * + * fun usage() { + * "hello".bar() // this call has implicit 'Foo' dispatch receiver + * } + * ``` + */ +internal val FirResolvedQualifier.isImplicitDispatchReceiver: Boolean + get() = source?.kind == KtFakeSourceElementKind.ImplicitReceiver diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt index f89aa67dddd..09bc84ccd63 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirImportOptimizer.kt @@ -5,11 +5,11 @@ package org.jetbrains.kotlin.analysis.api.fir.components -import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtRealSourceElementKind import org.jetbrains.kotlin.analysis.api.components.KtImportOptimizer import org.jetbrains.kotlin.analysis.api.components.KtImportOptimizerResult import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols +import org.jetbrains.kotlin.analysis.api.fir.isImplicitDispatchReceiver import org.jetbrains.kotlin.analysis.api.fir.utils.FirBodyReanalyzingVisitorVoid import org.jetbrains.kotlin.analysis.api.fir.utils.computeImportableName import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken @@ -274,7 +274,7 @@ internal class KtFirImportOptimizer( val dispatchReceiver = dispatchReceiver if ( dispatchReceiver !is FirResolvedQualifier || - dispatchReceiver.source?.kind != KtFakeSourceElementKind.ImplicitReceiver + !dispatchReceiver.isImplicitDispatchReceiver ) { return null } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt index 56be6e1e2c4..99900cc507c 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.api.components.ShortenCommand import org.jetbrains.kotlin.analysis.api.components.ShortenOption import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.analysis.api.fir.components.ElementsToShortenCollector.PartialOrderOfScope.Companion.toPartialOrder +import org.jetbrains.kotlin.analysis.api.fir.isImplicitDispatchReceiver import org.jetbrains.kotlin.analysis.api.fir.utils.FirBodyReanalyzingVisitorVoid import org.jetbrains.kotlin.analysis.api.fir.utils.computeImportableName import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken @@ -427,6 +428,8 @@ private class ElementsToShortenCollector( } private fun processTypeQualifier(resolvedQualifier: FirResolvedQualifier) { + if (resolvedQualifier.isImplicitDispatchReceiver) return + val wholeClassQualifier = resolvedQualifier.classId ?: return val qualifierPsi = resolvedQualifier.psi ?: return if (!qualifierPsi.textRange.intersects(selection)) return diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java index 053b21f1262..d6f2c82d7b8 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/references/FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java @@ -232,6 +232,12 @@ public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated ext runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/enumEntryInitUsesCompanion2.kt"); } + @Test + @TestMetadata("extensionFromObject.kt") + public void testExtensionFromObject() throws Exception { + runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.kt"); + } + @Test @TestMetadata("multipleImport.kt") public void testMultipleImport() throws Exception { diff --git a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java index 2b280e28590..e804b67317f 100644 --- a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java +++ b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/references/FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenerated.java @@ -232,6 +232,12 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenera runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/enumEntryInitUsesCompanion2.kt"); } + @Test + @TestMetadata("extensionFromObject.kt") + public void testExtensionFromObject() throws Exception { + runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.kt"); + } + @Test @TestMetadata("multipleImport.kt") public void testMultipleImport() throws Exception { diff --git a/analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.kt b/analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.kt new file mode 100644 index 00000000000..47202a4ef39 --- /dev/null +++ b/analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.kt @@ -0,0 +1,15 @@ +// FILE: main.kt +package test + +import dependency.Bar.extFun + +fun usage(str: String) { + str.extFun() +} + +// FILE: dependency.kt +package dependency + +object Bar { + fun String.extFun() {} +} diff --git a/analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.txt b/analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.txt new file mode 100644 index 00000000000..e8f84e7fc38 --- /dev/null +++ b/analysis/analysis-api/testData/components/referenceShortener/referenceShortener/extensionFromObject.txt @@ -0,0 +1,5 @@ +Before shortening: str.extFun() +with DO_NOT_SHORTEN: +with SHORTEN_IF_ALREADY_IMPORTED: +with SHORTEN_AND_IMPORT: +with SHORTEN_AND_STAR_IMPORT: