Shorten References: Extract common superclass for type- and expression-processing visitors

This commit is contained in:
Alexey Sedunov
2014-10-16 15:16:46 +04:00
parent f7e947e912
commit f3a219f062
@@ -38,6 +38,7 @@ import org.jetbrains.jet.lang.psi.psiUtil.getQualifiedElementSelector
import java.util.Collections import java.util.Collections
import org.jetbrains.jet.analyzer.analyzeInContext import org.jetbrains.jet.analyzer.analyzeInContext
import org.jetbrains.jet.lang.resolve.calls.callUtil.getCalleeExpressionIfAny import org.jetbrains.jet.lang.resolve.calls.callUtil.getCalleeExpressionIfAny
import java.util.LinkedHashSet
public object ShortenReferences { public object ShortenReferences {
public fun process(element: JetElement) { public fun process(element: JetElement) {
@@ -107,35 +108,31 @@ public object ShortenReferences {
// first resolve all qualified references - optimization // first resolve all qualified references - optimization
val referenceToContext = JetFileReferencesResolver.resolve(file, fileElements, resolveShortNames = false) val referenceToContext = JetFileReferencesResolver.resolve(file, fileElements, resolveShortNames = false)
val shortenTypesVisitor = ShortenTypesVisitor(file, elementFilter, referenceToContext) processElements(fileElements, ShortenTypesVisitor(file, elementFilter, referenceToContext))
processElements(fileElements, shortenTypesVisitor)
shortenTypesVisitor.finish()
processElements(fileElements, ShortenQualifiedExpressionsVisitor(file, elementFilter, referenceToContext)) processElements(fileElements, ShortenQualifiedExpressionsVisitor(file, elementFilter, referenceToContext))
} }
} }
private fun processElements(elements: Iterable<JetElement>, visitor: JetVisitorVoid) { private fun processElements(elements: Iterable<JetElement>, visitor: ShorteningVisitor<*>) {
for (element in elements) { for (element in elements) {
element.accept(visitor) element.accept(visitor)
} }
visitor.finish()
} }
private class ShortenTypesVisitor(val file: JetFile, private abstract class ShorteningVisitor<T : JetElement>(
val elementFilter: (PsiElement) -> FilterResult, val file: JetFile,
val resolveMap: Map<JetReferenceExpression, BindingContext>) : JetVisitorVoid() { val elementFilter: (PsiElement) -> FilterResult,
private val resolveSession : ResolveSessionForBodies val resolveMap: Map<JetReferenceExpression, BindingContext>) : JetVisitorVoid() {
protected val resolveSession: ResolveSessionForBodies
get() = file.getLazyResolveSession() get() = file.getLazyResolveSession()
private val typesToShorten = ArrayList<JetUserType>() protected val elementsToShorten: MutableSet<T> = LinkedHashSet()
public fun finish() { protected fun bindingContext(element: JetElement): BindingContext
for (userType in typesToShorten) { = resolveMap[element] ?: resolveSession.resolveToElement(element)
shortenType(userType)
}
}
private fun bindingContext(expression: JetReferenceExpression): BindingContext = resolveMap[expression]!! protected abstract fun getShortenedElement(element: T): JetElement?
override fun visitElement(element: PsiElement) { override fun visitElement(element: PsiElement) {
if (elementFilter(element) != FilterResult.SKIP) { if (elementFilter(element) != FilterResult.SKIP) {
@@ -143,20 +140,18 @@ public object ShortenReferences {
} }
} }
override fun visitUserType(userType: JetUserType) { public fun finish() {
val filterResult = elementFilter(userType) for (element in elementsToShorten) {
if (filterResult == FilterResult.SKIP) return getShortenedElement(element)?.let { element.replace(it) }
userType.getTypeArgumentList()?.accept(this)
if (filterResult == FilterResult.PROCESS && canShortenType(userType)) {
typesToShorten.add(userType)
}
else{
userType.getQualifier()?.accept(this)
} }
} }
}
private class ShortenTypesVisitor(
file: JetFile,
elementFilter: (PsiElement) -> FilterResult,
resolveMap: Map<JetReferenceExpression, BindingContext>
) : ShorteningVisitor<JetUserType>(file, elementFilter, resolveMap) {
private fun canShortenType(userType: JetUserType): Boolean { private fun canShortenType(userType: JetUserType): Boolean {
if (userType.getQualifier() == null) return false if (userType.getQualifier() == null) return false
val referenceExpression = userType.getReferenceExpression() val referenceExpression = userType.getReferenceExpression()
@@ -186,24 +181,36 @@ public object ShortenReferences {
} }
} }
private fun shortenType(userType: JetUserType) { override fun visitUserType(userType: JetUserType) {
val referenceExpression = userType.getReferenceExpression() val filterResult = elementFilter(userType)
if (referenceExpression == null) return if (filterResult == FilterResult.SKIP) return
val typeArgumentList = userType.getTypeArgumentList()
userType.getTypeArgumentList()?.accept(this)
if (filterResult == FilterResult.PROCESS && canShortenType(userType)) {
elementsToShorten.add(userType)
}
else{
userType.getQualifier()?.accept(this)
}
}
override fun getShortenedElement(element: JetUserType): JetElement? {
val referenceExpression = element.getReferenceExpression() ?: return null
val typeArgumentList = element.getTypeArgumentList()
val text = referenceExpression.getText() + (if (typeArgumentList != null) typeArgumentList.getText() else "") val text = referenceExpression.getText() + (if (typeArgumentList != null) typeArgumentList.getText() else "")
val newUserType = JetPsiFactory(userType).createType(text).getTypeElement()!! return JetPsiFactory(element).createType(text).getTypeElement()!!
userType.replace(newUserType)
} }
} }
private class ShortenQualifiedExpressionsVisitor(val file: JetFile, private class ShortenQualifiedExpressionsVisitor(
val elementFilter: (PsiElement) -> FilterResult, file: JetFile,
val resolveMap: Map<JetReferenceExpression, BindingContext>) : JetVisitorVoid() { elementFilter: (PsiElement) -> FilterResult,
private val resolveSession : ResolveSessionForBodies resolveMap: Map<JetReferenceExpression, BindingContext>
get() = file.getLazyResolveSession() ) : ShorteningVisitor<JetQualifiedExpression>(file, elementFilter, resolveMap) {
private fun adjustDescriptor(it: DeclarationDescriptor): DeclarationDescriptor {
private fun bindingContext(element: JetElement): BindingContext return (it as? ConstructorDescriptor)?.getContainingDeclaration() ?: it
= resolveMap[element] ?: resolveSession.resolveToElement(element) // binding context can be absent in the map if some references have been shortened already }
private fun JetReferenceExpression.getTargets(context: BindingContext): Collection<DeclarationDescriptor> { private fun JetReferenceExpression.getTargets(context: BindingContext): Collection<DeclarationDescriptor> {
return context[BindingContext.REFERENCE_TARGET, this]?.let { Collections.singletonList(adjustDescriptor(it)) } return context[BindingContext.REFERENCE_TARGET, this]?.let { Collections.singletonList(adjustDescriptor(it)) }
@@ -211,42 +218,22 @@ public object ShortenReferences {
?: Collections.emptyList() ?: Collections.emptyList()
} }
private fun adjustDescriptor(it: DeclarationDescriptor): DeclarationDescriptor { private fun canShorten(qualifiedExpression: JetDotQualifiedExpression): Boolean {
return (it as? ConstructorDescriptor)?.getContainingDeclaration() ?: it
}
override fun visitElement(element: PsiElement) {
if (elementFilter(element) != FilterResult.SKIP) {
acceptChildren(element)
}
}
override fun visitDotQualifiedExpression(expression: JetDotQualifiedExpression) {
val filterResult = elementFilter(expression)
val resultElement = if (filterResult == FilterResult.PROCESS) processDotQualifiedExpression(expression) else expression
if (filterResult != FilterResult.SKIP) {
acceptChildren(resultElement)
}
}
private fun JetQualifiedExpression.doShorten(): JetExpression = replace(getSelectorExpression()!!) as JetExpression
private fun processDotQualifiedExpression(qualifiedExpression: JetDotQualifiedExpression): PsiElement {
val context = bindingContext(qualifiedExpression) val context = bindingContext(qualifiedExpression)
if (context[BindingContext.QUALIFIER, qualifiedExpression.getReceiverExpression()] == null) return qualifiedExpression if (context[BindingContext.QUALIFIER, qualifiedExpression.getReceiverExpression()] == null) return false
if (PsiTreeUtil.getParentOfType( if (PsiTreeUtil.getParentOfType(
qualifiedExpression, qualifiedExpression,
javaClass<JetImportDirective>(), javaClass<JetPackageDirective>()) != null) return qualifiedExpression javaClass<JetImportDirective>(), javaClass<JetPackageDirective>()) != null) return false
val selector = qualifiedExpression.getSelectorExpression() ?: return qualifiedExpression val selector = qualifiedExpression.getSelectorExpression() ?: return false
val callee = selector.getCalleeExpressionIfAny() as? JetReferenceExpression ?: return qualifiedExpression val callee = selector.getCalleeExpressionIfAny() as? JetReferenceExpression ?: return false
val targetBefore = callee.getTargets(context).singleOrNull() ?: return qualifiedExpression val targetBefore = callee.getTargets(context).singleOrNull() ?: return false
val isClassMember = targetBefore.getContainingDeclaration() is ClassDescriptor val isClassMember = targetBefore.getContainingDeclaration() is ClassDescriptor
val isClassOrPackage = targetBefore is ClassDescriptor || targetBefore is PackageViewDescriptor val isClassOrPackage = targetBefore is ClassDescriptor || targetBefore is PackageViewDescriptor
val scope = context[BindingContext.RESOLUTION_SCOPE, qualifiedExpression] ?: return qualifiedExpression val scope = context[BindingContext.RESOLUTION_SCOPE, qualifiedExpression] ?: return false
val selectorCopy = selector.copy() as JetReferenceExpression val selectorCopy = selector.copy() as JetReferenceExpression
val newContext = selectorCopy.analyzeInContext(scope) val newContext = selectorCopy.analyzeInContext(scope)
val targetsAfter = (selectorCopy.getCalleeExpressionIfAny() as JetReferenceExpression).getTargets(newContext) val targetsAfter = (selectorCopy.getCalleeExpressionIfAny() as JetReferenceExpression).getTargets(newContext)
@@ -255,28 +242,32 @@ public object ShortenReferences {
0 -> { 0 -> {
if (!isClassMember && isClassOrPackage) { if (!isClassMember && isClassOrPackage) {
addImport(targetBefore, file) addImport(targetBefore, file)
qualifiedExpression.doShorten() return true
}
else {
qualifiedExpression
} }
false
} }
1 -> if (targetBefore == targetsAfter.first()) qualifiedExpression.doShorten() else qualifiedExpression 1 -> targetBefore == targetsAfter.first()
else -> qualifiedExpression else -> false
} }
} }
// we do not use standard PsiElement.acceptChildren because it won't work correctly if the element is replaced by the visitor override fun visitDotQualifiedExpression(expression: JetDotQualifiedExpression) {
private fun acceptChildren(element: PsiElement) { val filterResult = elementFilter(expression)
var child = element.getFirstChild() if (filterResult == FilterResult.SKIP) return
while(child != null) {
val nextChild = child!!.getNextSibling() expression.getSelectorExpression()?.acceptChildren(this)
child!!.accept(this)
child = nextChild if (filterResult == FilterResult.PROCESS && canShorten(expression)) {
elementsToShorten.add(expression)
}
else {
expression.getReceiverExpression().accept(this)
} }
} }
override fun getShortenedElement(element: JetQualifiedExpression): JetElement = element.getSelectorExpression()!!
} }
private fun DeclarationDescriptor.asString() private fun DeclarationDescriptor.asString()