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>()
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<ShorteningVisitor<*>> = listOf(
ShortenTypesVisitor(file, elementFilter, failedToImportDescriptors),
ShortenThisExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
RemoveExplicitCompanionObjectReferenceVisitor(file, elementFilter, failedToImportDescriptors)
val processors: List<ShorteningProcessor<*>> = 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<TElement : KtElement>(
protected val file: KtFile,
protected val elementFilter: (PsiElement) -> FilterResult,
protected val failedToImportDescriptors: Set<DeclarationDescriptor>
private data class ElementToAnalyze<TElement>(val element: TElement, val level: Int)
private abstract class CollectElementsVisitor<TElement : KtElement>(
protected val elementFilter: (PsiElement) -> FilterResult
) : KtVisitorVoid() {
var options: Options = Options.DEFAULT
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>()
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<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) {
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<KtElement>) {
for (element in elementsToShorten) {
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
}
private class ShortenTypesVisitor(
private class ShortenTypesProcessor(
file: KtFile,
elementFilter: (PsiElement) -> FilterResult,
failedToImportDescriptors: Set<DeclarationDescriptor>
) : ShorteningVisitor<KtUserType>(file, elementFilter, failedToImportDescriptors) {
override fun visitUserType(userType: KtUserType) {
val filterResult = elementFilter(userType)
if (filterResult == FilterResult.SKIP) return
) : ShorteningProcessor<KtUserType>(file, failedToImportDescriptors) {
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) {
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<DeclarationDescriptor>
) : ShorteningVisitor<KtDotQualifiedExpression>(file, elementFilter, failedToImportDescriptors) {
) : ShorteningProcessor<KtDotQualifiedExpression>(file, failedToImportDescriptors) {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
val filterResult = elementFilter(expression)
if (filterResult == FilterResult.SKIP) return
protected open class MyVisitor(elementFilter: (PsiElement) -> FilterResult) : CollectElementsVisitor<KtDotQualifiedExpression>(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<DeclarationDescriptor>
) : 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<DeclarationDescriptor>
) : ShorteningVisitor<KtThisExpression>(file, elementFilter, failedToImportDescriptors) {
) : ShorteningProcessor<KtThisExpression>(file, failedToImportDescriptors) {
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 {
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<DeclarationDescriptor>
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
) : QualifiedExpressionShorteningProcessor(file, elementFilter, failedToImportDescriptors) {
private fun KtExpression.singleTarget(context: BindingContext): DeclarationDescriptor? {
return (getCalleeExpressionIfAny() as? KtReferenceExpression)?.targets(context)?.singleOrNull()