Refactored to ensure use of correct options
This commit is contained in:
@@ -147,33 +147,34 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
val failedToImportDescriptors = LinkedHashSet<DeclarationDescriptor>()
|
val failedToImportDescriptors = LinkedHashSet<DeclarationDescriptor>()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Visitor order is important here so that enclosing elements are not shortened before their children are, e.g.
|
// Processors order is important here so that enclosing elements are not shortened before their children are, e.g.
|
||||||
// test.foo(this@A) -> foo(this)
|
// test.foo(this@A) -> foo(this)
|
||||||
val visitors: List<ShorteningVisitor<*>> = listOf(
|
val processors: List<ShorteningProcessor<*>> = listOf(
|
||||||
ShortenTypesVisitor(file, elementFilter, failedToImportDescriptors),
|
ShortenTypesProcessor(file, elementFilter, failedToImportDescriptors),
|
||||||
ShortenThisExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
|
ShortenThisExpressionsProcessor(file, elementFilter, failedToImportDescriptors),
|
||||||
ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
|
ShortenQualifiedExpressionsProcessor(file, elementFilter, failedToImportDescriptors),
|
||||||
RemoveExplicitCompanionObjectReferenceVisitor(file, elementFilter, failedToImportDescriptors)
|
RemoveExplicitCompanionObjectReferenceProcessor(file, elementFilter, failedToImportDescriptors)
|
||||||
)
|
)
|
||||||
|
|
||||||
// step 1: collect qualified elements to analyze (no resolve at this step)
|
// step 1: collect qualified elements to analyze (no resolve at this step)
|
||||||
|
val visitors = processors.map { it.collectElementsVisitor }
|
||||||
for (visitor in visitors) {
|
for (visitor in visitors) {
|
||||||
for (element in elementsToUse) {
|
for (element in elementsToUse) {
|
||||||
visitor.options = options(element)
|
visitor.options = options(element)
|
||||||
element.accept(visitor)
|
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
|
// 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)
|
val allElementsToAnalyze = visitors.flatMap { it.getElementsToAnalyze().map { it.element } }
|
||||||
visitors.forEach { it.analyzeCollectedElements(bindingContext) }
|
val bindingContext = file.getResolutionFacade().analyze(allElementsToAnalyze, BodyResolveMode.PARTIAL)
|
||||||
|
processors.forEach { it.analyzeCollectedElements(bindingContext) }
|
||||||
|
|
||||||
// step 3: shorten elements that can be shortened right now
|
// step 3: shorten elements that can be shortened right now
|
||||||
visitors.forEach { it.shortenElements(elementsToUse) }
|
processors.forEach { it.shortenElements(elementSetToUpdate = elementsToUse) }
|
||||||
|
|
||||||
// step 4: try to import descriptors needed to shorten other elements
|
// step 4: try to import descriptors needed to shorten other elements
|
||||||
val descriptorsToImport = visitors.flatMap { it.getDescriptorsToImport() }.toSet()
|
val descriptorsToImport = processors.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)
|
||||||
@@ -199,20 +200,15 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class ShorteningVisitor<TElement : KtElement>(
|
private data class ElementToAnalyze<TElement>(val element: TElement, val level: Int)
|
||||||
protected val file: KtFile,
|
|
||||||
protected val elementFilter: (PsiElement) -> FilterResult,
|
private abstract class CollectElementsVisitor<TElement : KtElement>(
|
||||||
protected val failedToImportDescriptors: Set<DeclarationDescriptor>
|
protected val elementFilter: (PsiElement) -> FilterResult
|
||||||
) : KtVisitorVoid() {
|
) : KtVisitorVoid() {
|
||||||
|
|
||||||
var options: Options = Options.DEFAULT
|
var options: Options = Options.DEFAULT
|
||||||
|
|
||||||
private data class ElementToAnalyze<TElement>(val element: TElement, val level: Int)
|
|
||||||
|
|
||||||
private val elementsToAnalyze = ArrayList<ElementToAnalyze<TElement>>()
|
private val elementsToAnalyze = ArrayList<ElementToAnalyze<TElement>>()
|
||||||
private val elementsToShorten = ArrayList<TElement>()
|
|
||||||
private val descriptorsToImport = LinkedHashSet<DeclarationDescriptor>()
|
|
||||||
|
|
||||||
protected val resolutionFacade = file.getResolutionFacade()
|
|
||||||
|
|
||||||
private var level = 0
|
private var level = 0
|
||||||
|
|
||||||
@@ -232,10 +228,31 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
elementsToAnalyze.add(ElementToAnalyze(element, level))
|
elementsToAnalyze.add(ElementToAnalyze(element, level))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
if (elementFilter(element) != FilterResult.SKIP) {
|
||||||
|
element.acceptChildren(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getElementsToAnalyze(): List<ElementToAnalyze<TElement>> = elementsToAnalyze
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class ShorteningProcessor<TElement : KtElement>(
|
||||||
|
protected val file: KtFile,
|
||||||
|
protected val failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
|
) {
|
||||||
|
protected val resolutionFacade = file.getResolutionFacade()
|
||||||
|
private val elementsToShorten = ArrayList<TElement>()
|
||||||
|
private val descriptorsToImport = LinkedHashSet<DeclarationDescriptor>()
|
||||||
|
|
||||||
|
abstract val collectElementsVisitor: CollectElementsVisitor<TElement>
|
||||||
|
|
||||||
fun analyzeCollectedElements(bindingContext: BindingContext) {
|
fun analyzeCollectedElements(bindingContext: BindingContext) {
|
||||||
|
val elements = collectElementsVisitor.getElementsToAnalyze()
|
||||||
|
|
||||||
var index = 0
|
var index = 0
|
||||||
while (index < elementsToAnalyze.size) {
|
while (index < elements.size) {
|
||||||
val (element, level) = elementsToAnalyze[index++]
|
val (element, level) = elements[index++]
|
||||||
|
|
||||||
val result = analyzeQualifiedElement(element, bindingContext)
|
val result = analyzeQualifiedElement(element, bindingContext)
|
||||||
|
|
||||||
@@ -266,7 +283,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
|
|
||||||
if (toBeShortened) {
|
if (toBeShortened) {
|
||||||
// we are going to shorten qualified element - we must skip all elements inside its qualifier
|
// we are going to shorten qualified element - we must skip all elements inside its qualifier
|
||||||
while (index < elementsToAnalyze.size && elementsToAnalyze[index].level > level) {
|
while (index < elements.size && elements[index].level > level) {
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,12 +309,6 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
|
|
||||||
protected abstract fun shortenElement(element: TElement): KtElement
|
protected abstract fun shortenElement(element: TElement): KtElement
|
||||||
|
|
||||||
override fun visitElement(element: PsiElement) {
|
|
||||||
if (elementFilter(element) != FilterResult.SKIP) {
|
|
||||||
element.acceptChildren(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun shortenElements(elementSetToUpdate: MutableSet<KtElement>) {
|
fun shortenElements(elementSetToUpdate: MutableSet<KtElement>) {
|
||||||
for (element in elementsToShorten) {
|
for (element in elementsToShorten) {
|
||||||
if (!element.isValid) continue
|
if (!element.isValid) continue
|
||||||
@@ -315,30 +326,33 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ShortenTypesVisitor(
|
private class ShortenTypesProcessor(
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementFilter: (PsiElement) -> FilterResult,
|
elementFilter: (PsiElement) -> FilterResult,
|
||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : ShorteningVisitor<KtUserType>(file, elementFilter, failedToImportDescriptors) {
|
) : ShorteningProcessor<KtUserType>(file, failedToImportDescriptors) {
|
||||||
override fun visitUserType(userType: KtUserType) {
|
|
||||||
val filterResult = elementFilter(userType)
|
|
||||||
if (filterResult == FilterResult.SKIP) return
|
|
||||||
|
|
||||||
userType.typeArgumentList?.accept(this)
|
override val collectElementsVisitor: CollectElementsVisitor<KtUserType> =
|
||||||
|
object : CollectElementsVisitor<KtUserType>(elementFilter) {
|
||||||
|
override fun visitUserType(userType: KtUserType) {
|
||||||
|
val filterResult = elementFilter(userType)
|
||||||
|
if (filterResult == FilterResult.SKIP) return
|
||||||
|
|
||||||
if (filterResult == FilterResult.PROCESS) {
|
userType.typeArgumentList?.accept(this)
|
||||||
addQualifiedElementToAnalyze(userType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// elements in qualifier must be under
|
if (filterResult == FilterResult.PROCESS) {
|
||||||
nextLevel()
|
addQualifiedElementToAnalyze(userType)
|
||||||
userType.qualifier?.accept(this)
|
}
|
||||||
prevLevel()
|
|
||||||
}
|
// elements in qualifier must be under
|
||||||
|
nextLevel()
|
||||||
|
userType.qualifier?.accept(this)
|
||||||
|
prevLevel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun analyzeQualifiedElement(element: KtUserType, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
override fun analyzeQualifiedElement(element: KtUserType, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
if (element.qualifier == null) return Skip
|
if (element.qualifier == null) return Skip
|
||||||
@@ -363,38 +377,44 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class QualifiedExpressionShorteningVisitor(
|
private abstract class QualifiedExpressionShorteningProcessor(
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementFilter: (PsiElement) -> FilterResult,
|
elementFilter: (PsiElement) -> FilterResult,
|
||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : ShorteningVisitor<KtDotQualifiedExpression>(file, elementFilter, failedToImportDescriptors) {
|
) : ShorteningProcessor<KtDotQualifiedExpression>(file, failedToImportDescriptors) {
|
||||||
|
|
||||||
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
|
protected open class MyVisitor(elementFilter: (PsiElement) -> FilterResult) : CollectElementsVisitor<KtDotQualifiedExpression>(elementFilter) {
|
||||||
val filterResult = elementFilter(expression)
|
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
|
||||||
if (filterResult == FilterResult.SKIP) return
|
val filterResult = elementFilter(expression)
|
||||||
|
if (filterResult == FilterResult.SKIP) return
|
||||||
|
|
||||||
expression.selectorExpression?.acceptChildren(this)
|
expression.selectorExpression?.acceptChildren(this)
|
||||||
|
|
||||||
if (filterResult == FilterResult.PROCESS) {
|
if (filterResult == FilterResult.PROCESS) {
|
||||||
addQualifiedElementToAnalyze(expression)
|
addQualifiedElementToAnalyze(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
// elements in receiver must be under
|
||||||
|
nextLevel()
|
||||||
|
expression.receiverExpression.accept(this)
|
||||||
|
prevLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// elements in receiver must be under
|
|
||||||
nextLevel()
|
|
||||||
expression.receiverExpression.accept(this)
|
|
||||||
prevLevel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val collectElementsVisitor = MyVisitor(elementFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ShortenQualifiedExpressionsVisitor(
|
private class ShortenQualifiedExpressionsProcessor(
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementFilter: (PsiElement) -> FilterResult,
|
elementFilter: (PsiElement) -> FilterResult,
|
||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
|
) : QualifiedExpressionShorteningProcessor(file, elementFilter, failedToImportDescriptors) {
|
||||||
|
|
||||||
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
|
override val collectElementsVisitor = object : MyVisitor(elementFilter) {
|
||||||
if (expression.receiverExpression is KtThisExpression && !options.removeThis) return
|
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
|
||||||
super.visitDotQualifiedExpression(expression)
|
if (expression.receiverExpression is KtThisExpression && !options.removeThis) return
|
||||||
|
super.visitDotQualifiedExpression(expression)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun analyzeQualifiedElement(element: KtDotQualifiedExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
override fun analyzeQualifiedElement(element: KtDotQualifiedExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
@@ -434,7 +454,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
return when {
|
return when {
|
||||||
targetsMatch -> ShortenNow
|
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
|
||||||
targetsWhenShort.any { it !is ClassDescriptor && it !is PackageViewDescriptor } -> Skip
|
targetsWhenShort.any { it !is ClassDescriptor && it !is PackageViewDescriptor } -> Skip
|
||||||
|
|
||||||
else -> ImportDescriptors(targets)
|
else -> ImportDescriptors(targets)
|
||||||
@@ -456,13 +476,23 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ShortenThisExpressionsVisitor(
|
private class ShortenThisExpressionsProcessor(
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementFilter: (PsiElement) -> FilterResult,
|
elementFilter: (PsiElement) -> FilterResult,
|
||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : ShorteningVisitor<KtThisExpression>(file, elementFilter, failedToImportDescriptors) {
|
) : ShorteningProcessor<KtThisExpression>(file, failedToImportDescriptors) {
|
||||||
|
|
||||||
private val simpleThis = KtPsiFactory(file).createExpression("this") as KtThisExpression
|
private val simpleThis = KtPsiFactory(file).createExpression("this") as KtThisExpression
|
||||||
|
|
||||||
|
override val collectElementsVisitor: CollectElementsVisitor<KtThisExpression> =
|
||||||
|
object : CollectElementsVisitor<KtThisExpression>(elementFilter) {
|
||||||
|
override fun visitThisExpression(expression: KtThisExpression) {
|
||||||
|
if (options.removeThisLabels && elementFilter(expression) == FilterResult.PROCESS && expression.getTargetLabel() != null) {
|
||||||
|
addQualifiedElementToAnalyze(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun analyzeQualifiedElement(element: KtThisExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
override fun analyzeQualifiedElement(element: KtThisExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult {
|
||||||
val targetBefore = element.instanceReference.targets(bindingContext).singleOrNull() ?: return Skip
|
val targetBefore = element.instanceReference.targets(bindingContext).singleOrNull() ?: return Skip
|
||||||
val scope = element.getResolutionScope(bindingContext, resolutionFacade)
|
val scope = element.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
@@ -471,22 +501,16 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
|
|||||||
return if (targetBefore == targetAfter) ShortenNow else Skip
|
return if (targetBefore == targetAfter) ShortenNow else Skip
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitThisExpression(expression: KtThisExpression) {
|
|
||||||
if (options.removeThisLabels && elementFilter(expression) == FilterResult.PROCESS && expression.getTargetLabel() != null) {
|
|
||||||
addQualifiedElementToAnalyze(expression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun shortenElement(element: KtThisExpression): KtElement {
|
override fun shortenElement(element: KtThisExpression): KtElement {
|
||||||
return element.replace(simpleThis) as KtElement
|
return element.replace(simpleThis) as KtElement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RemoveExplicitCompanionObjectReferenceVisitor(
|
private class RemoveExplicitCompanionObjectReferenceProcessor(
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementFilter: (PsiElement) -> FilterResult,
|
elementFilter: (PsiElement) -> FilterResult,
|
||||||
failedToImportDescriptors: Set<DeclarationDescriptor>
|
failedToImportDescriptors: Set<DeclarationDescriptor>
|
||||||
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
|
) : QualifiedExpressionShorteningProcessor(file, elementFilter, failedToImportDescriptors) {
|
||||||
|
|
||||||
private fun KtExpression.singleTarget(context: BindingContext): DeclarationDescriptor? {
|
private fun KtExpression.singleTarget(context: BindingContext): DeclarationDescriptor? {
|
||||||
return (getCalleeExpressionIfAny() as? KtReferenceExpression)?.targets(context)?.singleOrNull()
|
return (getCalleeExpressionIfAny() as? KtReferenceExpression)?.targets(context)?.singleOrNull()
|
||||||
|
|||||||
Reference in New Issue
Block a user