Rename: Support import aliases

#KT-4379 Fixed
This commit is contained in:
Alexey Sedunov
2017-04-25 20:27:58 +03:00
parent 5d87276cff
commit f2bb6e4dac
22 changed files with 156 additions and 10 deletions
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.psi.search.LocalSearchScope
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
@@ -31,6 +33,9 @@ class KtImportAlias : KtElementImplStub<KotlinImportAliasStub>, PsiNameIdentifie
return visitor.visitImportAlias(this, data)
}
val importDirective: KtImportDirective?
get() = parent as? KtImportDirective
override fun getName() = stub?.getName() ?: nameIdentifier?.text
override fun setName(name: String): PsiElement {
@@ -39,4 +44,8 @@ class KtImportAlias : KtElementImplStub<KotlinImportAliasStub>, PsiNameIdentifie
}
override fun getNameIdentifier(): PsiElement? = findChildByType(KtTokens.IDENTIFIER)
override fun getTextOffset() = nameIdentifier?.textOffset ?: startOffset
override fun getUseScope() = LocalSearchScope(containingFile)
}
@@ -23,6 +23,8 @@ import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.copied
@@ -43,6 +45,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.expressions.OperatorConventions
class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) {
@@ -235,4 +239,17 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
return listOf(element.getReferencedNameAsName())
}
fun getImportAlias(): KtImportAlias? {
val element = element
val name = element.getReferencedName()
val file = element.containingKtFile
val importDirective = file.findImportByAlias(name) ?: return null
val fqName = importDirective.importedFqName ?: return null
val importedDescriptors = file.resolveImportReference(fqName)
if (getTargetDescriptors(element.analyze(BodyResolveMode.PARTIAL)).any { it.getImportableDescriptor() in importedDescriptors }) {
return importDirective.alias
}
return null
}
}
@@ -20,8 +20,11 @@ import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
import org.jetbrains.kotlin.idea.kdoc.KDocReference
@@ -35,6 +38,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.constant
@@ -88,18 +92,22 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
}
}
val targets = unwrappedTargets
val element = element
val manager = candidateTarget.manager
if (targets.any { manager.areElementsEquivalent(unwrappedCandidate, it) }) {
return true
if (candidateTarget is KtImportAlias && element is KtSimpleNameExpression && element.getReferencedName() == candidateTarget.name) {
val importDirective = candidateTarget.importDirective ?: return false
val importedFqName = importDirective.importedFqName ?: return false
val importedDescriptors = importDirective.containingKtFile.resolveImportReference(importedFqName)
val importableTargets = unwrappedTargets.mapNotNull {
if (it is KtConstructor<*>) it.containingClassOrObject else it
}
return importedDescriptors.any { (it as? DeclarationDescriptorWithSource)?.source?.getPsi() in importableTargets }
}
val element = element
if (element is KtLabelReferenceExpression) {
val labelParent = (element.parent as? KtContainerNode)?.parent
when (labelParent) {
is KtReturnExpression -> targets.forEach {
is KtReturnExpression -> unwrappedTargets.forEach {
if (it !is KtFunctionLiteral && !(it is KtNamedFunction && it.name.isNullOrEmpty())) return@forEach
it as KtFunction
@@ -110,13 +118,19 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
val calleeReference = it.getCalleeByLambdaArgument()?.mainReference ?: return@forEach
if (calleeReference.matchesTarget(candidateTarget)) return true
}
is KtBreakExpression, is KtContinueExpression -> targets.forEach {
is KtBreakExpression, is KtContinueExpression -> unwrappedTargets.forEach {
val labeledExpression = (it as? KtExpression)?.getLabeledParent(element.getReferencedName()) ?: return@forEach
if (candidateTarget == labeledExpression) return true
}
}
}
val targets = unwrappedTargets
val manager = candidateTarget.manager
if (targets.any { manager.areElementsEquivalent(unwrappedCandidate, it) }) {
return true
}
if (this is KtReference) {
return targets.any {
it.isConstructorOf(unwrappedCandidate)
+1
View File
@@ -498,6 +498,7 @@
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameBackingFieldReferenceHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameClassByCompanionObjectShortReferenceHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameByLabeledReferenceInLambdaArgumentHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameImportAliasByReferenceHandler"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableRenamerFactory"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableRenamerFactoryForJavaClass"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableInJavaRenamerFactory"/>
@@ -125,6 +125,7 @@ class KotlinElementDescriptionProvider : ElementDescriptionProvider {
is KtDestructuringDeclarationEntry -> "variable"
is KtTypeAlias -> "type alias"
is KtLabeledExpression -> "label"
is KtImportAlias -> "import alias"
is RenameJavaSyntheticPropertyHandler.SyntheticPropertyWrapper -> "property"
is KtLightClassForFacade -> "facade class"
is RenameKotlinPropertyProcessor.PropertyMethodWrapper -> "property accessor"
@@ -80,6 +80,7 @@ class KotlinFindUsagesProvider : FindUsagesProvider {
return funDescription + (element.containerDescription?.let { " of $it" } ?: "")
}
is KtLabeledExpression -> element.getLabelName() ?: ""
is KtImportAlias -> element.getName() ?: ""
is KtLightElement<*, *> -> element.kotlinOrigin?.let { getDescriptiveName(it) } ?: ""
else -> ""
}
@@ -71,7 +71,7 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
return grandparent is KtCatchClause || grandparent is KtFunctionLiteral
}
}
is KtLabeledExpression -> return true
is KtLabeledExpression, is KtImportAlias -> return true
}
return false
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class RenameImportAliasByReferenceHandler : AbstractReferenceSubstitutionRenameHandler(VariableInplaceRenameHandler()) {
override fun getElementToRename(dataContext: DataContext): PsiElement? {
val refExpr = getReferenceExpression(dataContext) as? KtSimpleNameExpression ?: return null
return refExpr.mainReference.getImportAlias()
}
}
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -38,6 +39,7 @@ import org.jetbrains.kotlin.resolve.source.getPsi
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtilExtender {
companion object {
val DO_NOT_UNWRAP_LABELED_EXPRESSION = 0x100
val BYPASS_IMPORT_ALIAS = 0x200
// Place caret after the open curly brace in lambda for generated 'it'
fun findLambdaOpenLBraceForGeneratedIt(ref: PsiReference): PsiElement? {
@@ -69,9 +71,9 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtil
override fun getAdditionalDefinitionSearchFlags() = 0
override fun getAdditionalReferenceSearchFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION
override fun getAdditionalReferenceSearchFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION or BYPASS_IMPORT_ALIAS
override fun getAllAdditionalFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION
override fun getAllAdditionalFlags() = additionalDefinitionSearchFlags + additionalReferenceSearchFlags
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
@@ -84,6 +86,10 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtil
return refTarget
}
if (!BitUtil.isSet(flags, BYPASS_IMPORT_ALIAS)) {
(ref.element as? KtSimpleNameExpression)?.mainReference?.getImportAlias()?.let { return it }
}
// prefer destructing declaration entry to its target if element name is accepted
if (ref is KtDestructuringDeclarationReference && BitUtil.isSet(flags, TargetElementUtil.ELEMENT_NAME_ACCEPTED)) {
return ref.element
@@ -0,0 +1,3 @@
package foo
class Foo
@@ -0,0 +1,5 @@
import foo.Foo as Baz
fun test() {
val bar: Baz = Baz()
}
@@ -0,0 +1,3 @@
package foo
class Foo
@@ -0,0 +1,5 @@
import foo.Foo as /*rename*/Bar
fun test() {
val bar: Bar = Bar()
}
@@ -0,0 +1,5 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test.kt",
"newName": "Baz"
}
@@ -0,0 +1,3 @@
package foo
class Foo
@@ -0,0 +1,5 @@
import foo.Foo as Baz
fun test() {
val bar: Baz = Baz()
}
@@ -0,0 +1,3 @@
package foo
class Foo
@@ -0,0 +1,5 @@
import foo.Foo as Bar
fun test() {
val bar: /*rename*/Bar = Bar()
}
@@ -0,0 +1,5 @@
{
"type": "AUTO_DETECT",
"mainFile": "test.kt",
"newName": "Baz"
}
+7
View File
@@ -0,0 +1,7 @@
import Foo as <info descr="null">~Bar</info>
class Foo
fun test() {
val bar: <info descr="null">Bar</info> = <info descr="null">Bar</info>()
}
@@ -36,6 +36,12 @@ public class UsageHighlightingTestGenerated extends AbstractUsageHighlightingTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/usageHighlighter"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("importAlias.kt")
public void testImportAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/usageHighlighter/importAlias.kt");
doTest(fileName);
}
@TestMetadata("labeledAnonymousFun.kt")
public void testLabeledAnonymousFun() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/usageHighlighter/labeledAnonymousFun.kt");
@@ -204,6 +204,18 @@ public class RenameTestGenerated extends AbstractRenameTest {
doTest(fileName);
}
@TestMetadata("importAlias/importAlias.test")
public void testImportAlias_ImportAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/importAlias/importAlias.test");
doTest(fileName);
}
@TestMetadata("importAliasByRef/importAliasByRef.test")
public void testImportAliasByRef_ImportAliasByRef() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/importAliasByRef/importAliasByRef.test");
doTest(fileName);
}
@TestMetadata("labeledAnonymousFunByLabel/labeledLambdaByLabel.test")
public void testLabeledAnonymousFunByLabel_LabeledLambdaByLabel() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/labeledAnonymousFunByLabel/labeledLambdaByLabel.test");