Handle SHORTEN_IF_ALEADY_IMPORTED case of KtFirReferenceShortener
For the following example, when we run the reference shortener, it
drops `a.b.c` qualifier, because it matches "FOURTH".
```
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode() // FIRST
fun <E> E.foo() = hashCode() // SECOND
object Receiver {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode() // THIRD
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode() // FOURTH
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c // FIFTH
return <expr>a.b.c.foo(1, false, "bar")</expr>
}
}
```
As shown in the above example, when SHORTEN_IF_ALEADY_IMPORTED option is
given from a user, the reference shortener has to check whether it can
drop the qualifier without changing the referenced symbol and if it is
possible to do that without adding a new import directive, it deletes
the qualifier.
It needs two steps:
1. Collect all candidate symbols matching the signature e.g., function
arguments / type arguments
2. Determine whether the referenced symbol has the highest reference
priority when we drops the qualifier depending on scopes
This commit uses `AllCandidatesResolver(shorteningContext.analysisSession.useSiteSession).
getAllCandidates( .. fake FIR call/property-access ..)` for step1.
For step2, we use a heuristic based on scopes of candidates. If a
candidate symbol is under the same scope with the target expression, it
has a `FirLocalScope` which has the high priority. So when we have a
candidate under a `FirLocalScope` and the actual referenced symbol is
different from the candidate, we must avoid dropping its qualifier
because the shortening will change its semantics i.e., reference.
The order of scopes depending on their scope types is:
1. FirLocalScope
2. FirClassUseSiteMemberScope / FirNestedClassifierScope
3. FirExplicitSimpleImportingScope
4. FirPackageMemberScope
5. others
Note that for "others" the above rule can be wrong. Please update it if
you find other scopes that have a priority higher than the specified
scopes.
One of non-trivial parts is the priority among multiple
FirClassUseSiteMemberScope and FirNestedClassifierScope. They are
basically scopes for class declarations. We decide their priorities
based on the distance of class declaration from the target expression.
Note that we take a strict approach to reject all false positive. For
example, when we are not sure, we don't shorten it to avoid changing its
semantics.
TODO: One corner case is handling receivers. We have to update
```
private fun shortenIfAlreadyImported(
firQualifiedAccess: FirQualifiedAccess,
calledSymbol: FirCallableSymbol<*>,
expressionInScope: KtExpression,
): Boolean
```
The current implementation cannot handle the following example:
```
package foo
class Foo {
fun test() {
// It references FIRST. Removing `foo` lets it reference SECOND.
<caret>foo.myRun {
42
}
}
}
inline fun <R> myRun(block: () -> R): R = block() // FIRST
inline fun <T, R> T.myRun(block: T.() -> R): R = block() // SECOND
```
Tests related to TODO:
- analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver2.kt
- analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver3.kt
This commit is contained in:
+448
-36
@@ -5,23 +5,25 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.fir.components
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import org.jetbrains.kotlin.analysis.api.components.KtReferenceShortener
|
||||
import org.jetbrains.kotlin.analysis.api.components.ShortenCommand
|
||||
import org.jetbrains.kotlin.analysis.api.components.ShortenOption
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.fir.components.ElementsToShortenCollector.PartialOrderOfScope.Companion.toPartialOrder
|
||||
import org.jetbrains.kotlin.analysis.api.fir.utils.addImportToFile
|
||||
import org.jetbrains.kotlin.analysis.api.fir.utils.computeImportableName
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirOfType
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.AllCandidatesResolver
|
||||
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
@@ -29,14 +31,16 @@ import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildImport
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport
|
||||
import org.jetbrains.kotlin.fir.expressions.FirErrorResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
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.expressions.impl.FirNoReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnmatchedTypeArgumentsError
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
@@ -48,17 +52,14 @@ 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.*
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.parentOrNull
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
internal class KtFirReferenceShortener(
|
||||
@@ -77,7 +78,9 @@ internal class KtFirReferenceShortener(
|
||||
val declarationToVisit = file.findSmallestDeclarationContainingSelection(selection)
|
||||
?: file.withDeclarationsResolvedToBodyResolve()
|
||||
|
||||
val firDeclaration = declarationToVisit.getOrBuildFirOfType<FirDeclaration>(firResolveSession)
|
||||
val firDeclaration = declarationToVisit.getOrBuildFir(firResolveSession) as? FirDeclaration ?: return ShortenCommandImpl(
|
||||
file.createSmartPointer(), emptyList(), emptyList(), emptyList(), emptyList(),
|
||||
)
|
||||
|
||||
val towerContext =
|
||||
LowLevelFirApiFacadeForResolveOnAir.onAirGetTowerContextProvider(firResolveSession, declarationToVisit)
|
||||
@@ -169,6 +172,8 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
|
||||
private val firSession: FirSession
|
||||
get() = firResolveSession.useSiteFirSession
|
||||
|
||||
class ClassifierCandidate(val scope: FirScope, val availableSymbol: AvailableSymbol<ClassId>)
|
||||
|
||||
fun findFirstClassifierInScopesByName(positionScopes: List<FirScope>, targetClassName: Name): AvailableSymbol<ClassId>? {
|
||||
for (scope in positionScopes) {
|
||||
val classifierSymbol = scope.findFirstClassifierByName(targetClassName) ?: continue
|
||||
@@ -180,11 +185,26 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
|
||||
return null
|
||||
}
|
||||
|
||||
fun findFunctionsInScopes(scopes: List<FirScope>, name: Name): List<AvailableSymbol<FirNamedFunctionSymbol>> {
|
||||
fun findClassifiersInScopesByName(scopes: List<FirScope>, targetClassName: Name): List<ClassifierCandidate> =
|
||||
scopes.mapNotNull { scope ->
|
||||
val classifierSymbol = scope.findFirstClassifierByName(targetClassName) ?: return@mapNotNull null
|
||||
val classifierLookupTag = classifierSymbol.toLookupTag() as? ConeClassLikeLookupTag ?: return@mapNotNull null
|
||||
|
||||
ClassifierCandidate(scope, AvailableSymbol(classifierLookupTag.classId, ImportKind.fromScope(scope)))
|
||||
}
|
||||
|
||||
fun findFunctionsInScopes(scopes: List<FirScope>, name: Name): List<AvailableSymbol<FirFunctionSymbol<*>>> {
|
||||
return scopes.flatMap { scope ->
|
||||
val importKind = ImportKind.fromScope(scope)
|
||||
scope.getFunctions(name).map {
|
||||
AvailableSymbol(it, importKind)
|
||||
buildList {
|
||||
// Collect constructors
|
||||
scope.findFirstClassifierByName(name)?.let { classifierSymbol ->
|
||||
val classSymbol = classifierSymbol as? FirClassSymbol ?: return@let null
|
||||
classSymbol.declarationSymbols.filterIsInstance<FirConstructorSymbol>()
|
||||
}?.forEach { add(AvailableSymbol(it, importKind)) }
|
||||
|
||||
// Collect functions
|
||||
addAll(scope.getFunctions(name).map { AvailableSymbol(it, importKind) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +237,10 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
|
||||
): List<FirScope>? {
|
||||
val towerDataContext = towerContextProvider.getClosestAvailableParentContext(position) ?: return null
|
||||
val result = buildList {
|
||||
addAll(towerDataContext.nonLocalTowerDataElements.mapNotNull { it.scope })
|
||||
addAll(towerDataContext.nonLocalTowerDataElements.mapNotNull {
|
||||
// We must use `it.getAvailableScope()` instead of `it.scope` to check scopes of companion objects as well.
|
||||
it.getAvailableScope()
|
||||
})
|
||||
addIfNotNull(createFakeImportingScope(newImports))
|
||||
addAll(towerDataContext.localScopes)
|
||||
}
|
||||
@@ -418,18 +441,210 @@ private class ElementsToShortenCollector(
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun <E> findClassifierElementsToShorten(
|
||||
/**
|
||||
* Returns true if the class symbol has a type parameter that is supposed to be provided for its parent class.
|
||||
*
|
||||
* Example:
|
||||
* class Outer<T> {
|
||||
* inner class Inner // Inner has an implicit type parameter `T`.
|
||||
* }
|
||||
*/
|
||||
private fun FirClassLikeSymbol<*>.hasTypeParameterFromParent(): Boolean = typeParameterSymbols.orEmpty().any {
|
||||
it.containingDeclarationSymbol != this
|
||||
}
|
||||
|
||||
private fun FirScope.correspondingClassIdIfExists(): ClassId = when (this) {
|
||||
is FirNestedClassifierScope -> klass.classId
|
||||
is FirClassUseSiteMemberScope -> classId
|
||||
else -> error("FirScope `$this` is expected to be one of FirNestedClassifierScope and FirClassUseSiteMemberScope to get ClassId")
|
||||
}
|
||||
|
||||
private fun ClassId.idWithoutCompanion() = if (shortClassName == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) outerClassId else this
|
||||
|
||||
private fun FirScope.isScopeForClass() = this is FirNestedClassifierScope || this is FirClassUseSiteMemberScope
|
||||
|
||||
/**
|
||||
* Assuming that both this [FirScope] and [another] are [FirNestedClassifierScope] or [FirClassUseSiteMemberScope] and both of them
|
||||
* are surrounding [from], returns whether this [FirScope] is closer than [another] based on the distance from [from].
|
||||
*
|
||||
* If one of this [FirScope] and [another] is not [FirNestedClassifierScope] or [FirClassUseSiteMemberScope], it returns false.
|
||||
*
|
||||
* Example:
|
||||
* class Outer { // scope1 ClassId = Other
|
||||
* class Inner { // scope2 ClassId = Other.Inner
|
||||
* fun foo() {
|
||||
* // Distance to scopes for classes from <element> in the order from the closest:
|
||||
* // scope2 -> scope3 -> scope1
|
||||
* <element>
|
||||
* }
|
||||
* companion object { // scope3 ClassId = Other.Inner.Companion
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* This function determines the distance based on [ClassId].
|
||||
*/
|
||||
private fun FirScope.isScopeForClassCloserThanAnotherScopeForClass(another: FirScope, from: KtClass): Boolean {
|
||||
// Make sure both are scopes for classes
|
||||
if (!isScopeForClass() || !another.isScopeForClass()) return false
|
||||
|
||||
if (this == another) return false
|
||||
|
||||
val classId = correspondingClassIdIfExists()
|
||||
val classIdOfAnother = another.correspondingClassIdIfExists()
|
||||
if (classId == classIdOfAnother) return false
|
||||
|
||||
// Find the first ClassId matching inner class. If the first matching one is this scope's ClassId, it means this scope is closer
|
||||
// than `another`.
|
||||
val candidates = setOfNotNull(classId, classIdOfAnother, classId.idWithoutCompanion(), classIdOfAnother.idWithoutCompanion())
|
||||
val closestClassId = findMostInnerClassMatchingId(from, candidates)
|
||||
return closestClassId == classId || (closestClassId != classIdOfAnother && closestClassId == classId.idWithoutCompanion())
|
||||
}
|
||||
|
||||
/**
|
||||
* Travels all containing classes of [innerClass] and finds the one matching ClassId with one of [candidates]. Returns the matching
|
||||
* ClassId. If it does not have a matching ClassId, it returns null.
|
||||
*/
|
||||
private fun findMostInnerClassMatchingId(innerClass: KtClass, candidates: Set<ClassId>): ClassId? {
|
||||
var classInNestedClass: KtClass? = innerClass
|
||||
while (classInNestedClass != null) {
|
||||
val containingClassId = classInNestedClass.getClassId()
|
||||
if (containingClassId in candidates) return containingClassId
|
||||
classInNestedClass = classInNestedClass.containingClass()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An enum class to specify the distance of scopes from an element as a partial order
|
||||
*
|
||||
* Example:
|
||||
* import ... // scope1 FirExplicitSimpleImportingScope - enum entry: ExplicitSimpleImporting
|
||||
* // scope2 FirPackageMemberScope - enum entry: PackageMember
|
||||
* class Outer { // scope3 FirClassUseSiteMemberScope - enum entry: ClassUseSite
|
||||
* class Inner { // scope4 FirClassUseSiteMemberScope or FirNestedClassifierScope - enum entry: ClassUseSite/NestedClassifier
|
||||
* fun foo() { // scope5 FirLocalScope - enum entry: Local
|
||||
*
|
||||
* // Distance to scopes from <element> in the order from the closest:
|
||||
* // scope5 -> scope4 -> scope6 -> scope3 -> scope1 -> scope2
|
||||
* <element>
|
||||
*
|
||||
* }
|
||||
* companion object {
|
||||
* // scope6 FirClassUseSiteMemberScope or FirNestedClassifierScope - enum entry: ClassUseSite/NestedClassifier
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
private enum class PartialOrderOfScope(
|
||||
val scopeDistanceLevel: Int // Note: Don't use the built-in ordinal since there are some scopes that are at the same level.
|
||||
) {
|
||||
Local(1),
|
||||
ClassUseSite(2),
|
||||
NestedClassifier(2),
|
||||
ExplicitSimpleImporting(3),
|
||||
PackageMember(4),
|
||||
Unclassified(5),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun FirScope.toPartialOrder(): PartialOrderOfScope {
|
||||
return when (this) {
|
||||
is FirLocalScope -> Local
|
||||
is FirClassUseSiteMemberScope -> ClassUseSite
|
||||
is FirNestedClassifierScope -> NestedClassifier
|
||||
is FirExplicitSimpleImportingScope -> ExplicitSimpleImporting
|
||||
is FirPackageMemberScope -> PackageMember
|
||||
else -> Unclassified
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this [FirScope] is a scope wider than [another] based on the above [PartialOrderOfScope] or not.
|
||||
*/
|
||||
private fun FirScope.isWiderThan(another: FirScope): Boolean =
|
||||
toPartialOrder().scopeDistanceLevel > another.toPartialOrder().scopeDistanceLevel
|
||||
|
||||
/**
|
||||
* Assuming that all scopes in this List<FirScope> and [base] are surrounding [from], returns whether an element of
|
||||
* this List<FirScope> is closer than [base] based on the distance from [from].
|
||||
*/
|
||||
private fun List<FirScope>.hasScopeCloserThan(base: FirScope, from: KtElement) = any { scope ->
|
||||
if (scope.isScopeForClass() && base.isScopeForClass()) {
|
||||
val classContainingFrom = from.containingClass() ?: return@any false
|
||||
return@any scope.isScopeForClassCloserThanAnotherScopeForClass(base, classContainingFrom)
|
||||
}
|
||||
base.isWiderThan(scope)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this [PsiFile] has a [KtImportDirective] whose imported FqName is the same as [classId] but references a different
|
||||
* symbol.
|
||||
*/
|
||||
private fun PsiFile.hasImportDirectiveForDifferentSymbolWithSameName(classId: ClassId): Boolean {
|
||||
val importDirectivesWithSameImportedFqName = collectDescendantsOfType { importedDirective: KtImportDirective ->
|
||||
importedDirective.importedFqName?.shortName() == classId.shortClassName
|
||||
}
|
||||
return importDirectivesWithSameImportedFqName.isNotEmpty() &&
|
||||
importDirectivesWithSameImportedFqName.all { it.importedFqName != classId.asSingleFqName() }
|
||||
}
|
||||
|
||||
private fun shortenClassifierIfAlreadyImported(
|
||||
classId: ClassId,
|
||||
element: KtElement,
|
||||
classSymbol: FirClassLikeSymbol<*>,
|
||||
positionScopes: 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
|
||||
|
||||
val name = classId.shortClassName
|
||||
val availableClassifiers = shorteningContext.findClassifiersInScopesByName(positionScopes, name)
|
||||
val matchingAvailableSymbol = availableClassifiers.firstOrNull { it.availableSymbol.symbol == classId }
|
||||
val scopeForClass = matchingAvailableSymbol?.scope ?: return false
|
||||
|
||||
if (availableClassifiers.map { it.scope }.hasScopeCloserThan(scopeForClass, element)) return false
|
||||
|
||||
/**
|
||||
* If we have a property with the same name, avoid dropping qualifiers makes it reference a property with the same name e.g.,
|
||||
* package my.component
|
||||
* class foo { .. } // A
|
||||
* ..
|
||||
* fun test() {
|
||||
* val foo = .. // B
|
||||
* my.component.foo::class.java // If we drop `my.component`, it will reference `B` instead of `A`
|
||||
* }
|
||||
*/
|
||||
if (shorteningContext.findPropertiesInScopes(positionScopes, name).isNotEmpty()) {
|
||||
val firForElement = element.getOrBuildFir(firResolveSession) as? FirQualifiedAccessExpression
|
||||
val typeArguments = firForElement?.typeArguments ?: emptyList()
|
||||
val qualifiedAccessCandidates = findCandidatesForPropertyAccess(classSymbol.annotations, typeArguments, name, element)
|
||||
if (qualifiedAccessCandidates.mapNotNull { it.candidate.originScope }.hasScopeCloserThan(scopeForClass, element)) return false
|
||||
}
|
||||
|
||||
return !element.containingFile.hasImportDirectiveForDifferentSymbolWithSameName(classId)
|
||||
}
|
||||
|
||||
private inline fun <E : KtElement> findClassifierElementsToShorten(
|
||||
positionScopes: List<FirScope>,
|
||||
allClassIds: Sequence<ClassId>,
|
||||
allQualifiedElements: Sequence<E>,
|
||||
createElementToShorten: (E, nameToImport: FqName?, importAllInParent: Boolean) -> ElementToShorten,
|
||||
findFakePackageToShortenFn: (E) -> ElementToShorten?,
|
||||
): ElementToShorten? {
|
||||
|
||||
for ((classId, element) in allClassIds.zip(allQualifiedElements)) {
|
||||
val option = classShortenOption(shorteningContext.toClassSymbol(classId) ?: return null)
|
||||
val classSymbol = shorteningContext.toClassSymbol(classId) ?: return null
|
||||
val option = classShortenOption(classSymbol)
|
||||
if (option == ShortenOption.DO_NOT_SHORTEN) continue
|
||||
|
||||
if (shortenClassifierIfAlreadyImported(classId, element, classSymbol, positionScopes)) {
|
||||
return createElementToShorten(element, null, false)
|
||||
}
|
||||
if (option == ShortenOption.SHORTEN_IF_ALREADY_IMPORTED) continue
|
||||
|
||||
// Find class with the same name that's already available in this file.
|
||||
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, classId.shortClassName)
|
||||
|
||||
@@ -469,13 +684,193 @@ private class ElementsToShortenCollector(
|
||||
return findFakePackageToShortenFn(allQualifiedElements.last())
|
||||
}
|
||||
|
||||
private fun resolveUnqualifiedAccess(
|
||||
fullyQualifiedAccess: FirQualifiedAccessExpression,
|
||||
name: Name,
|
||||
expressionInScope: KtExpression,
|
||||
): List<OverloadCandidate> {
|
||||
val fakeCalleeReference = buildSimpleNamedReference { this.name = name }
|
||||
val functionCall = fullyQualifiedAccess as? FirFunctionCall
|
||||
val fakeFirQualifiedAccess: FirQualifiedAccessExpression?
|
||||
if (functionCall == null) {
|
||||
fakeFirQualifiedAccess = buildPropertyAccessExpression {
|
||||
annotations.addAll(fullyQualifiedAccess.annotations)
|
||||
typeArguments.addAll(fullyQualifiedAccess.typeArguments)
|
||||
calleeReference = fakeCalleeReference
|
||||
}
|
||||
} else {
|
||||
val callExpression = expressionInScope as? KtCallExpression
|
||||
fakeFirQualifiedAccess = buildFunctionCall {
|
||||
annotations.addAll(functionCall.annotations)
|
||||
|
||||
/**
|
||||
* It is important to avoid passing type arguments when they are implicit type arguments.
|
||||
* For example,
|
||||
* package a.b.c
|
||||
* fun <T, E> foo(a: T, b: E) {} // A
|
||||
* fun test() {
|
||||
* fun foo(a: Int, b: String) {} // B
|
||||
* a.b.c.foo(3, "test")
|
||||
* }
|
||||
*
|
||||
* In the above code, we must prevent it from shortening `a.b.c.foo(3, "test")`. However, if we explicitly pass the type
|
||||
* arguments to the resolver, the resolver considers it has some explicit type arguments. Therefore, it reports that the
|
||||
* only function matching the signature is A, because B does not have type parameters. However, actually B also matches the
|
||||
* signature, and we must avoid dropping a.b.c from the call expression.
|
||||
*/
|
||||
if (callExpression?.typeArguments?.isNotEmpty() == true) typeArguments.addAll(functionCall.typeArguments)
|
||||
|
||||
argumentList = functionCall.argumentList
|
||||
calleeReference = fakeCalleeReference
|
||||
}
|
||||
}
|
||||
val candidates = AllCandidatesResolver(shorteningContext.analysisSession.useSiteSession).getAllCandidates(
|
||||
firResolveSession, fakeFirQualifiedAccess, name, expressionInScope
|
||||
)
|
||||
return candidates.filter { overloadCandidate ->
|
||||
overloadCandidate.candidate.currentApplicability == CandidateApplicability.RESOLVED
|
||||
}
|
||||
}
|
||||
|
||||
private fun findCandidatesForPropertyAccess(
|
||||
annotations: List<FirAnnotation>,
|
||||
typeArguments: List<FirTypeProjection>,
|
||||
name: Name,
|
||||
elementInScope: KtElement,
|
||||
): List<OverloadCandidate> {
|
||||
val fakeCalleeReference = buildSimpleNamedReference { this.name = name }
|
||||
val fakeFirQualifiedAccess = buildPropertyAccessExpression {
|
||||
this.annotations.addAll(annotations)
|
||||
this.typeArguments.addAll(typeArguments)
|
||||
calleeReference = fakeCalleeReference
|
||||
}
|
||||
val candidates = AllCandidatesResolver(shorteningContext.analysisSession.useSiteSession).getAllCandidates(
|
||||
firResolveSession, fakeFirQualifiedAccess, name, elementInScope
|
||||
)
|
||||
return candidates.filter { overloadCandidate ->
|
||||
overloadCandidate.candidate.currentApplicability == CandidateApplicability.RESOLVED
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<OverloadCandidate>.findScopeForSymbol(symbol: FirBasedSymbol<*>): FirScope? = firstOrNull {
|
||||
it.candidate.symbol == symbol
|
||||
}?.candidate?.originScope
|
||||
|
||||
/**
|
||||
* Returns whether a member of companion is used to initialize the enum entry or not. For example,
|
||||
* enum class C(val i: Int) {
|
||||
* ONE(<expr>C.K</expr>) // C.ONE uses C.K for initialization
|
||||
* ;
|
||||
* companion object {
|
||||
* const val K = 1
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
private fun KtExpression.isCompanionMemberUsedForEnumEntryInit(resolvedSymbol: FirCallableSymbol<*>): Boolean {
|
||||
val enumEntry = getNonStrictParentOfType<KtEnumEntry>() ?: return false
|
||||
val firEnumEntry = enumEntry.resolveToFirSymbol(firResolveSession) as? FirEnumEntrySymbol ?: return false
|
||||
val classNameOfResolvedSymbol = resolvedSymbol.callableId.className ?: return false
|
||||
return firEnumEntry.callableId.className == classNameOfResolvedSymbol.parent() &&
|
||||
classNameOfResolvedSymbol.shortName() == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether it is fine to shorten [firQualifiedAccess] or not.
|
||||
*
|
||||
* @param firQualifiedAccess FIR for the shortening target expression
|
||||
* @param calledSymbol The symbol referenced by the qualified access expression
|
||||
* @param expressionInScope An expression under the same scope as the shortening target expression
|
||||
*
|
||||
* The decision has two steps:
|
||||
* 1. Collect all candidates matching [firQualifiedAccess]
|
||||
* - We use `AllCandidatesResolver(shorteningContext.analysisSession.useSiteSession).getAllCandidates( .. fake FIR .. )`. See
|
||||
* [resolveUnqualifiedAccess] above.
|
||||
* 2. Check whether the candidate with the highest priority based on the distance to the scope from [expressionInScope] is the same
|
||||
* as [calledSymbol] ot not
|
||||
* - We use [hasScopeCloserThan] to determine the distance to the scope
|
||||
*/
|
||||
private fun shortenIfAlreadyImported(
|
||||
firQualifiedAccess: FirQualifiedAccessExpression,
|
||||
calledSymbol: FirCallableSymbol<*>,
|
||||
expressionInScope: KtExpression,
|
||||
): Boolean {
|
||||
/**
|
||||
* Avoid shortening reference to enum companion used in enum entry initialization there is no guarantee that the companion object
|
||||
* was initialized in advance.
|
||||
* For example, When we shorten the following code:
|
||||
* enum class C(val i: Int) {
|
||||
* ONE(<expr>C.K</expr>) // shorten C.K to K
|
||||
* ;
|
||||
* companion object {
|
||||
* const val K = 1
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* the compiler reports "Variable 'K' must be initialized". This happens because there is no guarantee that the companion object
|
||||
* was initialized at the time when we use `C.K` for the enum entry `ONE`. To avoid this type of compiler errors, we don't shorten
|
||||
* the reference if it is a part of the enum companion object, and it is used by the enum entry initialization.
|
||||
*/
|
||||
if (expressionInScope.isCompanionMemberUsedForEnumEntryInit(calledSymbol)) return false
|
||||
|
||||
val candidates = resolveUnqualifiedAccess(firQualifiedAccess, calledSymbol.name, expressionInScope)
|
||||
|
||||
val scopeForQualifiedAccess = candidates.findScopeForSymbol(calledSymbol) ?: return false
|
||||
if (candidates.mapNotNull { it.candidate.originScope }
|
||||
.hasScopeCloserThan(scopeForQualifiedAccess, expressionInScope)) return false
|
||||
val candidatesWithinSamePriorityScopes = candidates.filter { it.candidate.originScope == scopeForQualifiedAccess }
|
||||
if (candidatesWithinSamePriorityScopes.isEmpty() || candidatesWithinSamePriorityScopes.size == 1) return true
|
||||
|
||||
/**
|
||||
* This is a conservative decision to avoid false positives.
|
||||
*
|
||||
* TODO: Figure out the priorities among `candidatesWithinSamePriorityScopes` and determine if [firQualifiedAccess] matches the
|
||||
* one with the highest priority. At this moment, we have some counter examples that [OverloadCandidate.isInBestCandidates] is true
|
||||
* and its symbol matches [firQualifiedAccess], but we cannot shorten it.
|
||||
*
|
||||
* For example:
|
||||
* package foo
|
||||
* class Foo {
|
||||
* fun test() {
|
||||
* // It references FIRST. Removing `foo` lets it reference SECOND. However, the one has true for
|
||||
* // [OverloadCandidate.isInBestCandidates] is FIRST. Therefore, making a decision based on `isInBestCandidates` can
|
||||
* // cause false positives i.e., shortening changes the referenced symbol.
|
||||
* <caret>foo.myRun {
|
||||
* 42
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* inline fun <R> myRun(block: () -> R): R = block() // FIRST
|
||||
* inline fun <T, R> T.myRun(block: T.() -> R): R = block() // SECOND
|
||||
*/
|
||||
return false
|
||||
}
|
||||
|
||||
private fun processPropertyReference(resolvedNamedReference: FirResolvedNamedReference) {
|
||||
val referenceExpression = resolvedNamedReference.psi as? KtNameReferenceExpression ?: return
|
||||
if (!referenceExpression.textRange.intersects(selection)) return
|
||||
val qualifiedProperty = referenceExpression.getDotQualifiedExpressionForSelector() ?: return
|
||||
|
||||
val callableSymbol = resolvedNamedReference.resolvedSymbol as? FirCallableSymbol<*> ?: return
|
||||
processCallableQualifiedAccess(callableSymbol, qualifiedProperty, qualifiedProperty, shorteningContext::findPropertiesInScopes)
|
||||
|
||||
val option = callableShortenOption(callableSymbol)
|
||||
if (option == ShortenOption.DO_NOT_SHORTEN) return
|
||||
|
||||
val scopes = shorteningContext.findScopesAtPosition(qualifiedProperty, namesToImport, towerContextProvider) ?: return
|
||||
val availableCallables = shorteningContext.findPropertiesInScopes(scopes, callableSymbol.name)
|
||||
|
||||
val firPropertyAccess = qualifiedProperty.getOrBuildFir(firResolveSession) as? FirQualifiedAccessExpression ?: return
|
||||
if (availableCallables.isNotEmpty() && shortenIfAlreadyImported(firPropertyAccess, callableSymbol, referenceExpression)) {
|
||||
addElementToShorten(ShortenQualifier(qualifiedProperty))
|
||||
return
|
||||
}
|
||||
if (option == ShortenOption.SHORTEN_IF_ALREADY_IMPORTED) return
|
||||
|
||||
processCallableQualifiedAccess(
|
||||
callableSymbol,
|
||||
option,
|
||||
qualifiedProperty,
|
||||
availableCallables,
|
||||
)
|
||||
}
|
||||
|
||||
private fun processFunctionCall(functionCall: FirFunctionCall) {
|
||||
@@ -487,21 +882,34 @@ private class ElementsToShortenCollector(
|
||||
|
||||
val calleeReference = functionCall.calleeReference
|
||||
val calledSymbol = findUnambiguousReferencedCallableId(calleeReference) ?: return
|
||||
processCallableQualifiedAccess(calledSymbol, qualifiedCallExpression, callExpression, shorteningContext::findFunctionsInScopes)
|
||||
|
||||
val option = callableShortenOption(calledSymbol)
|
||||
if (option == ShortenOption.DO_NOT_SHORTEN) return
|
||||
|
||||
val scopes = shorteningContext.findScopesAtPosition(callExpression, namesToImport, towerContextProvider) ?: return
|
||||
val availableCallables = shorteningContext.findFunctionsInScopes(scopes, calledSymbol.name)
|
||||
if (availableCallables.isNotEmpty() && shortenIfAlreadyImported(functionCall, calledSymbol, callExpression)) {
|
||||
addElementToShorten(ShortenQualifier(qualifiedCallExpression))
|
||||
return
|
||||
}
|
||||
if (option == ShortenOption.SHORTEN_IF_ALREADY_IMPORTED) return
|
||||
|
||||
processCallableQualifiedAccess(
|
||||
calledSymbol,
|
||||
option,
|
||||
qualifiedCallExpression,
|
||||
availableCallables,
|
||||
)
|
||||
}
|
||||
|
||||
private fun processCallableQualifiedAccess(
|
||||
calledSymbol: FirCallableSymbol<*>,
|
||||
option: ShortenOption,
|
||||
qualifiedCallExpression: KtDotQualifiedExpression,
|
||||
expressionToGetScope: KtExpression,
|
||||
findCallableInScopes: (List<FirScope>, Name) -> List<AvailableSymbol<FirCallableSymbol<*>>>,
|
||||
availableCallables: List<AvailableSymbol<FirCallableSymbol<*>>>,
|
||||
) {
|
||||
val option = callableShortenOption(calledSymbol)
|
||||
if (option == ShortenOption.DO_NOT_SHORTEN) return
|
||||
|
||||
val scopes = shorteningContext.findScopesAtPosition(expressionToGetScope, namesToImport, towerContextProvider) ?: return
|
||||
val availableCallables = findCallableInScopes(scopes, calledSymbol.name)
|
||||
|
||||
val nameToImport = shorteningContext.convertToImportableName(calledSymbol)
|
||||
|
||||
val (matchedCallables, otherCallables) = availableCallables.partition { it.symbol.callableId == calledSymbol.callableId }
|
||||
@@ -609,11 +1017,11 @@ private class ElementsToShortenCollector(
|
||||
}
|
||||
|
||||
private class ShortenCommandImpl(
|
||||
val targetFile: SmartPsiElementPointer<KtFile>,
|
||||
val importsToAdd: List<FqName>,
|
||||
val starImportsToAdd: List<FqName>,
|
||||
val typesToShorten: List<SmartPsiElementPointer<KtUserType>>,
|
||||
val qualifiersToShorten: List<SmartPsiElementPointer<KtDotQualifiedExpression>>,
|
||||
private val targetFile: SmartPsiElementPointer<KtFile>,
|
||||
private val importsToAdd: List<FqName>,
|
||||
private val starImportsToAdd: List<FqName>,
|
||||
private val typesToShorten: List<SmartPsiElementPointer<KtUserType>>,
|
||||
private val qualifiersToShorten: List<SmartPsiElementPointer<KtDotQualifiedExpression>>,
|
||||
) : ShortenCommand {
|
||||
|
||||
override fun invokeShortening() {
|
||||
@@ -643,6 +1051,10 @@ private class ShortenCommandImpl(
|
||||
}
|
||||
|
||||
override val isEmpty: Boolean get() = typesToShorten.isEmpty() && qualifiersToShorten.isEmpty()
|
||||
|
||||
override fun getTypesToShorten(): List<SmartPsiElementPointer<KtUserType>> = typesToShorten
|
||||
|
||||
override fun getQualifiersToShorten(): List<SmartPsiElementPointer<KtDotQualifiedExpression>> = qualifiersToShorten
|
||||
}
|
||||
|
||||
private fun KtUserType.hasFakeRootPrefix(): Boolean =
|
||||
@@ -657,4 +1069,4 @@ internal fun KtElement.getDotQualifiedExpressionForSelector(): KtDotQualifiedExp
|
||||
private fun KtDotQualifiedExpression.deleteQualifier(): KtExpression? {
|
||||
val selectorExpression = selectorExpression ?: return null
|
||||
return this.replace(selectorExpression) as KtExpression
|
||||
}
|
||||
}
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.fir.test.cases.generated.cases.references;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory;
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references.AbstractReferenceShortenerTest;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.analysis.api.GenerateAnalysisApiTestsKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("analysis/analysis-api/testData/components/referenceShortener/referenceShortener")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated extends AbstractReferenceShortenerTest {
|
||||
@NotNull
|
||||
@Override
|
||||
public AnalysisApiTestConfigurator getConfigurator() {
|
||||
return AnalysisApiFirTestConfiguratorFactory.INSTANCE.createConfigurator(
|
||||
new AnalysisApiTestConfiguratorFactoryData(
|
||||
FrontendKind.Fir,
|
||||
TestModuleKind.Source,
|
||||
AnalysisSessionMode.Normal,
|
||||
AnalysisApiMode.Ide
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllFilesPresentInReferenceShortener() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/referenceShortener/referenceShortener"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classScopes.kt")
|
||||
public void testClassScopes() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classScopes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classScopes2.kt")
|
||||
public void testClassScopes2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classScopes2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classScopes3.kt")
|
||||
public void testClassScopes3() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classScopes3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classType.kt")
|
||||
public void testClassType() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classType2.kt")
|
||||
public void testClassType2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classType2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName.kt")
|
||||
public void testClassesWithSameName() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName2.kt")
|
||||
public void testClassesWithSameName2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName3.kt")
|
||||
public void testClassesWithSameName3() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName4.kt")
|
||||
public void testClassesWithSameName4() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName4.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName5.kt")
|
||||
public void testClassesWithSameName5() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName5.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName6.kt")
|
||||
public void testClassesWithSameName6() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName6.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName7.kt")
|
||||
public void testClassesWithSameName7() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName7.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName8.kt")
|
||||
public void testClassesWithSameName8() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName8.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classesWithSameName9.kt")
|
||||
public void testClassesWithSameName9() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/classesWithSameName9.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionClassLiteral.kt")
|
||||
public void testCompanionClassLiteral() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/companionClassLiteral.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionClassLiteral2.kt")
|
||||
public void testCompanionClassLiteral2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/companionClassLiteral2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionClassLiteral3.kt")
|
||||
public void testCompanionClassLiteral3() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/companionClassLiteral3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionQualifier.kt")
|
||||
public void testCompanionQualifier() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/companionQualifier.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionUsedOutOfClass.kt")
|
||||
public void testCompanionUsedOutOfClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/companionUsedOutOfClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enumClassCompanionAlreadyImported.kt")
|
||||
public void testEnumClassCompanionAlreadyImported() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/enumClassCompanionAlreadyImported.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enumEntryInitUsesCompanion.kt")
|
||||
public void testEnumEntryInitUsesCompanion() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/enumEntryInitUsesCompanion.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enumEntryInitUsesCompanion2.kt")
|
||||
public void testEnumEntryInitUsesCompanion2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/enumEntryInitUsesCompanion2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nestedClass.kt")
|
||||
public void testNestedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nestedClass2.kt")
|
||||
public void testNestedClass2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClass2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nestedClass3.kt")
|
||||
public void testNestedClass3() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClass3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("parameterTypeTopLevelTypeLoses.kt")
|
||||
public void testParameterTypeTopLevelTypeLoses() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/parameterTypeTopLevelTypeLoses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("receiver.kt")
|
||||
public void testReceiver() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("receiver2.kt")
|
||||
public void testReceiver2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("receiver3.kt")
|
||||
public void testReceiver3() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("receiver4.kt")
|
||||
public void testReceiver4() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver4.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("referenceInNestedClass.kt")
|
||||
public void testReferenceInNestedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/referenceInNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sameNameDifferentParams.kt")
|
||||
public void testSameNameDifferentParams() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/sameNameDifferentParams.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sameNameDifferentReceiver.kt")
|
||||
public void testSameNameDifferentReceiver() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/sameNameDifferentReceiver.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sameTypeNamesWithinSameScopes.kt")
|
||||
public void testSameTypeNamesWithinSameScopes() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/sameTypeNamesWithinSameScopes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("shortenAlreadyImportedClass.kt")
|
||||
public void testShortenAlreadyImportedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/shortenAlreadyImportedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("shortenAlreadyImportedClass2.kt")
|
||||
public void testShortenAlreadyImportedClass2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/shortenAlreadyImportedClass2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("shortenAlreadyImportedFunction.kt")
|
||||
public void testShortenAlreadyImportedFunction() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/shortenAlreadyImportedFunction.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("shortenAlreadyImportedFunction2.kt")
|
||||
public void testShortenAlreadyImportedFunction2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/shortenAlreadyImportedFunction2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("shortenAlreadyImportedFunction3.kt")
|
||||
public void testShortenAlreadyImportedFunction3() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/shortenAlreadyImportedFunction3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("shortenAlreadyImportedFunction4.kt")
|
||||
public void testShortenAlreadyImportedFunction4() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/shortenAlreadyImportedFunction4.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("superClass.kt")
|
||||
public void testSuperClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/superClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeParams.kt")
|
||||
public void testTypeParams() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParams.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeParams2.kt")
|
||||
public void testTypeParams2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/typeParams2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("variable.kt")
|
||||
public void testVariable() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/variable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("variable2.kt")
|
||||
public void testVariable2() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/variable2.kt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user