KTIJ-27050 [Analysis API] Correctly handle type parameters in KtFirReferenceShortener

Make `FirShorteningContext` correctly return symbols for type
parameters, so they are not ignored when scopes are inspected

Add additional type of `PartialOrderOfScope` - `TypeParameter`, because
otherwise it would have been classified as `Unclassified`, and that
breaks scopes comparison

Add missing type parameters to the scope of class header in
`ContextCollector`, add testdata for that

There is a bug in the compiler with type parameters leaking to nested
classes headers (KT-61959). After it's fixed, the testData with
incorrect expected shortenings/scopes should be adjusted and fixed too

^KTIJ-27050 Fixed
This commit is contained in:
Roman Golyshev
2023-09-14 17:06:15 +02:00
committed by teamcity
parent e2019ceb95
commit a78d631b16
29 changed files with 560 additions and 12 deletions
@@ -51,11 +51,11 @@ import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.PackageResolutionResult
import org.jetbrains.kotlin.fir.resolve.transformers.resolveToPackageOrClass
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeParameterScope
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.scopes.getProperties
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
@@ -262,9 +262,9 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
private val firSession: FirSession
get() = firResolveSession.useSiteFirSession
class ClassifierCandidate(val scope: FirScope, val availableSymbol: AvailableSymbol<FirClassLikeSymbol<*>>)
class ClassifierCandidate(val scope: FirScope, val availableSymbol: AvailableSymbol<FirClassifierSymbol<*>>)
fun findFirstClassifierInScopesByName(positionScopes: List<FirScope>, targetClassName: Name): AvailableSymbol<FirClassLikeSymbol<*>>? =
fun findFirstClassifierInScopesByName(positionScopes: List<FirScope>, targetClassName: Name): AvailableSymbol<FirClassifierSymbol<*>>? =
positionScopes.firstNotNullOfOrNull { scope -> findFirstClassifierSymbolByName(scope, targetClassName) }
fun findClassifiersInScopesByName(scopes: List<FirScope>, targetClassName: Name): List<ClassifierCandidate> =
@@ -274,8 +274,8 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
ClassifierCandidate(scope, classifierSymbol)
}
private fun findFirstClassifierSymbolByName(scope: FirScope, targetClassName: Name): AvailableSymbol<FirClassLikeSymbol<*>>? {
val classifierSymbol = scope.findFirstClassifierByName(targetClassName) as? FirClassLikeSymbol<*> ?: return null
private fun findFirstClassifierSymbolByName(scope: FirScope, targetClassName: Name): AvailableSymbol<FirClassifierSymbol<*>>? {
val classifierSymbol = scope.findFirstClassifierByName(targetClassName) ?: return null
return AvailableSymbol(classifierSymbol, ImportKind.fromScope(scope))
}
@@ -560,6 +560,8 @@ private class ElementsToShortenCollector(
)
}
private val FirClassifierSymbol<*>.classIdIfExists: ClassId? get() = (this as? FirClassLikeSymbol<*>)?.classId
/**
* Returns true if the class symbol has a type parameter that is supposed to be provided for its parent class.
*
@@ -670,6 +672,7 @@ private class ElementsToShortenCollector(
Local(1),
ClassUseSite(2),
NestedClassifier(2),
TypeParameter(2),
ExplicitSimpleImporting(3),
PackageMember(4),
Unclassified(5),
@@ -681,6 +684,7 @@ private class ElementsToShortenCollector(
is FirLocalScope -> Local
is FirClassUseSiteMemberScope -> ClassUseSite
is FirNestedClassifierScope -> NestedClassifier
is FirTypeParameterScope -> TypeParameter
is FirNestedClassifierScopeWithSubstitution -> originalScope.toPartialOrder()
is FirExplicitSimpleImportingScope -> ExplicitSimpleImporting
is FirPackageMemberScope -> PackageMember
@@ -746,7 +750,7 @@ private class ElementsToShortenCollector(
val name = classId.shortClassName
val availableClassifiers = shorteningContext.findClassifiersInScopesByName(scopes, name)
val matchingAvailableSymbol = availableClassifiers.firstOrNull { it.availableSymbol.symbol.classId == classId }
val matchingAvailableSymbol = availableClassifiers.firstOrNull { it.availableSymbol.symbol.classIdIfExists == classId }
val scopeForClass = matchingAvailableSymbol?.scope ?: return false
if (availableClassifiers.map { it.scope }.hasScopeCloserThan(scopeForClass, element)) return false
@@ -803,7 +807,7 @@ private class ElementsToShortenCollector(
)
}
// The class with name `classId.shortClassName` happens to be the same class referenced by this qualified access.
availableClassifier.symbol.classId == classId -> {
availableClassifier.symbol.classIdIfExists == classId -> {
// Respect caller's request to use star import, if it's not already star-imported.
return when {
availableClassifier.importKind == ImportKind.EXPLICIT && importAllInParent -> {
@@ -838,7 +842,7 @@ private class ElementsToShortenCollector(
}
private fun importedClassifierOverwritesAvailableClassifier(
availableClassifier: AvailableSymbol<FirClassLikeSymbol<*>>,
availableClassifier: AvailableSymbol<FirClassifierSymbol<*>>,
importAllInParent: Boolean
): Boolean {
val importKindFromOption = if (importAllInParent) ImportKind.STAR else ImportKind.EXPLICIT
@@ -868,7 +872,7 @@ private class ElementsToShortenCollector(
val positionScopes = shorteningContext.findScopesAtPosition(expression, getNamesToImport(), contextProvider) ?: return
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, shortClassName) ?: return
when {
availableClassifier.symbol.classId == classToImport -> return
availableClassifier.symbol.classIdIfExists == classToImport -> return
importedClassifierOverwritesAvailableClassifier(availableClassifier, importAllInParent) -> {
importAffectsUsages = true
}
@@ -71,4 +71,14 @@ public class FirIdeNormalAnalysisScriptSourceModuleReferenceShortenerTestGenerat
}
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters")
@TestDataPath("$PROJECT_ROOT")
public class TypeParameters {
@Test
public void testAllFilesPresentInTypeParameters() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters"), Pattern.compile("^(.+)\\.kts$"), null, true);
}
}
}
@@ -755,4 +755,38 @@ public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated ext
}
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters")
@TestDataPath("$PROJECT_ROOT")
public class TypeParameters {
@Test
public void testAllFilesPresentInTypeParameters() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("functionTypeParameterVsType.kt")
public void testFunctionTypeParameterVsType() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters/functionTypeParameterVsType.kt");
}
@Test
@TestMetadata("typeParameterVsNestedType.kt")
public void testTypeParameterVsNestedType() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters/typeParameterVsNestedType.kt");
}
@Test
@TestMetadata("typeParameterVsType_conflict.kt")
public void testTypeParameterVsType_conflict() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters/typeParameterVsType_conflict.kt");
}
@Test
@TestMetadata("typeParameterVsType_noConflict.kt")
public void testTypeParameterVsType_noConflict() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParameters/typeParameterVsType_noConflict.kt");
}
}
}