[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
|
package org.jetbrains.kotlin.analysis.api.fir.components
|
||||||
|
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.SmartPsiElementPointer
|
import com.intellij.psi.SmartPsiElementPointer
|
||||||
import org.jetbrains.kotlin.analysis.api.components.KtReferenceShortener
|
import org.jetbrains.kotlin.analysis.api.components.KtReferenceShortener
|
||||||
@@ -146,8 +147,6 @@ private enum class ImportKind {
|
|||||||
|
|
||||||
infix fun hasHigherPriorityThan(that: ImportKind): Boolean = this < that
|
infix fun hasHigherPriorityThan(that: ImportKind): Boolean = this < that
|
||||||
|
|
||||||
val canBeOverwrittenByExplicitImport: Boolean get() = DEFAULT_EXPLICIT hasHigherPriorityThan this
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromScope(scope: FirScope): ImportKind {
|
fun fromScope(scope: FirScope): ImportKind {
|
||||||
return when (scope) {
|
return when (scope) {
|
||||||
@@ -662,6 +661,9 @@ private class ElementsToShortenCollector(
|
|||||||
}
|
}
|
||||||
if (option == ShortenOption.SHORTEN_IF_ALREADY_IMPORTED) continue
|
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.
|
// Find class with the same name that's already available in this file.
|
||||||
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, classId.shortClassName)
|
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, classId.shortClassName)
|
||||||
|
|
||||||
@@ -672,33 +674,70 @@ private class ElementsToShortenCollector(
|
|||||||
return createElementToShorten(
|
return createElementToShorten(
|
||||||
element,
|
element,
|
||||||
classId.asSingleFqName(),
|
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.
|
// The class with name `classId.shortClassName` happens to be the same class referenced by this qualified access.
|
||||||
availableClassifier.symbol == classId -> {
|
availableClassifier.symbol == classId -> {
|
||||||
// Respect caller's request to use star import, if it's not already star-imported.
|
// Respect caller's request to use star import, if it's not already star-imported.
|
||||||
return when {
|
return when {
|
||||||
availableClassifier.importKind == ImportKind.EXPLICIT && option == ShortenOption.SHORTEN_AND_STAR_IMPORT -> {
|
availableClassifier.importKind == ImportKind.EXPLICIT && importAllInParent -> {
|
||||||
createElementToShorten(element, classId.asSingleFqName(), true)
|
createElementToShorten(element, classId.asSingleFqName(), importAllInParent)
|
||||||
}
|
}
|
||||||
// Otherwise, just shorten it and don't alter import statements
|
// Otherwise, just shorten it and don't alter import statements
|
||||||
else -> createElementToShorten(element, null, false)
|
else -> createElementToShorten(element, null, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Allow using star import to overwrite members implicitly imported by default.
|
importedClassifierOverwritesAvailableClassifier(availableClassifier, importAllInParent) -> {
|
||||||
availableClassifier.importKind == ImportKind.DEFAULT_STAR && option == ShortenOption.SHORTEN_AND_STAR_IMPORT -> {
|
return createElementToShorten(element, classId.asSingleFqName(), importAllInParent)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return findFakePackageToShortenFn(allQualifiedElements.last())
|
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(
|
private fun resolveUnqualifiedAccess(
|
||||||
fullyQualifiedAccess: FirQualifiedAccessExpression,
|
fullyQualifiedAccess: FirQualifiedAccessExpression,
|
||||||
name: Name,
|
name: Name,
|
||||||
|
|||||||
+12
@@ -94,6 +94,18 @@ public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated ext
|
|||||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName.kt");
|
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
|
@Test
|
||||||
@TestMetadata("classesWithSameName2.kt")
|
@TestMetadata("classesWithSameName2.kt")
|
||||||
public void testClassesWithSameName2() throws Exception {
|
public void testClassesWithSameName2() throws Exception {
|
||||||
|
|||||||
+12
@@ -94,6 +94,18 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenera
|
|||||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName.kt");
|
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
|
@Test
|
||||||
@TestMetadata("classesWithSameName2.kt")
|
@TestMetadata("classesWithSameName2.kt")
|
||||||
public void testClassesWithSameName2() throws Exception {
|
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