[Analysis API] Shortener: don't add import if it affects usages
^KTIJ-24096 Fixed
This commit is contained in:
committed by
teamcity
parent
338c2433ac
commit
a96e2f37e7
+51
-12
@@ -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,
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// FILE: main.kt
|
||||
class SortedSet
|
||||
|
||||
fun test() {
|
||||
val a = <expr>java.util.SortedSet</expr>
|
||||
}
|
||||
|
||||
fun foo(set: SortedSet)
|
||||
+5
@@ -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:
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FILE: main.kt
|
||||
class SortedSet
|
||||
|
||||
fun test() {
|
||||
val a = <expr>java.util.SortedSet</expr>
|
||||
}
|
||||
+6
@@ -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:
|
||||
Reference in New Issue
Block a user