Refactored to ensure use of correct options

This commit is contained in:
Valentin Kipyatkov
2016-09-08 02:44:35 +03:00
parent 32e4a033db
commit 1d24fc3a7b
@@ -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()