[Analysis API] Shortener: don't add import if it affects usages

^KTIJ-24096 Fixed
This commit is contained in:
aleksandrina-streltsova
2023-05-03 10:31:12 +03:00
committed by teamcity
parent 338c2433ac
commit a96e2f37e7
7 changed files with 100 additions and 12 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.SmartPsiElementPointer
import org.jetbrains.kotlin.analysis.api.components.KtReferenceShortener
@@ -146,8 +147,6 @@ private enum class ImportKind {
infix fun hasHigherPriorityThan(that: ImportKind): Boolean = this < that
val canBeOverwrittenByExplicitImport: Boolean get() = DEFAULT_EXPLICIT hasHigherPriorityThan this
companion object {
fun fromScope(scope: FirScope): ImportKind {
return when (scope) {
@@ -662,6 +661,9 @@ private class ElementsToShortenCollector(
}
if (option == ShortenOption.SHORTEN_IF_ALREADY_IMPORTED) continue
val importAllInParent = option == ShortenOption.SHORTEN_AND_STAR_IMPORT
if (importAffectsUsagesOfClassesWithSameName(classId, element.containingKtFile, importAllInParent)) continue
// Find class with the same name that's already available in this file.
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, classId.shortClassName)
@@ -672,33 +674,70 @@ private class ElementsToShortenCollector(
return createElementToShorten(
element,
classId.asSingleFqName(),
option == ShortenOption.SHORTEN_AND_STAR_IMPORT
importAllInParent
)
}
// The class with name `classId.shortClassName` happens to be the same class referenced by this qualified access.
availableClassifier.symbol == classId -> {
// Respect caller's request to use star import, if it's not already star-imported.
return when {
availableClassifier.importKind == ImportKind.EXPLICIT && option == ShortenOption.SHORTEN_AND_STAR_IMPORT -> {
createElementToShorten(element, classId.asSingleFqName(), true)
availableClassifier.importKind == ImportKind.EXPLICIT && importAllInParent -> {
createElementToShorten(element, classId.asSingleFqName(), importAllInParent)
}
// Otherwise, just shorten it and don't alter import statements
else -> createElementToShorten(element, null, false)
}
}
// Allow using star import to overwrite members implicitly imported by default.
availableClassifier.importKind == ImportKind.DEFAULT_STAR && option == ShortenOption.SHORTEN_AND_STAR_IMPORT -> {
return createElementToShorten(element, classId.asSingleFqName(), true)
}
// Allow using explicit import to overwrite members star-imported or in package
availableClassifier.importKind.canBeOverwrittenByExplicitImport && option == ShortenOption.SHORTEN_AND_IMPORT -> {
return createElementToShorten(element, classId.asSingleFqName(), false)
importedClassifierOverwritesAvailableClassifier(availableClassifier, importAllInParent) -> {
return createElementToShorten(element, classId.asSingleFqName(), importAllInParent)
}
}
}
return findFakePackageToShortenFn(allQualifiedElements.last())
}
private fun importedClassifierOverwritesAvailableClassifier(
availableClassifier: AvailableSymbol<ClassId>,
importAllInParent: Boolean
): Boolean {
val importKindFromOption = if (importAllInParent) ImportKind.STAR else ImportKind.EXPLICIT
return importKindFromOption.hasHigherPriorityThan(availableClassifier.importKind)
}
private fun importAffectsUsagesOfClassesWithSameName(classToImport: ClassId, file: KtFile, importAllInParent: Boolean): Boolean {
var importAffectsUsages = false
file.accept(object : KtVisitorVoid() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitImportList(importList: KtImportList) {}
override fun visitPackageDirective(directive: KtPackageDirective) {}
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
if (importAffectsUsages) return
if (KtPsiUtil.isSelectorInQualified(expression)) return
val shortClassName = classToImport.shortClassName
if (expression.getReferencedNameAsName() != shortClassName) return
val contextProvider = LowLevelFirApiFacadeForResolveOnAir.getOnAirGetTowerContextProvider(firResolveSession, expression)
val positionScopes = shorteningContext.findScopesAtPosition(expression, getNamesToImport(), contextProvider) ?: return
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, shortClassName) ?: return
when {
availableClassifier.symbol == classToImport -> return
importedClassifierOverwritesAvailableClassifier(availableClassifier, importAllInParent) -> {
importAffectsUsages = true
}
}
}
})
return importAffectsUsages
}
private fun resolveUnqualifiedAccess(
fullyQualifiedAccess: FirQualifiedAccessExpression,
name: Name,
@@ -94,6 +94,18 @@ public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated ext
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName.kt");
}
@Test
@TestMetadata("classesWithSameName10.kt")
public void testClassesWithSameName10() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName10.kt");
}
@Test
@TestMetadata("classesWithSameName11.kt")
public void testClassesWithSameName11() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName11.kt");
}
@Test
@TestMetadata("classesWithSameName2.kt")
public void testClassesWithSameName2() throws Exception {
@@ -94,6 +94,18 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenera
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName.kt");
}
@Test
@TestMetadata("classesWithSameName10.kt")
public void testClassesWithSameName10() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName10.kt");
}
@Test
@TestMetadata("classesWithSameName11.kt")
public void testClassesWithSameName11() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName11.kt");
}
@Test
@TestMetadata("classesWithSameName2.kt")
public void testClassesWithSameName2() throws Exception {
@@ -0,0 +1,8 @@
// FILE: main.kt
class SortedSet
fun test() {
val a = <expr>java.util.SortedSet</expr>
}
fun foo(set: SortedSet)
@@ -0,0 +1,5 @@
Before shortening: java.util.SortedSet
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,6 @@
// FILE: main.kt
class SortedSet
fun test() {
val a = <expr>java.util.SortedSet</expr>
}
@@ -0,0 +1,6 @@
Before shortening: java.util.SortedSet
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[qualifier] java.util.SortedSet
with SHORTEN_AND_STAR_IMPORT: