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:
Jaebaek Seo
2023-01-25 02:17:38 +01:00
committed by teamcity
parent 860dee2cb1
commit a09d0aa1cf
99 changed files with 2265 additions and 45 deletions
@@ -6,15 +6,18 @@
package org.jetbrains.kotlin.analysis.api.descriptors.components
import com.intellij.openapi.util.TextRange
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.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.components.base.Fe10KtAnalysisSessionComponent
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.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtUserType
internal class KtFe10ReferenceShortener(
override val analysisSession: KtFe10AnalysisSession
@@ -34,6 +37,10 @@ internal class KtFe10ReferenceShortener(
override val isEmpty: Boolean
get() = true
override fun getTypesToShorten(): List<SmartPsiElementPointer<KtUserType>> = emptyList()
override fun getQualifiersToShorten(): List<SmartPsiElementPointer<KtDotQualifiedExpression>> = emptyList()
override fun invokeShortening() {}
}
}
@@ -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
}
}
@@ -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");
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2022 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.impl.base.test.cases.references
import org.jetbrains.kotlin.analysis.api.components.ShortenOption
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedSingleModuleTest
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.utils.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
/**
* A class for reference shortener test.
*
* Note that it tests shortening only a single expression between <expr> and </expr> in the first file.
*/
abstract class AbstractReferenceShortenerTest : AbstractAnalysisApiBasedSingleModuleTest() {
override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
val element = testServices.expressionMarkerProvider.getSelectedElement(ktFiles.first())
val shortenings = executeOnPooledThreadInReadAction {
analyseForTest(element) {
ShortenOption.values().map { option ->
Pair(option.name, collectPossibleReferenceShorteningsInElement(element, { option }, { option }))
}
}
}
val actual = buildString {
appendLine("Before shortening: ${element.text}")
shortenings.forEach { (name, shortening) ->
appendLine("with ${name}:")
if (shortening.isEmpty) return@forEach
shortening.getTypesToShorten().forEach { userType ->
userType.element?.text?.let {
appendLine("[type] $it")
}
}
shortening.getQualifiersToShorten().forEach { qualifier ->
qualifier.element?.text?.let {
appendLine("[qualifier] $it")
}
}
}
}
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
}
}
@@ -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.standalone.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.standalone.fir.test.AnalysisApiFirStandaloneModeTestConfiguratorFactory;
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 FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenerated extends AbstractReferenceShortenerTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFirStandaloneModeTestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fir,
TestModuleKind.Source,
AnalysisSessionMode.Normal,
AnalysisApiMode.Standalone
)
);
}
@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");
}
}
@@ -6,26 +6,55 @@
package org.jetbrains.kotlin.analysis.api.components
import com.intellij.openapi.util.TextRange
import com.intellij.psi.SmartPsiElementPointer
import org.jetbrains.kotlin.analysis.api.components.ShortenOption.Companion.defaultCallableShortenOption
import org.jetbrains.kotlin.analysis.api.components.ShortenOption.Companion.defaultClassShortenOption
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtEnumEntrySymbol
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtUserType
public enum class ShortenOption {
/** Skip shortening references to this symbol. */
DO_NOT_SHORTEN,
/** Only shorten references to this symbol if it's already imported in the file. Otherwise, leave it as it is. */
/**
* Only shorten references to this symbol if it's possible without adding a new import directive to the file. Otherwise, leave it as
* it is.
*
* Example:
* package a.b.c
* import foo.bar
* fun test() {}
* fun runFunctions() {
* foo.bar() // -> bar()
* a.b.c.test() // -> test()
* }
*/
SHORTEN_IF_ALREADY_IMPORTED,
/** Shorten references to this symbol and import it into the file. */
/**
* Shorten references to this symbol and import it into the file if importing it is needed for the shortening.
*
* Example:
* package a.b.c
* fun test() {}
* fun runFunctions() {
* foo.bar() // -> bar() and add a new import directive `import foo.bar`
* a.b.c.test() // -> test()
* }
*/
SHORTEN_AND_IMPORT,
/** Shorten references to this symbol and import this symbol and all its sibling symbols with star import on the parent. */
/**
* Shorten references to this symbol and import this symbol and all its sibling symbols with star import on the parent if importing them
* is needed for the shortening.
*/
SHORTEN_AND_STAR_IMPORT;
public companion object {
@@ -92,4 +121,6 @@ public interface KtReferenceShortenerMixIn : KtAnalysisSessionMixIn {
public interface ShortenCommand {
public fun invokeShortening()
public val isEmpty: Boolean
public fun getTypesToShorten(): List<SmartPsiElementPointer<KtUserType>>
public fun getQualifiersToShorten(): List<SmartPsiElementPointer<KtDotQualifiedExpression>>
}
@@ -0,0 +1,22 @@
// FILE: main.kt
package my.simple.name
open class SuperClass {
companion object {
fun check() {}
}
}
class Child: SuperClass {
class Foo constructor() {
constructor(i: Int) : this()
fun foo() {
<expr>my.simple.name.SuperClass.check()</expr>
}
companion object {
fun check() {}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: my.simple.name.SuperClass.check()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] my.simple.name.SuperClass
with SHORTEN_AND_IMPORT:
[qualifier] my.simple.name.SuperClass
with SHORTEN_AND_STAR_IMPORT:
[qualifier] my.simple.name.SuperClass
@@ -0,0 +1,22 @@
// FILE: main.kt
package my.simple.name
open class SuperClass {
companion object {
fun check() {}
}
}
class Child: SuperClass {
class Foo constructor() {
constructor(i: Int) : this()
fun foo() {
<expr>Child.Foo.check()</expr>
}
companion object {
fun check() {}
}
}
}
@@ -0,0 +1,11 @@
Before shortening: Child.Foo.check()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Child.Foo
[qualifier] Child.Foo.check()
with SHORTEN_AND_IMPORT:
[qualifier] Child.Foo
[qualifier] Child.Foo.check()
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Child.Foo
[qualifier] Child.Foo.check()
@@ -0,0 +1,28 @@
// FILE: main.kt
package my.simple.name
class ClassFarAway {
companion object {
fun check() {}
}
}
open class SuperClass {
companion object {
fun check() {}
}
}
class Child: SuperClass {
class Foo constructor() {
constructor(i: Int) : this()
fun foo() {
<expr>my.simple.name.ClassFarAway.check()</expr>
}
companion object {
fun check() {}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: my.simple.name.ClassFarAway.check()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] my.simple.name.ClassFarAway
with SHORTEN_AND_IMPORT:
[qualifier] my.simple.name.ClassFarAway
with SHORTEN_AND_STAR_IMPORT:
[qualifier] my.simple.name.ClassFarAway
@@ -0,0 +1,13 @@
// FILE: main.kt
package a.b.c
class Outer {
class Inner {
companion object {
}
}
}
fun foo(): Class<*> {
return <expr>a.b.c.Outer.Inner::class.java</expr>
}
@@ -0,0 +1,8 @@
Before shortening: a.b.c.Outer.Inner::class.java
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.Outer
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Inner
@@ -0,0 +1,12 @@
// FILE: main.kt
package a.b.c
class Outer {
class Middle {
class Inner {
fun foo(): Class<*> {
return <expr>a.b.c.Outer.Middle.Inner::class.java</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: a.b.c.Outer.Middle.Inner::class.java
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.Outer.Middle.Inner
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Middle.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Middle.Inner
@@ -0,0 +1,14 @@
// FILE: main.kt
package a.b.c
class Outer {
class Inner {
companion object {
}
}
}
fun foo(): Class<*> {
class Outer
return <expr>a.b.c.Outer.Inner::class.java</expr>
}
@@ -0,0 +1,7 @@
Before shortening: a.b.c.Outer.Inner::class.java
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Inner
@@ -0,0 +1,13 @@
// FILE: main.kt
package a.b.c
class Outer {
class Inner {
fun foo(): Class<*> {
class Outer
return <expr>a.b.c.Outer.Inner::class.java</expr>
}
companion object {
}
}
}
@@ -0,0 +1,8 @@
Before shortening: a.b.c.Outer.Inner::class.java
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Inner
@@ -0,0 +1,13 @@
// FILE: main.kt
package a.b.c
class Outer {
class Outer
class Inner {
fun foo(): Class<*> {
return <expr>a.b.c.Outer.Inner::class.java</expr>
}
companion object {
}
}
}
@@ -0,0 +1,8 @@
Before shortening: a.b.c.Outer.Inner::class.java
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Inner
@@ -0,0 +1,16 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
class Bwd {
fun dad() {
val Bwd = 42
class BBD
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,16 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
class Bwd {
fun dad() {
val Bwd = 42
val BBD = 3
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,15 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
class Bwd {
class BBD
fun dad() {
val Bwd = 42
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,15 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
class BBD
class Bwd {
fun dad() {
val Bwd = 42
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,17 @@
// FILE: main.kt
package foo.www.ddd
class BBD {
class Bwd
}
class Check {
class BBD {
class Bwd {
fun dad() {
val Bwd = 42
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check.BBD
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,13 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
class Bwd {
fun dad() {
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,13 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
companion object Bwd {
fun dad() {
val Bwd = 42
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check.BBD
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,13 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
object Bwd {
fun dad() {
val Bwd = 42
val a = <expr>foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Bwd::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check.BBD
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Bwd
@@ -0,0 +1,13 @@
// FILE: main.kt
package foo.www.ddd
class Check {
class BBD {
companion object {
fun dad() {
val Bwd = 42
val a = <expr>foo.www.ddd.Check.BBD.Companion::class.java.annotatedInterfaces.size</expr>
}
}
}
}
@@ -0,0 +1,8 @@
Before shortening: foo.www.ddd.Check.BBD.Companion::class.java.annotatedInterfaces.size
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] foo.www.ddd.Check.BBD.Companion
with SHORTEN_AND_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Companion
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.www.ddd.Check.BBD.Companion
@@ -0,0 +1,19 @@
// FILE: main.kt
package a.b.c
class Outer {
fun <E> E.foo(x: E, y: E, z: E) = x.hashCode() + y.hashCode() + z.hashCode()
class Inner {
fun foo(a: Int, b: Boolean, c: String) = c + a + b
companion object {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
}
}
}
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c
return <expr>a.b.c.Outer.Inner.Companion.foo(1, false, "bar")</expr>
}
@@ -0,0 +1,8 @@
Before shortening: a.b.c.Outer.Inner.Companion.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.Outer
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Inner.Companion
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Inner.Companion
@@ -0,0 +1,14 @@
// FILE: main.kt
package a.b.c
class MyClass(val id: Int) {
companion object {
fun foo() = ""
}
}
fun test() {
<expr>a.b.c.MyClass.Companion.foo()</expr>
}
fun foo() = ""
@@ -0,0 +1,8 @@
Before shortening: a.b.c.MyClass.Companion.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.MyClass
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.MyClass.Companion
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.MyClass.Companion
@@ -0,0 +1,18 @@
package a.b.c
import a.b.c.MyEnum.Companion.foo
enum class MyEnum(val id: Int) {
A(1),
B(2);
companion object {
fun foo() = ""
}
}
fun test() {
<expr>a.b.c.MyEnum.Companion.foo()</expr>
}
fun foo() = ""
@@ -0,0 +1,11 @@
Before shortening: a.b.c.MyEnum.Companion.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.MyEnum
[qualifier] a.b.c.MyEnum.Companion.foo()
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.MyEnum.Companion
[qualifier] a.b.c.MyEnum.Companion.foo()
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.MyEnum.Companion
[qualifier] a.b.c.MyEnum.Companion.foo()
@@ -0,0 +1,8 @@
enum class C(val i: Int) {
ONE(<expr>C.K</expr>)
;
companion object {
const val K = 1
}
}
@@ -0,0 +1,7 @@
Before shortening: C.K
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[qualifier] C.K
with SHORTEN_AND_STAR_IMPORT:
[qualifier] C.K
@@ -0,0 +1,8 @@
enum class C(val i: Int) {
ONE(<expr>C.foo()</expr>)
;
companion object {
fun foo() = 1
}
}
@@ -0,0 +1,7 @@
Before shortening: C.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[qualifier] C.foo()
with SHORTEN_AND_STAR_IMPORT:
[qualifier] C.foo()
@@ -0,0 +1,23 @@
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
class Outer {
fun <E> E.foo(x: E, y: E, z: E) = x.hashCode() + y.hashCode() + z.hashCode()
class Inner {
fun foo(a: Int, b: Boolean, c: String) = c + a + b
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c
return <expr>Inner.foo(1, false, "bar")</expr>
}
companion object {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
}
}
}
@@ -0,0 +1,5 @@
Before shortening: Inner.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,23 @@
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
class Outer {
fun <E> E.foo(x: E, y: E, z: E) = x.hashCode() + y.hashCode() + z.hashCode()
class Inner {
fun foo(a: Int, b: Boolean, c: String) = c + a + b
fun test(): Int {
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode()
return <expr>a.b.c.foo(1, false, "bar")</expr>
}
companion object {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
}
}
}
@@ -0,0 +1,5 @@
Before shortening: a.b.c.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,23 @@
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
class Outer {
fun <E> E.foo(x: E, y: E, z: E) = x.hashCode() + y.hashCode() + z.hashCode()
class Inner {
fun foo(a: Int, b: Boolean, c: String) = c + a + b
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c
return <expr>Inner.Companion.foo(1, false, "bar")</expr>
}
companion object {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
}
}
}
@@ -0,0 +1,8 @@
Before shortening: Inner.Companion.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Inner.Companion
with SHORTEN_AND_IMPORT:
[qualifier] Inner.Companion
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Inner.Companion
@@ -0,0 +1,13 @@
// FILE: main.kt
package test
import dependency.T
class T
fun foo(t: <expr>test.T</expr>) {}
// FILE: dep.kt
package dependency
class T
@@ -0,0 +1,5 @@
Before shortening: test.T
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,17 @@
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
object Receiver {
fun <E> E.foo(x: E, y: E, z: E) = x.hashCode() + y.hashCode() + z.hashCode()
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode()
fun test(): Int {
fun foo(a: Int, b: Boolean, c: String) = a + b.hashCode() + c.hashCode()
return <expr>Receiver.foo(1, false, "bar")</expr>
}
}
@@ -0,0 +1,5 @@
Before shortening: Receiver.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,18 @@
// IGNORE_FIR
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
object Receiver {
fun <E> E.foo(x: E, y: E, z: E) = x.hashCode() + y.hashCode() + z.hashCode()
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode()
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c
return <expr>Receiver.foo(1, false, "bar")</expr>
}
}
@@ -0,0 +1,8 @@
Before shortening: Receiver.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Receiver.foo(1, false, "bar")
with SHORTEN_AND_IMPORT:
[qualifier] Receiver.foo(1, false, "bar")
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Receiver.foo(1, false, "bar")
@@ -0,0 +1,18 @@
// IGNORE_FIR
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
object Receiver {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode()
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c
return <expr>Receiver.foo(1, false, "bar")</expr>
}
}
@@ -0,0 +1,8 @@
Before shortening: Receiver.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Receiver.foo(1, false, "bar")
with SHORTEN_AND_IMPORT:
[qualifier] Receiver.foo(1, false, "bar")
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Receiver.foo(1, false, "bar")
@@ -0,0 +1,17 @@
// FILE: main.kt
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun <E> E.foo() = hashCode()
object Receiver {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode()
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode()
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c
return <expr>a.b.c.foo(1, false, "bar")</expr>
}
}
@@ -0,0 +1,5 @@
Before shortening: a.b.c.foo(1, false, "bar")
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,35 @@
// MODULE: commonMain
// FILE: main.kt
package a.b.c
fun foo() {}
class Outer {
fun foo() {}
class Inner {
fun foo() {}
class InnerOfInner {
fun foo() {}
private fun check() {
fun foo() {}
<expr>a.b.c.Outer.Inner.Companion.foo()</expr>
}
companion object {
fun foo() {}
}
}
companion object {
fun foo() {}
}
}
companion object {
fun foo() {}
}
}
@@ -0,0 +1,8 @@
Before shortening: a.b.c.Outer.Inner.Companion.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_IMPORT:
[qualifier] a.b.c.Outer.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] a.b.c.Outer.Inner
@@ -0,0 +1,8 @@
// FILE: main.kt
fun foo() {}
fun test() {
fun foo(a: Int) = a + 1
<expr>foo()</expr>
}
@@ -0,0 +1,5 @@
Before shortening: foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,13 @@
package foo
class Foo {
fun test() {
<expr>foo.myRun {
42
}</expr>
}
}
inline fun <R> myRun(block: () -> R): R = block()
inline fun <T, R> T.myRun(block: T.() -> R): R = block()
@@ -0,0 +1,13 @@
Before shortening: foo.myRun {
42
}
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[qualifier] foo.myRun {
42
}
with SHORTEN_AND_STAR_IMPORT:
[qualifier] foo.myRun {
42
}
@@ -0,0 +1,4 @@
// FILE: main.kt
package test
<expr>fun foo(p1: dependency1.T, p2: dependency2.T) {}</expr>
@@ -0,0 +1,5 @@
Before shortening: fun foo(p1: dependency1.T, p2: dependency2.T) {}
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,16 @@
// FILE: main.kt
package a.b.c
import dependency.T
class T
fun test() {
class T(a: Int)
<expr>dependency.T<Int>(3)</expr>
}
// FILE: dep.kt
package dependency
class T<E>(value: Int)
@@ -0,0 +1,8 @@
Before shortening: dependency.T<Int>(3)
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] dependency.T<Int>(3)
with SHORTEN_AND_IMPORT:
[qualifier] dependency.T<Int>(3)
with SHORTEN_AND_STAR_IMPORT:
[qualifier] dependency.T<Int>(3)
@@ -0,0 +1,15 @@
// FILE: main.kt
package a.b.c
import dependency.T
class T(a: Int)
fun test() {
<expr>dependency.T::class.java</expr>
}
// FILE: dep.kt
package dependency
class T<E>(value: Int)
@@ -0,0 +1,8 @@
Before shortening: dependency.T::class.java
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] dependency.T
with SHORTEN_AND_IMPORT:
[qualifier] dependency.T
with SHORTEN_AND_STAR_IMPORT:
[qualifier] dependency.T
@@ -0,0 +1,20 @@
// FILE: main.kt
package a.b.c
import dependency.foo
fun foo() {}
class Outer {
fun foo() {}
class Inner {
fun test() {
<expr>a.b.c.foo()</expr>
}
}
}
// FILE: dep.kt
package dependency
fun foo() {}
@@ -0,0 +1,5 @@
Before shortening: a.b.c.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,22 @@
// FILE: main.kt
package a.b.c
import dependency.foo
fun foo() {}
class Outer {
class Inner {
fun test() {
<expr>Outer.Inner.foo()</expr>
}
companion object {
fun foo() {}
}
}
}
// FILE: dep.kt
package dependency
fun foo() {}
@@ -0,0 +1,11 @@
Before shortening: Outer.Inner.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Outer.Inner
[qualifier] Outer.Inner.foo()
with SHORTEN_AND_IMPORT:
[qualifier] Outer.Inner
[qualifier] Outer.Inner.foo()
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Outer.Inner
[qualifier] Outer.Inner.foo()
@@ -0,0 +1,23 @@
// FILE: main.kt
package a.b.c
import dependency.foo
fun foo() {}
class Outer {
class Inner {
fun foo() {}
fun test() {
<expr>Outer.Inner.foo()</expr>
}
companion object {
fun foo() {}
}
}
}
// FILE: dep.kt
package dependency
fun foo() {}
@@ -0,0 +1,8 @@
Before shortening: Outer.Inner.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Outer.Inner
with SHORTEN_AND_IMPORT:
[qualifier] Outer.Inner
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Outer.Inner
@@ -0,0 +1,23 @@
// FILE: main.kt
package a.b.c
import dependency.foo
fun foo() {}
open class Base {
companion object {
fun foo() {}
}
}
class Child : Base() {
fun test() {
<expr>Base.foo()</expr>
}
}
// FILE: dep.kt
package dependency
fun foo() {}
@@ -0,0 +1,8 @@
Before shortening: Base.foo()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Base.foo()
with SHORTEN_AND_IMPORT:
[qualifier] Base.foo()
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Base.foo()
@@ -0,0 +1,14 @@
// FILE: main.kt
package my.simple.name
fun one() {}
open class Parent {
fun one() {}
}
class Child: Parent() {
fun test() {
<expr>my.simple.name.one()</expr>
}
}
@@ -0,0 +1,5 @@
Before shortening: my.simple.name.one()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,9 @@
// FILE: main.kt
package a.b.c
fun <T> foo(a: T, b: T) = a.hashCode() + b.hashCode()
fun test(): Int {
fun foo(a: Int, b: Int) = a + b
return <expr>a.b.c.foo(1, 2)</expr>
}
@@ -0,0 +1,5 @@
Before shortening: a.b.c.foo(1, 2)
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,10 @@
fun <T> foo(a: T, b: T) = a.hashCode() + b.hashCode()
fun <E> E.foo() = hashCode()
fun <E> E.foo(x: E, y: E) = x.hashCode() + y.hashCode()
fun test(): Int {
fun foo(a: Int, b: Int) = a + b
return <expr>foo<Int>(1, 2)</expr>
}
@@ -0,0 +1,5 @@
Before shortening: foo<Int>(1, 2)
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,17 @@
// FILE: main.kt
package a.b.c
val foo = 3
val <E> E.foo: Int
get() = 4
object Receiver {
val foo: Int
get() = 5
fun test(): Int {
val foo = 6
return <expr>Receiver.foo</expr>
}
}
@@ -0,0 +1,5 @@
Before shortening: Receiver.foo
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
with SHORTEN_AND_STAR_IMPORT:
@@ -0,0 +1,16 @@
// FILE: main.kt
package a.b.c
val foo = 3
val <E> E.foo: Int
get() = 4
object Receiver {
val foo: Int
get() = 5
fun test(): Int {
return <expr>Receiver.foo</expr>
}
}
@@ -0,0 +1,8 @@
Before shortening: Receiver.foo
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[qualifier] Receiver.foo
with SHORTEN_AND_IMPORT:
[qualifier] Receiver.foo
with SHORTEN_AND_STAR_IMPORT:
[qualifier] Receiver.foo
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.calls.InapplicableCandidate
@@ -61,16 +61,16 @@ class AllCandidatesResolver(private val firSession: FirSession) {
fun getAllCandidates(
firResolveSession: LLFirResolveSession,
functionCall: FirFunctionCall,
qualifiedAccess: FirQualifiedAccessExpression,
calleeName: Name,
element: KtElement
element: KtElement,
): List<OverloadCandidate> {
initializeBodyResolveContext(firResolveSession, element)
val firFile = element.containingKtFile.getOrBuildFirFile(firResolveSession)
return bodyResolveComponents.context.withFile(firFile, bodyResolveComponents) {
bodyResolveComponents.callResolver
.collectAllCandidates(functionCall, calleeName, bodyResolveComponents.context.containers, resolutionContext)
.collectAllCandidates(qualifiedAccess, calleeName, bodyResolveComponents.context.containers, resolutionContext)
.apply { postProcessCandidates() }
}
}
@@ -127,4 +127,4 @@ class AllCandidatesResolver(private val firSession: FirSession) {
candidate.addDiagnostic(InapplicableCandidate)
}
}
}
@@ -234,6 +234,13 @@ class FirTowerDataElement(
isLocal,
staticScopeOwnerSymbol
)
/**
* Returns [scope] if it is not null. Otherwise, returns [implicitReceiver.implicitScope].
*
* Note that a scope for a companion object is an implicit scope.
*/
fun getAvailableScope() = scope ?: implicitReceiver?.implicitScope
}
fun ImplicitReceiverValue<*>.asTowerDataElement(): FirTowerDataElement =
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.typeInf
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.typeProvider.AbstractAnalysisApiGetSuperTypesTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.typeProvider.AbstractHasCommonSubtypeTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references.AbstractReferenceResolveTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references.AbstractReferenceShortenerTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.scopes.*
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSingleSymbolByPsi
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByFqNameTest
@@ -237,6 +238,12 @@ private fun AnalysisApiTestGroup.generateAnalysisApiComponentsTests() {
}
}
component("referenceShortener", filter = frontendIs(FrontendKind.Fir) and analysisSessionModeIs(AnalysisSessionMode.Normal)) {
test(AbstractReferenceShortenerTest::class) {
model("referenceShortener")
}
}
component("expressionTypeProvider") {
test(AbstractExpectedExpressionTypeTest::class) {
model("expectedExpressionType")