From 1d24fc3a7be9a169a4ee4e87e73bf46fd413646c Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 8 Sep 2016 02:44:35 +0300 Subject: [PATCH] Refactored to ensure use of correct options --- .../kotlin/idea/core/ShortenReferences.kt | 172 ++++++++++-------- 1 file changed, 98 insertions(+), 74 deletions(-) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt index d56b96b10c3..2301e7ac648 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt @@ -147,33 +147,34 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT val failedToImportDescriptors = LinkedHashSet() 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) - val visitors: List> = listOf( - ShortenTypesVisitor(file, elementFilter, failedToImportDescriptors), - ShortenThisExpressionsVisitor(file, elementFilter, failedToImportDescriptors), - ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors), - RemoveExplicitCompanionObjectReferenceVisitor(file, elementFilter, failedToImportDescriptors) + val processors: List> = listOf( + ShortenTypesProcessor(file, elementFilter, failedToImportDescriptors), + ShortenThisExpressionsProcessor(file, elementFilter, failedToImportDescriptors), + ShortenQualifiedExpressionsProcessor(file, elementFilter, failedToImportDescriptors), + RemoveExplicitCompanionObjectReferenceProcessor(file, elementFilter, failedToImportDescriptors) ) // step 1: collect qualified elements to analyze (no resolve at this step) + val visitors = processors.map { it.collectElementsVisitor } 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) } + val allElementsToAnalyze = visitors.flatMap { it.getElementsToAnalyze().map { it.element } } + val bindingContext = file.getResolutionFacade().analyze(allElementsToAnalyze, BodyResolveMode.PARTIAL) + processors.forEach { it.analyzeCollectedElements(bindingContext) } // 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 - val descriptorsToImport = visitors.flatMap { it.getDescriptorsToImport() }.toSet() + val descriptorsToImport = processors.flatMap { it.getDescriptorsToImport() }.toSet() var anyChange = false for (descriptor in descriptorsToImport) { assert(descriptor !in failedToImportDescriptors) @@ -199,20 +200,15 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT } } - private abstract class ShorteningVisitor( - protected val file: KtFile, - protected val elementFilter: (PsiElement) -> FilterResult, - protected val failedToImportDescriptors: Set + private data class ElementToAnalyze(val element: TElement, val level: Int) + + private abstract class CollectElementsVisitor( + protected val elementFilter: (PsiElement) -> FilterResult ) : KtVisitorVoid() { + var options: Options = Options.DEFAULT - private data class ElementToAnalyze(val element: TElement, val level: Int) - private val elementsToAnalyze = ArrayList>() - private val elementsToShorten = ArrayList() - private val descriptorsToImport = LinkedHashSet() - - protected val resolutionFacade = file.getResolutionFacade() private var level = 0 @@ -232,10 +228,31 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT elementsToAnalyze.add(ElementToAnalyze(element, level)) } + override fun visitElement(element: PsiElement) { + if (elementFilter(element) != FilterResult.SKIP) { + element.acceptChildren(this) + } + } + + fun getElementsToAnalyze(): List> = elementsToAnalyze + } + + private abstract class ShorteningProcessor( + protected val file: KtFile, + protected val failedToImportDescriptors: Set + ) { + protected val resolutionFacade = file.getResolutionFacade() + private val elementsToShorten = ArrayList() + private val descriptorsToImport = LinkedHashSet() + + abstract val collectElementsVisitor: CollectElementsVisitor + fun analyzeCollectedElements(bindingContext: BindingContext) { + val elements = collectElementsVisitor.getElementsToAnalyze() + var index = 0 - while (index < elementsToAnalyze.size) { - val (element, level) = elementsToAnalyze[index++] + while (index < elements.size) { + val (element, level) = elements[index++] val result = analyzeQualifiedElement(element, bindingContext) @@ -266,7 +283,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT 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) { + while (index < elements.size && elements[index].level > level) { index++ } } @@ -292,12 +309,6 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT protected abstract fun shortenElement(element: TElement): KtElement - override fun visitElement(element: PsiElement) { - if (elementFilter(element) != FilterResult.SKIP) { - element.acceptChildren(this) - } - } - fun shortenElements(elementSetToUpdate: MutableSet) { for (element in elementsToShorten) { if (!element.isValid) continue @@ -315,30 +326,33 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT } } - fun getElementsToAnalyze(): Collection = elementsToAnalyze.map { it.element } fun getDescriptorsToImport(): Set = descriptorsToImport } - private class ShortenTypesVisitor( + private class ShortenTypesProcessor( file: KtFile, elementFilter: (PsiElement) -> FilterResult, failedToImportDescriptors: Set - ) : ShorteningVisitor(file, elementFilter, failedToImportDescriptors) { - override fun visitUserType(userType: KtUserType) { - val filterResult = elementFilter(userType) - if (filterResult == FilterResult.SKIP) return + ) : ShorteningProcessor(file, failedToImportDescriptors) { - userType.typeArgumentList?.accept(this) + override val collectElementsVisitor: CollectElementsVisitor = + object : CollectElementsVisitor(elementFilter) { + override fun visitUserType(userType: KtUserType) { + val filterResult = elementFilter(userType) + if (filterResult == FilterResult.SKIP) return - if (filterResult == FilterResult.PROCESS) { - addQualifiedElementToAnalyze(userType) - } + userType.typeArgumentList?.accept(this) - // elements in qualifier must be under - nextLevel() - userType.qualifier?.accept(this) - prevLevel() - } + if (filterResult == FilterResult.PROCESS) { + addQualifiedElementToAnalyze(userType) + } + + // elements in qualifier must be under + nextLevel() + userType.qualifier?.accept(this) + prevLevel() + } + } override fun analyzeQualifiedElement(element: KtUserType, bindingContext: BindingContext): AnalyzeQualifiedElementResult { 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, elementFilter: (PsiElement) -> FilterResult, failedToImportDescriptors: Set - ) : ShorteningVisitor(file, elementFilter, failedToImportDescriptors) { + ) : ShorteningProcessor(file, failedToImportDescriptors) { - override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { - val filterResult = elementFilter(expression) - if (filterResult == FilterResult.SKIP) return + protected open class MyVisitor(elementFilter: (PsiElement) -> FilterResult) : CollectElementsVisitor(elementFilter) { + override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { + val filterResult = elementFilter(expression) + if (filterResult == FilterResult.SKIP) return - expression.selectorExpression?.acceptChildren(this) + expression.selectorExpression?.acceptChildren(this) - if (filterResult == FilterResult.PROCESS) { - addQualifiedElementToAnalyze(expression) + if (filterResult == FilterResult.PROCESS) { + 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, elementFilter: (PsiElement) -> FilterResult, failedToImportDescriptors: Set - ) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) { + ) : QualifiedExpressionShorteningProcessor(file, elementFilter, failedToImportDescriptors) { - override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { - if (expression.receiverExpression is KtThisExpression && !options.removeThis) return - super.visitDotQualifiedExpression(expression) + override val collectElementsVisitor = object : MyVisitor(elementFilter) { + override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { + if (expression.receiverExpression is KtThisExpression && !options.removeThis) return + super.visitDotQualifiedExpression(expression) + } } override fun analyzeQualifiedElement(element: KtDotQualifiedExpression, bindingContext: BindingContext): AnalyzeQualifiedElementResult { @@ -434,7 +454,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT 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 targetsWhenShort.any { it !is ClassDescriptor && it !is PackageViewDescriptor } -> Skip else -> ImportDescriptors(targets) @@ -456,13 +476,23 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT } } - private class ShortenThisExpressionsVisitor( + private class ShortenThisExpressionsProcessor( file: KtFile, elementFilter: (PsiElement) -> FilterResult, failedToImportDescriptors: Set - ) : ShorteningVisitor(file, elementFilter, failedToImportDescriptors) { + ) : ShorteningProcessor(file, failedToImportDescriptors) { + private val simpleThis = KtPsiFactory(file).createExpression("this") as KtThisExpression + override val collectElementsVisitor: CollectElementsVisitor = + object : CollectElementsVisitor(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 { val targetBefore = element.instanceReference.targets(bindingContext).singleOrNull() ?: return Skip 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 } - override fun visitThisExpression(expression: KtThisExpression) { - if (options.removeThisLabels && elementFilter(expression) == FilterResult.PROCESS && expression.getTargetLabel() != null) { - addQualifiedElementToAnalyze(expression) - } - } - override fun shortenElement(element: KtThisExpression): KtElement { return element.replace(simpleThis) as KtElement } } - private class RemoveExplicitCompanionObjectReferenceVisitor( + private class RemoveExplicitCompanionObjectReferenceProcessor( file: KtFile, elementFilter: (PsiElement) -> FilterResult, failedToImportDescriptors: Set - ) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) { + ) : QualifiedExpressionShorteningProcessor(file, elementFilter, failedToImportDescriptors) { private fun KtExpression.singleTarget(context: BindingContext): DeclarationDescriptor? { return (getCalleeExpressionIfAny() as? KtReferenceExpression)?.targets(context)?.singleOrNull()