KTIJ-26103 [Analysis API] Ignore implicit dispatch receivers in Reference Shortener

Calls to extension functions imported from objects have implicit
dispatch receivers, but those receivers are not present in the code
and should not be analyzed by the shortener

^KTIJ-26103 Fixed
This commit is contained in:
Roman Golyshev
2023-07-03 15:57:49 +02:00
committed by teamcity
parent 8d2ac41454
commit c5ded1ba48
7 changed files with 57 additions and 2 deletions
@@ -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
@@ -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
}
@@ -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
@@ -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 {
@@ -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 {
@@ -0,0 +1,15 @@
// FILE: main.kt
package test
import dependency.Bar.extFun
fun usage(str: String) {
<expr>str.extFun()</expr>
}
// FILE: dependency.kt
package dependency
object Bar {
fun String.extFun() {}
}
@@ -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: