Additional safety check to not corrupt PSI tree (see KT-9122)
This commit is contained in:
@@ -22,9 +22,7 @@ import com.intellij.psi.PsiMember
|
|||||||
import com.intellij.psi.PsiPackage
|
import com.intellij.psi.PsiPackage
|
||||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.isOneSegmentFQN
|
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement
|
|
||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,33 +42,6 @@ public fun PsiElement.getKotlinFqName(): FqName? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace [[JetSimpleNameExpression]] (and its enclosing qualifier) with qualified element given by FqName
|
|
||||||
* Result is either the same as original element, or [[JetQualifiedExpression]], or [[JetUserType]]
|
|
||||||
* Note that FqName may not be empty
|
|
||||||
*/
|
|
||||||
fun JetSimpleNameExpression.changeQualifiedName(fqName: FqName): JetElement {
|
|
||||||
assert(!fqName.isRoot()) { "Can't set empty FqName for element $this" }
|
|
||||||
|
|
||||||
val shortName = fqName.shortName().asString()
|
|
||||||
val psiFactory = JetPsiFactory(this)
|
|
||||||
val fqNameBase = (getParent() as? JetCallExpression)?.let { parent ->
|
|
||||||
val callCopy = parent.copy() as JetCallExpression
|
|
||||||
callCopy.getCalleeExpression()!!.replace(psiFactory.createSimpleName(shortName)).getParent()!!.getText()
|
|
||||||
} ?: shortName
|
|
||||||
|
|
||||||
val text = if (!fqName.isOneSegmentFQN()) "${fqName.parent().asString()}.$fqNameBase" else fqNameBase
|
|
||||||
|
|
||||||
val elementToReplace = getQualifiedElement()
|
|
||||||
return when (elementToReplace) {
|
|
||||||
is JetUserType -> {
|
|
||||||
val typeText = "$text${elementToReplace.getTypeArgumentList()?.getText() ?: ""}"
|
|
||||||
elementToReplace.replace(psiFactory.createType(typeText).getTypeElement()!!)
|
|
||||||
}
|
|
||||||
else -> elementToReplace.replace(psiFactory.createExpression(text))
|
|
||||||
} as JetElement
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun FqName.isImported(importPath: ImportPath, skipAliasedImports: Boolean = true): Boolean {
|
public fun FqName.isImported(importPath: ImportPath, skipAliasedImports: Boolean = true): Boolean {
|
||||||
return when {
|
return when {
|
||||||
skipAliasedImports && importPath.hasAlias() -> false
|
skipAliasedImports && importPath.hasAlias() -> false
|
||||||
|
|||||||
+38
-7
@@ -21,22 +21,22 @@ import com.intellij.openapi.util.TextRange
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||||
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.changeQualifiedName
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
import org.jetbrains.kotlin.lexer.JetToken
|
import org.jetbrains.kotlin.lexer.JetToken
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens
|
import org.jetbrains.kotlin.lexer.JetTokens
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.isOneSegmentFQN
|
||||||
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
|
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|
||||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||||
|
|
||||||
class JetSimpleNameReference(expression: JetSimpleNameExpression) : JetSimpleReference<JetSimpleNameExpression>(expression) {
|
class JetSimpleNameReference(expression: JetSimpleNameExpression) : JetSimpleReference<JetSimpleNameExpression>(expression) {
|
||||||
@@ -125,9 +125,13 @@ class JetSimpleNameReference(expression: JetSimpleNameExpression) : JetSimpleRef
|
|||||||
element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode) } ?: expression
|
element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode) } ?: expression
|
||||||
|
|
||||||
fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING): PsiElement {
|
fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING): PsiElement {
|
||||||
if (fqName.isRoot()) return expression
|
val expression = expression
|
||||||
|
if (fqName.isRoot) return expression
|
||||||
|
|
||||||
val newExpression = expression.changeQualifiedName(fqName).getQualifiedElementSelector() as JetSimpleNameExpression
|
// not supported for infix calls and operators
|
||||||
|
if (expression !is JetNameReferenceExpression) return expression
|
||||||
|
|
||||||
|
val newExpression = expression.changeQualifiedName(fqName).getQualifiedElementSelector() as JetNameReferenceExpression
|
||||||
val newQualifiedElement = newExpression.getQualifiedElement()
|
val newQualifiedElement = newExpression.getQualifiedElement()
|
||||||
|
|
||||||
if (shorteningMode == ShorteningMode.NO_SHORTENING) return newExpression
|
if (shorteningMode == ShorteningMode.NO_SHORTENING) return newExpression
|
||||||
@@ -146,5 +150,32 @@ class JetSimpleNameReference(expression: JetSimpleNameExpression) : JetSimpleRef
|
|||||||
return newExpression
|
return newExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace [[JetNameReferenceExpression]] (and its enclosing qualifier) with qualified element given by FqName
|
||||||
|
* Result is either the same as original element, or [[JetQualifiedExpression]], or [[JetUserType]]
|
||||||
|
* Note that FqName may not be empty
|
||||||
|
*/
|
||||||
|
private fun JetNameReferenceExpression.changeQualifiedName(fqName: FqName): JetElement {
|
||||||
|
assert(!fqName.isRoot()) { "Can't set empty FqName for element $this" }
|
||||||
|
|
||||||
|
val shortName = fqName.shortName().asString()
|
||||||
|
val psiFactory = JetPsiFactory(this)
|
||||||
|
val fqNameBase = (getParent() as? JetCallExpression)?.let { parent ->
|
||||||
|
val callCopy = parent.copy() as JetCallExpression
|
||||||
|
callCopy.getCalleeExpression()!!.replace(psiFactory.createSimpleName(shortName)).getParent()!!.getText()
|
||||||
|
} ?: shortName
|
||||||
|
|
||||||
|
val text = if (!fqName.isOneSegmentFQN()) "${fqName.parent().asString()}.$fqNameBase" else fqNameBase
|
||||||
|
|
||||||
|
val elementToReplace = getQualifiedElement()
|
||||||
|
return when (elementToReplace) {
|
||||||
|
is JetUserType -> {
|
||||||
|
val typeText = "$text${elementToReplace.getTypeArgumentList()?.getText() ?: ""}"
|
||||||
|
elementToReplace.replace(psiFactory.createType(typeText).getTypeElement()!!)
|
||||||
|
}
|
||||||
|
else -> elementToReplace.replace(psiFactory.createExpression(text))
|
||||||
|
} as JetElement
|
||||||
|
}
|
||||||
|
|
||||||
override fun getCanonicalText(): String = expression.getText()
|
override fun getCanonicalText(): String = expression.getText()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user