Fix broken isReferenceTo checking code for Android extensions

#KT-16132 Fixed
This commit is contained in:
Vyacheslav Gerasimov
2017-02-06 17:16:34 +03:00
parent 25c1828288
commit 39010ab847
29 changed files with 409 additions and 27 deletions
@@ -72,12 +72,8 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
}
override fun isReferenceTo(element: PsiElement?): Boolean {
if (element != null) {
if (!canBeReferenceTo(element)) return false
for (extension in Extensions.getArea(element.project).getExtensionPoint(SimpleNameReferenceExtension.EP_NAME).extensions) {
if (extension.isReferenceTo(this, element)) return true
}
if (element != null && !canBeReferenceTo(element)) {
return false
}
return super.isReferenceTo(element)
@@ -27,6 +27,5 @@ interface SimpleNameReferenceExtension {
ExtensionPointName.create("org.jetbrains.kotlin.simpleNameReferenceExtension")
}
fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement): Boolean
fun handleElementRename(reference: KtSimpleNameReference, psiFactory: KtPsiFactory, newElementName: String): PsiElement?
}
@@ -25,16 +25,11 @@ import org.jetbrains.kotlin.android.synthetic.androidIdToName
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiFactory
class AndroidSimpleNameReferenceExtension : SimpleNameReferenceExtension {
override fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement) = when {
element is ValueResourceElementWrapper && AndroidResourceUtil.isIdDeclaration(element) -> true
isLayoutPackageIdentifier(reference) -> true
else -> false
}
private fun isLayoutPackageIdentifier(reference: KtSimpleNameReference): Boolean {
val probablyVariant = reference.element?.parent as? KtDotQualifiedExpression ?: return false
val probablyKAS = probablyVariant.receiverExpression as? KtDotQualifiedExpression ?: return false
@@ -0,0 +1,20 @@
package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
// KT-16132 Renaming property provided by kotlinx leads to renaming another members
object Loginer {
fun login() { }
}
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
<caret>login.setOnClickListener {
Loginer.login()
}
}
}
@@ -0,0 +1,20 @@
package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
// KT-16132 Renaming property provided by kotlinx leads to renaming another members
object Loginer {
fun login() { }
}
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
NEWNAME.setOnClickListener {
Loginer.login()
}
}
}
@@ -0,0 +1,17 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,17 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,9 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.NEWNAME
}
@@ -0,0 +1,15 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,9 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.NEWNAME
}
@@ -0,0 +1,15 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,9 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.NEWNAME
}
@@ -0,0 +1,14 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,9 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.NEWNAME
}
@@ -0,0 +1,14 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,10 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.NEWNAME
val button1 = this.loginButton
}
@@ -0,0 +1,29 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,29 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,10 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.NEWNAME
val button1 = this.loginButton
}
@@ -0,0 +1,29 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,29 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,17 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,12 @@
package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
val button = NEWNAME
}
@@ -0,0 +1,17 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,11 @@
package com.myapp
import android.app.Fragment
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyFragment : Fragment() {
override fun onResume() {}
val button = NEWNAME
}
@@ -0,0 +1,17 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/NEWNAME"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,9 @@
package com.myapp
import android.view.View
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = NEWNAME
}
@@ -17,9 +17,11 @@
package org.jetbrains.kotlin.android
import com.intellij.codeInsight.TargetElementUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.psi.impl.source.xml.XmlAttributeValueImpl
import com.intellij.testFramework.PlatformTestUtil
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
abstract class AbstractAndroidRenameTest : KotlinAndroidTestCase() {
@@ -27,22 +29,19 @@ abstract class AbstractAndroidRenameTest : KotlinAndroidTestCase() {
private val NEW_ID_NAME = "@+id/$NEW_NAME"
fun doTest(path: String) {
val f = myFixture!!
getResourceDirs(path).forEach { myFixture.copyDirectoryToProject(it.name, it.name) }
val virtualFile = f.copyFileToProject(path + getTestName(true) + ".kt", "src/" + getTestName(true) + ".kt")
f.configureFromExistingVirtualFile(virtualFile)
val completionEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(f.editor, f.file)
val element = TargetElementUtil.findTargetElement(
completionEditor,
TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED or TargetElementUtil.ELEMENT_NAME_ACCEPTED) as XmlAttributeValueImpl
RenameProcessor(f.project, element, NEW_ID_NAME, false, true).run()
val expression = TargetElementUtil.findReference(f.editor, f.caretOffset)!!.element as KtNameReferenceExpression
assertEquals(NEW_NAME, expression.getReferencedName())
val virtualFile = myFixture.copyFileToProject("$path${getTestName(true)}.kt", "src/${getTestName(true)}.kt")
myFixture.configureFromExistingVirtualFile(virtualFile)
myFixture.renameElement(myFixture.elementAtCaret, NEW_ID_NAME)
myFixture.checkResultByFile("expected/${getTestName(true)}.kt")
assertResourcesEqual("$path/expected/res")
}
fun assertResourcesEqual(expectedPath: String) {
PlatformTestUtil.assertDirectoriesEqual(LocalFileSystem.getInstance().findFileByPath(expectedPath), getResourceDirectory())
}
fun getResourceDirectory() = LocalFileSystem.getInstance().findFileByPath(myFixture.tempDirPath + "/res")
override fun getTestDataPath() = KotlinAndroidTestCaseBase.getPluginTestDataPathBase() + "/rename/" + getTestName(true) + "/"
}
@@ -36,6 +36,12 @@ public class AndroidRenameTestGenerated extends AbstractAndroidRenameTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/rename"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, false);
}
@TestMetadata("commonElementId")
public void testCommonElementId() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/rename/commonElementId/");
doTest(fileName);
}
@TestMetadata("fqNameInAttr")
public void testFqNameInAttr() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/rename/fqNameInAttr/");