Shorten references to use analyze() for multiple elements
This commit is contained in:
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
|||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
@@ -45,6 +44,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.utils.findPackage
|
import org.jetbrains.kotlin.resolve.scopes.utils.findPackage
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT }) {
|
class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT }) {
|
||||||
@@ -130,7 +130,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads fun process(elements: Iterable<KtElement>, elementFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS }): Collection<KtElement> {
|
@JvmOverloads fun process(elements: Iterable<KtElement>, elementFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS }): Collection<KtElement> {
|
||||||
return elements.groupBy { element -> element.getContainingKtFile() }
|
return elements.groupBy(KtElement::getContainingKtFile)
|
||||||
.flatMap { shortenReferencesInFile(it.key, it.value, elementFilter) }
|
.flatMap { shortenReferencesInFile(it.key, it.value, elementFilter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,9 +155,25 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
|
ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
|
||||||
RemoveExplicitCompanionObjectReferenceVisitor(file, elementFilter, failedToImportDescriptors)
|
RemoveExplicitCompanionObjectReferenceVisitor(file, elementFilter, failedToImportDescriptors)
|
||||||
)
|
)
|
||||||
val descriptorsToImport = visitors.flatMap { analyzeReferences(elementsToUse, it) }.toSet()
|
|
||||||
|
// step 1: collect qualified elements to analyze (no resolve at this step)
|
||||||
|
for (visitor in visitors) {
|
||||||
|
for (element in elementsToUse) {
|
||||||
|
visitor.options = options(element)
|
||||||
|
element.accept(visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val elementsToAnalyze = visitors.flatMap { it.getElementsToAnalyze() }
|
||||||
|
|
||||||
|
// step 2: analyze collected elements with resolve and decide which can be shortened now and which need descriptors to be imported before shortening
|
||||||
|
val bindingContext = file.getResolutionFacade().analyze(elementsToAnalyze, BodyResolveMode.PARTIAL)
|
||||||
|
visitors.forEach { it.analyzeCollectedElements(bindingContext) }
|
||||||
|
|
||||||
|
// step 3: shorten elements that can be shortened right now
|
||||||
visitors.forEach { it.shortenElements(elementsToUse) }
|
visitors.forEach { it.shortenElements(elementsToUse) }
|
||||||
|
|
||||||
|
// step 4: try to import descriptors needed to shorten other elements
|
||||||
|
val descriptorsToImport = visitors.flatMap { it.getDescriptorsToImport() }.toSet()
|
||||||
var anyChange = false
|
var anyChange = false
|
||||||
for (descriptor in descriptorsToImport) {
|
for (descriptor in descriptorsToImport) {
|
||||||
assert(descriptor !in failedToImportDescriptors)
|
assert(descriptor !in failedToImportDescriptors)
|
||||||
@@ -178,57 +194,103 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
|
|
||||||
private fun dropNestedElements(elements: List<KtElement>): LinkedHashSet<KtElement> {
|
private fun dropNestedElements(elements: List<KtElement>): LinkedHashSet<KtElement> {
|
||||||
val elementSet = elements.toSet()
|
val elementSet = elements.toSet()
|
||||||
val newElements = LinkedHashSet<KtElement>(elementSet.size)
|
return elementSet.filterTo(LinkedHashSet<KtElement>(elementSet.size)) { element ->
|
||||||
for (element in elementSet) {
|
element.parents.none { it in elementSet }
|
||||||
if (!element.parents.any { it in elementSet }) {
|
|
||||||
newElements.add(element)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return newElements
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun analyzeReferences(elements: Iterable<KtElement>, visitor: ShorteningVisitor<*>): Set<DeclarationDescriptor> {
|
private abstract class ShorteningVisitor<TElement : KtElement>(
|
||||||
for (element in elements) {
|
|
||||||
visitor.options = options(element)
|
|
||||||
element.accept(visitor)
|
|
||||||
}
|
|
||||||
return visitor.getDescriptorsToImport()
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class ShorteningVisitor<in T : KtElement>(
|
|
||||||
protected val file: KtFile,
|
protected val file: KtFile,
|
||||||
protected val elementFilter: (PsiElement) -> FilterResult,
|
protected val elementFilter: (PsiElement) -> FilterResult,
|
||||||
protected val failedToImportDescriptors: Set<DeclarationDescriptor>
|
protected val failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : KtVisitorVoid() {
|
) : KtVisitorVoid() {
|
||||||
var options: Options = Options.DEFAULT
|
var options: Options = Options.DEFAULT
|
||||||
|
|
||||||
private val elementsToShorten = ArrayList<T>()
|
private data class ElementToAnalyze<TElement>(val element: TElement, val level: Int)
|
||||||
|
|
||||||
|
private val elementsToAnalyze = ArrayList<ElementToAnalyze<TElement>>()
|
||||||
|
private val elementsToShorten = ArrayList<TElement>()
|
||||||
private val descriptorsToImport = LinkedHashSet<DeclarationDescriptor>()
|
private val descriptorsToImport = LinkedHashSet<DeclarationDescriptor>()
|
||||||
|
|
||||||
protected val resolutionFacade = file.getResolutionFacade()
|
protected val resolutionFacade = file.getResolutionFacade()
|
||||||
|
|
||||||
protected fun analyze(element: KtElement)
|
private var level = 0
|
||||||
= resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
|
|
||||||
|
|
||||||
protected fun processQualifiedElement(element: T, targets: Collection<DeclarationDescriptor>, canShortenNow: Boolean) {
|
protected fun nextLevel() {
|
||||||
if (canShortenNow) {
|
level++
|
||||||
addElementToShorten(element)
|
}
|
||||||
}
|
|
||||||
else if (targets.isNotEmpty() && targets.none { it in failedToImportDescriptors } && targets.all { mayImport(it, file) }) {
|
protected fun prevLevel() {
|
||||||
descriptorsToImport.addAll(targets)
|
level--
|
||||||
}
|
assert(level >= 0)
|
||||||
else {
|
}
|
||||||
qualifier(element).accept(this)
|
|
||||||
|
/**
|
||||||
|
* Should be invoked by implementors when visiting the PSI tree for those elements that can potentially be shortened
|
||||||
|
*/
|
||||||
|
protected fun addQualifiedElementToAnalyze(element: TElement) {
|
||||||
|
elementsToAnalyze.add(ElementToAnalyze(element, level))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun analyzeCollectedElements(bindingContext: BindingContext) {
|
||||||
|
var index = 0
|
||||||
|
while (index < elementsToAnalyze.size) {
|
||||||
|
val (element, level) = elementsToAnalyze[index++]
|
||||||
|
|
||||||
|
val result = analyzeQualifiedElement(element, bindingContext)
|
||||||
|
|
||||||
|
val toBeShortened: Boolean
|
||||||
|
when (result) {
|
||||||
|
is ShortenNow -> {
|
||||||
|
elementsToShorten.add(element)
|
||||||
|
toBeShortened = true
|
||||||
|
}
|
||||||
|
|
||||||
|
is ImportDescriptors -> {
|
||||||
|
val tryImport = result.descriptors.isNotEmpty()
|
||||||
|
&& result.descriptors.none { it in failedToImportDescriptors }
|
||||||
|
&& result.descriptors.all { mayImport(it, file) }
|
||||||
|
if (tryImport) {
|
||||||
|
descriptorsToImport.addAll(result.descriptors)
|
||||||
|
toBeShortened = true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toBeShortened = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Skip -> {
|
||||||
|
toBeShortened = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toBeShortened) {
|
||||||
|
// we are going to shorten qualified element - we must skip all elements inside its qualifier
|
||||||
|
while (index < elementsToAnalyze.size && elementsToAnalyze[index].level > level) {
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun addElementToShorten(element: T) {
|
/**
|
||||||
elementsToShorten.add(element)
|
* This method is invoked for all qualified elements added by [addQualifiedElementToAnalyze]
|
||||||
|
*/
|
||||||
|
protected abstract fun analyzeQualifiedElement(element: TElement, bindingContext: BindingContext): AnalyzeQualifiedElementResult
|
||||||
|
|
||||||
|
protected sealed class AnalyzeQualifiedElementResult {
|
||||||
|
object Skip : AnalyzeQualifiedElementResult()
|
||||||
|
|
||||||
|
object ShortenNow : AnalyzeQualifiedElementResult()
|
||||||
|
|
||||||
|
class ImportDescriptors(val descriptors: Collection<DeclarationDescriptor>) : AnalyzeQualifiedElementResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun qualifier(element: T): KtElement
|
typealias Skip = AnalyzeQualifiedElementResult.Skip
|
||||||
|
typealias ShortenNow = AnalyzeQualifiedElementResult.ShortenNow
|
||||||
|
typealias ImportDescriptors = AnalyzeQualifiedElementResult.ImportDescriptors
|
||||||
|
|
||||||
protected abstract fun shortenElement(element: T): KtElement
|
protected abstract fun shortenElement(element: TElement): KtElement
|
||||||
|
|
||||||
override fun visitElement(element: PsiElement) {
|
override fun visitElement(element: PsiElement) {
|
||||||
if (elementFilter(element) != FilterResult.SKIP) {
|
if (elementFilter(element) != FilterResult.SKIP) {
|
||||||
@@ -253,6 +315,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getElementsToAnalyze(): Collection<TElement> = elementsToAnalyze.map { it.element }
|
||||||
fun getDescriptorsToImport(): Set<DeclarationDescriptor> = descriptorsToImport
|
fun getDescriptorsToImport(): Set<DeclarationDescriptor> = descriptorsToImport
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,33 +331,32 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
userType.typeArgumentList?.accept(this)
|
userType.typeArgumentList?.accept(this)
|
||||||
|
|
||||||
if (filterResult == FilterResult.PROCESS) {
|
if (filterResult == FilterResult.PROCESS) {
|
||||||
processType(userType)
|
addQualifiedElementToAnalyze(userType)
|
||||||
}
|
|
||||||
else {
|
|
||||||
userType.qualifier?.accept(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// elements in qualifier must be under
|
||||||
|
nextLevel()
|
||||||
|
userType.qualifier?.accept(this)
|
||||||
|
prevLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processType(type: KtUserType) {
|
override fun analyzeQualifiedElement(element: KtUserType, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
if (type.qualifier == null) return
|
if (element.qualifier == null) return Skip
|
||||||
val referenceExpression = type.referenceExpression ?: return
|
val referenceExpression = element.referenceExpression ?: return Skip
|
||||||
|
|
||||||
val bindingContext = analyze(referenceExpression)
|
val target = referenceExpression.targets(bindingContext).singleOrNull() ?: return Skip
|
||||||
val target = referenceExpression.targets(bindingContext).singleOrNull() ?: return
|
|
||||||
|
|
||||||
val scope = type.getResolutionScope(bindingContext, resolutionFacade)
|
val scope = element.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
val name = target.name
|
val name = target.name
|
||||||
val targetByName = if (target is ClassifierDescriptor)
|
val targetByName = if (target is ClassifierDescriptor)
|
||||||
scope.findClassifier(name, NoLookupLocation.FROM_IDE)
|
scope.findClassifier(name, NoLookupLocation.FROM_IDE)
|
||||||
else
|
else
|
||||||
scope.findPackage(name)
|
scope.findPackage(name)
|
||||||
|
|
||||||
val canShortenNow = targetByName?.asString() == target.asString()
|
val canShortenNow = targetByName?.asString() == target.asString()
|
||||||
|
return if (canShortenNow) ShortenNow else ImportDescriptors(target.singletonOrEmptyList())
|
||||||
processQualifiedElement(type, target.singletonOrEmptyList(), canShortenNow)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun qualifier(element: KtUserType) = element.qualifier!!
|
|
||||||
|
|
||||||
override fun shortenElement(element: KtUserType): KtElement {
|
override fun shortenElement(element: KtUserType): KtElement {
|
||||||
element.deleteQualifier()
|
element.deleteQualifier()
|
||||||
return element
|
return element
|
||||||
@@ -306,6 +368,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
elementFilter: (PsiElement) -> FilterResult,
|
elementFilter: (PsiElement) -> FilterResult,
|
||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : ShorteningVisitor<KtDotQualifiedExpression>(file, elementFilter, failedToImportDescriptors) {
|
) : ShorteningVisitor<KtDotQualifiedExpression>(file, elementFilter, failedToImportDescriptors) {
|
||||||
|
|
||||||
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
|
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
|
||||||
val filterResult = elementFilter(expression)
|
val filterResult = elementFilter(expression)
|
||||||
if (filterResult == FilterResult.SKIP) return
|
if (filterResult == FilterResult.SKIP) return
|
||||||
@@ -313,15 +376,14 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
expression.selectorExpression?.acceptChildren(this)
|
expression.selectorExpression?.acceptChildren(this)
|
||||||
|
|
||||||
if (filterResult == FilterResult.PROCESS) {
|
if (filterResult == FilterResult.PROCESS) {
|
||||||
if (process(expression)) return
|
addQualifiedElementToAnalyze(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// elements in receiver must be under
|
||||||
|
nextLevel()
|
||||||
expression.receiverExpression.accept(this)
|
expression.receiverExpression.accept(this)
|
||||||
|
prevLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun process(qualifiedExpression: KtDotQualifiedExpression): Boolean
|
|
||||||
|
|
||||||
override fun qualifier(element: KtDotQualifiedExpression) = element.receiverExpression
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ShortenQualifiedExpressionsVisitor(
|
private class ShortenQualifiedExpressionsVisitor(
|
||||||
@@ -330,56 +392,55 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
|
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
|
||||||
|
|
||||||
override fun process(qualifiedExpression: KtDotQualifiedExpression): Boolean {
|
override fun analyzeQualifiedElement(element: KtDotQualifiedExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
val bindingContext = analyze(qualifiedExpression)
|
val receiver = element.receiverExpression
|
||||||
|
|
||||||
val receiver = qualifiedExpression.receiverExpression
|
|
||||||
when (receiver) {
|
when (receiver) {
|
||||||
is KtThisExpression -> {
|
is KtThisExpression -> {
|
||||||
if (!options.removeThis) return false
|
if (!options.removeThis) return Skip
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (bindingContext[BindingContext.QUALIFIER, receiver] == null) return false
|
if (bindingContext[BindingContext.QUALIFIER, receiver] == null) return Skip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PsiTreeUtil.getParentOfType(
|
if (PsiTreeUtil.getParentOfType(
|
||||||
qualifiedExpression,
|
element,
|
||||||
KtImportDirective::class.java, KtPackageDirective::class.java) != null) return true
|
KtImportDirective::class.java, KtPackageDirective::class.java) != null) return Skip
|
||||||
|
|
||||||
val selector = qualifiedExpression.selectorExpression ?: return false
|
val selector = element.selectorExpression ?: return Skip
|
||||||
val callee = selector.getCalleeExpressionIfAny() as? KtReferenceExpression ?: return false
|
val callee = selector.getCalleeExpressionIfAny() as? KtReferenceExpression ?: return Skip
|
||||||
val targets = callee.targets(bindingContext)
|
val targets = callee.targets(bindingContext)
|
||||||
if (targets.isEmpty()) return false
|
if (targets.isEmpty()) return Skip
|
||||||
|
|
||||||
val scope = qualifiedExpression.getResolutionScope(bindingContext, resolutionFacade)
|
val scope = element.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
val selectorCopy = selector.copy() as KtReferenceExpression
|
val selectorCopy = selector.copy() as KtReferenceExpression
|
||||||
val newContext = selectorCopy.analyzeInContext(scope, selector)
|
val newContext = selectorCopy.analyzeInContext(scope, selector)
|
||||||
val targetsWhenShort = (selectorCopy.getCalleeExpressionIfAny() as KtReferenceExpression).targets(newContext)
|
val targetsWhenShort = (selectorCopy.getCalleeExpressionIfAny() as KtReferenceExpression).targets(newContext)
|
||||||
val targetsMatch = targetsMatch(targets, targetsWhenShort)
|
val targetsMatch = targetsMatch(targets, targetsWhenShort)
|
||||||
|
|
||||||
if (receiver is KtThisExpression) {
|
if (receiver is KtThisExpression) {
|
||||||
if (!targetsMatch) return false
|
if (!targetsMatch) return Skip
|
||||||
val originalCall = selector.getResolvedCall(bindingContext) ?: return false
|
val originalCall = selector.getResolvedCall(bindingContext) ?: return Skip
|
||||||
val newCall = selectorCopy.getResolvedCall(newContext) ?: return false
|
val newCall = selectorCopy.getResolvedCall(newContext) ?: return Skip
|
||||||
val receiverKind = originalCall.explicitReceiverKind
|
val receiverKind = originalCall.explicitReceiverKind
|
||||||
val newReceiver = when (receiverKind) {
|
val newReceiver = when (receiverKind) {
|
||||||
ExplicitReceiverKind.BOTH_RECEIVERS, ExplicitReceiverKind.EXTENSION_RECEIVER -> newCall.extensionReceiver
|
ExplicitReceiverKind.BOTH_RECEIVERS, ExplicitReceiverKind.EXTENSION_RECEIVER -> newCall.extensionReceiver
|
||||||
ExplicitReceiverKind.DISPATCH_RECEIVER -> newCall.dispatchReceiver
|
ExplicitReceiverKind.DISPATCH_RECEIVER -> newCall.dispatchReceiver
|
||||||
else -> return false
|
else -> return Skip
|
||||||
} as? ImplicitReceiver ?: return false
|
} as? ImplicitReceiver ?: return Skip
|
||||||
|
|
||||||
val thisTarget = receiver.instanceReference.targets(bindingContext).singleOrNull()
|
val thisTarget = receiver.instanceReference.targets(bindingContext).singleOrNull()
|
||||||
if (newReceiver.declarationDescriptor.asString() != thisTarget?.asString()) return false
|
if (newReceiver.declarationDescriptor.asString() != thisTarget?.asString()) return Skip
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetsMatch && targetsWhenShort.any { it !is ClassDescriptor && it !is PackageViewDescriptor }) {
|
return when {
|
||||||
|
targetsMatch -> ShortenNow
|
||||||
|
|
||||||
// it makes no sense to insert import when there is a conflict with function, property etc
|
// it makes no sense to insert import when there is a conflict with function, property etc
|
||||||
return false
|
targetsWhenShort.any { it !is ClassDescriptor && it !is PackageViewDescriptor } -> Skip
|
||||||
}
|
|
||||||
|
|
||||||
processQualifiedElement(qualifiedExpression, targets, targetsMatch)
|
else -> ImportDescriptors(targets)
|
||||||
return true
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun targetsMatch(targets1: Collection<DeclarationDescriptor>, targets2: Collection<DeclarationDescriptor>): Boolean {
|
private fun targetsMatch(targets1: Collection<DeclarationDescriptor>, targets2: Collection<DeclarationDescriptor>): Boolean {
|
||||||
@@ -404,29 +465,22 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
) : ShorteningVisitor<KtThisExpression>(file, elementFilter, failedToImportDescriptors) {
|
) : ShorteningVisitor<KtThisExpression>(file, elementFilter, failedToImportDescriptors) {
|
||||||
private val simpleThis = KtPsiFactory(file).createExpression("this") as KtThisExpression
|
private val simpleThis = KtPsiFactory(file).createExpression("this") as KtThisExpression
|
||||||
|
|
||||||
private fun process(thisExpression: KtThisExpression) {
|
override fun analyzeQualifiedElement(element: KtThisExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
if (!options.removeThisLabels || thisExpression.getTargetLabel() == null) return
|
if (!options.removeThisLabels || element.getTargetLabel() == null) return Skip
|
||||||
|
|
||||||
val bindingContext = analyze(thisExpression)
|
val targetBefore = element.instanceReference.targets(bindingContext).singleOrNull() ?: return Skip
|
||||||
|
val scope = element.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
val targetBefore = thisExpression.instanceReference.targets(bindingContext).singleOrNull() ?: return
|
val newContext = simpleThis.analyzeInContext(scope, element)
|
||||||
val scope = thisExpression.getResolutionScope(bindingContext, resolutionFacade)
|
|
||||||
val newContext = simpleThis.analyzeInContext(scope, thisExpression)
|
|
||||||
val targetAfter = simpleThis.instanceReference.targets(newContext).singleOrNull()
|
val targetAfter = simpleThis.instanceReference.targets(newContext).singleOrNull()
|
||||||
if (targetBefore == targetAfter) {
|
return if (targetBefore == targetAfter) ShortenNow else Skip
|
||||||
addElementToShorten(thisExpression)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitThisExpression(expression: KtThisExpression) {
|
override fun visitThisExpression(expression: KtThisExpression) {
|
||||||
if (elementFilter(expression) == FilterResult.PROCESS) {
|
if (elementFilter(expression) == FilterResult.PROCESS) {
|
||||||
process(expression)
|
addQualifiedElementToAnalyze(expression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun qualifier(element: KtThisExpression): KtElement =
|
|
||||||
throw AssertionError("Qualifier requested: ${element.getElementTextWithContext()}")
|
|
||||||
|
|
||||||
override fun shortenElement(element: KtThisExpression): KtElement {
|
override fun shortenElement(element: KtThisExpression): KtElement {
|
||||||
return element.replace(simpleThis) as KtElement
|
return element.replace(simpleThis) as KtElement
|
||||||
}
|
}
|
||||||
@@ -442,39 +496,33 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
return (getCalleeExpressionIfAny() as? KtReferenceExpression)?.targets(context)?.singleOrNull()
|
return (getCalleeExpressionIfAny() as? KtReferenceExpression)?.targets(context)?.singleOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun process(qualifiedExpression: KtDotQualifiedExpression): Boolean {
|
override fun analyzeQualifiedElement(element: KtDotQualifiedExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
val bindingContext = analyze(qualifiedExpression)
|
val receiver = element.receiverExpression
|
||||||
|
|
||||||
val receiver = qualifiedExpression.receiverExpression
|
|
||||||
|
|
||||||
if (PsiTreeUtil.getParentOfType(
|
if (PsiTreeUtil.getParentOfType(
|
||||||
qualifiedExpression,
|
element,
|
||||||
KtImportDirective::class.java, KtPackageDirective::class.java) != null) return false
|
KtImportDirective::class.java, KtPackageDirective::class.java) != null) return Skip
|
||||||
|
|
||||||
val receiverTarget = receiver.singleTarget(bindingContext) ?: return false
|
val receiverTarget = receiver.singleTarget(bindingContext) ?: return Skip
|
||||||
if (receiverTarget !is ClassDescriptor) return false
|
if (receiverTarget !is ClassDescriptor) return Skip
|
||||||
|
|
||||||
val selectorExpression = qualifiedExpression.selectorExpression ?: return false
|
val selectorExpression = element.selectorExpression ?: return Skip
|
||||||
val selectorTarget = selectorExpression.singleTarget(bindingContext) ?: return false
|
val selectorTarget = selectorExpression.singleTarget(bindingContext) ?: return Skip
|
||||||
|
|
||||||
if (receiverTarget.companionObjectDescriptor != selectorTarget) return false
|
if (receiverTarget.companionObjectDescriptor != selectorTarget) return Skip
|
||||||
|
|
||||||
val selectorsSelector = (qualifiedExpression.parent as? KtDotQualifiedExpression)?.selectorExpression
|
val selectorsSelector = (element.parent as? KtDotQualifiedExpression)?.selectorExpression
|
||||||
if (selectorsSelector == null) {
|
?: return ShortenNow
|
||||||
addElementToShorten(qualifiedExpression)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
val selectorsSelectorTarget = selectorsSelector.singleTarget(bindingContext) ?: return false
|
val selectorsSelectorTarget = selectorsSelector.singleTarget(bindingContext) ?: return Skip
|
||||||
if (selectorsSelectorTarget is ClassDescriptor) return false
|
if (selectorsSelectorTarget is ClassDescriptor) return Skip
|
||||||
// TODO: More generic solution may be possible
|
// TODO: More generic solution may be possible
|
||||||
if (selectorsSelectorTarget is PropertyDescriptor) {
|
if (selectorsSelectorTarget is PropertyDescriptor) {
|
||||||
val source = selectorsSelectorTarget.source.getPsi() as? KtProperty
|
val source = selectorsSelectorTarget.source.getPsi() as? KtProperty
|
||||||
if (source != null && isEnumCompanionPropertyWithEntryConflict(source, source.name ?: "")) return false
|
if (source != null && isEnumCompanionPropertyWithEntryConflict(source, source.name ?: "")) return Skip
|
||||||
}
|
}
|
||||||
|
|
||||||
addElementToShorten(qualifiedExpression)
|
return ShortenNow
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shortenElement(element: KtDotQualifiedExpression): KtElement {
|
override fun shortenElement(element: KtDotQualifiedExpression): KtElement {
|
||||||
|
|||||||
Reference in New Issue
Block a user