KTIJ-26057 [AA] Ignore implicit receivers scope when dealing with types in reference shortener

Implicit receivers generally do not affect the resolution of types.
However, they generate scopes which might contain undesirable
classifiers, which can confuse reference shortener.

Dropping all the implicit receivers when dealing with type references
allows completely avoid such undesirable scopes instead of filtering
them by `instanceof` checks.

Also, temporary move `hasTypeParameterFromParent` check higher to the
`findClassifierElementsToShorten`, because ATM we don't know how to
properly decide whether to shorten the fully-qualified inner types
with implicit parameters or not (see KTIJ-26072).

^KTIJ-26057 Fixed
This commit is contained in:
Roman Golyshev
2023-10-23 22:42:29 +02:00
committed by teamcity
parent 65ff684589
commit 44d48510b4
7 changed files with 71 additions and 26 deletions
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall
import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression
import org.jetbrains.kotlin.fir.java.scopes.JavaClassMembersEnhancementScope
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
@@ -320,15 +319,21 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
fun findScopesAtPosition(
position: KtElement,
newImports: Sequence<FqName>,
towerContextProvider: FirTowerContextProvider
towerContextProvider: FirTowerContextProvider,
withImplicitReceivers: Boolean = true,
): List<FirScope>? {
val towerDataContext = towerContextProvider.getClosestAvailableParentContext(position) ?: return null
val result = buildList {
addAll(towerDataContext.nonLocalTowerDataElements.flatMap {
val nonLocalScopes = towerDataContext.nonLocalTowerDataElements
.asSequence()
.filter { withImplicitReceivers || it.implicitReceiver == null }
.flatMap {
// We must use `it.getAvailableScopes()` instead of `it.scope` to check scopes of companion objects
// and context receivers as well.
it.getAvailableScopes()
})
}
val result = buildList {
addAll(nonLocalScopes)
addIfNotNull(createFakeImportingScope(newImports))
addAll(towerDataContext.localScopes)
}
@@ -552,8 +557,13 @@ private class ElementsToShortenCollector(
wholeClassQualifier: ClassId,
wholeQualifierElement: KtElement,
): ElementToShorten? {
val positionScopes: List<FirScope> =
shorteningContext.findScopesAtPosition(wholeQualifierElement, getNamesToImport(), towerContextProvider) ?: return null
val positionScopes = shorteningContext.findScopesAtPosition(
wholeQualifierElement,
getNamesToImport(),
towerContextProvider,
withImplicitReceivers = false,
) ?: return null
val allClassIds = wholeClassQualifier.outerClassesWithSelf
val allQualifiers = wholeQualifierElement.qualifiedElementsWithSelf
return findClassifierElementsToShorten(
@@ -731,26 +741,8 @@ private class ElementsToShortenCollector(
classId: ClassId,
element: KtElement,
classSymbol: FirClassLikeSymbol<*>,
positionScopes: List<FirScope>,
scopes: List<FirScope>,
): Boolean {
// If its parent has a type parameter, we cannot shorten it because it will lose its type parameter.
if (classSymbol.hasTypeParameterFromParent()) return false
/**
* Class use-site member scopes may contain classifiers which are not actually available without explicit import.
* And if the class is declared in Java, it can be represented with JavaClassMembersEnhancementScope.
*/
val scopes = positionScopes.filter {
when (it) {
// KTIJ-24684, KTIJ-24662
is FirClassUseSiteMemberScope -> false
// KTIJ-26785
is JavaClassMembersEnhancementScope -> false
else -> true
}
}
val name = classId.shortClassName
val availableClassifiers = shorteningContext.findClassifiersInScopesByName(scopes, name)
val matchingAvailableSymbol = availableClassifiers.firstOrNull { it.availableSymbol.symbol.classIdIfExists == classId }
@@ -788,6 +780,9 @@ private class ElementsToShortenCollector(
val option = classShortenOption(classSymbol)
if (option == ShortenOption.DO_NOT_SHORTEN) continue
// If its parent has a type parameter, we do not shorten it ATM because it will lose its type parameter. See KTIJ-26072
if (classSymbol.hasTypeParameterFromParent()) continue
if (shortenClassifierIfAlreadyImported(classId, element, classSymbol, positionScopes)) {
return createElementToShorten(element, null, false)
}
@@ -643,6 +643,18 @@ public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated ext
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/nestedClassFromSupertypes6_java.kt");
}
@Test
@TestMetadata("TypeWithGenericsAsExtensionReceiverType_innerType.kt")
public void testTypeWithGenericsAsExtensionReceiverType_innerType() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/TypeWithGenericsAsExtensionReceiverType_innerType.kt");
}
@Test
@TestMetadata("TypeWithGenericsAsExtensionReceiverType_nestedType.kt")
public void testTypeWithGenericsAsExtensionReceiverType_nestedType() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/TypeWithGenericsAsExtensionReceiverType_nestedType.kt");
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/classHeaderPositions")
@TestDataPath("$PROJECT_ROOT")
@@ -643,6 +643,18 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenera
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/nestedClassFromSupertypes6_java.kt");
}
@Test
@TestMetadata("TypeWithGenericsAsExtensionReceiverType_innerType.kt")
public void testTypeWithGenericsAsExtensionReceiverType_innerType() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/TypeWithGenericsAsExtensionReceiverType_innerType.kt");
}
@Test
@TestMetadata("TypeWithGenericsAsExtensionReceiverType_nestedType.kt")
public void testTypeWithGenericsAsExtensionReceiverType_nestedType() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/TypeWithGenericsAsExtensionReceiverType_nestedType.kt");
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClasses/classHeaderPositions")
@TestDataPath("$PROJECT_ROOT")
@@ -0,0 +1,7 @@
package test
class Outer<T> {
inner class Inner
}
fun <T> Outer<T>.test(p: <expr>Outer<T>.Inner</expr>) {}
@@ -0,0 +1,5 @@
Before shortening: Outer<T>.Inner
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,7 @@
package test
class Outer<T> {
class Nested
}
fun <T> Outer<T>.test(p: <expr>Outer.Nested</expr>) {}
@@ -0,0 +1,7 @@
Before shortening: Outer.Nested
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[type] Outer.Nested
with SHORTEN_AND_STAR_IMPORT:
[type] Outer.Nested