Navigate to receiver from this in extension function (KT-16991)

#KT-16991 Fixed
 #KT-13013 In Progress
This commit is contained in:
Nikolay Krasko
2017-03-22 01:42:35 +03:00
parent 9dfc92c55e
commit daaa59a1ad
7 changed files with 120 additions and 1 deletions
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiReference
import com.intellij.util.BitUtil
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.source.getPsi
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
@@ -48,6 +50,20 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
val lambdaExpression = descriptorWithSource.source.getPsi()?.parent as? KtLambdaExpression ?: return null
return lambdaExpression.leftCurlyBrace.treeNext?.psi
}
// Navigate to receiver element for this in extension declaration
fun findReceiverForThisInExtensionFunction(ref: PsiReference): PsiElement? {
val element: PsiElement = ref.element
if (element.text != "this") return null
if (element !is KtNameReferenceExpression) return null
val callableDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() as? CallableDescriptor ?: return null
if (!callableDescriptor.isExtension) return null
val callableDeclaration = callableDescriptor.source.getPsi() as? KtCallableDeclaration ?: return null
return callableDeclaration.receiverTypeReference
}
}
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
@@ -67,7 +83,8 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
}
if (BitUtil.isSet(flags, TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED)) {
return findLambdaOpenLBraceForGeneratedIt(ref)
return findLambdaOpenLBraceForGeneratedIt(ref) ?:
findReceiverForThisInExtensionFunction(ref)
}
return null
@@ -0,0 +1,13 @@
// FILE: before.kt
class A {
fun String.foo() {
this<caret>@A
}
}
// FILE: after.kt
class <caret>A {
fun String.foo() {
this@A
}
}
@@ -0,0 +1,13 @@
// FILE: before.kt
class A {
fun String.foo() {
this<caret>@foo
}
}
// FILE: after.kt
class A {
fun <caret>String.foo() {
this@foo
}
}
@@ -0,0 +1,18 @@
// FILE: before.kt
interface Foo
fun foo(a: Any) {}
fun Foo.bar() {
foo(this<caret>)
}
// FILE: after.kt
interface Foo
fun foo(a: Any) {}
fun <caret>Foo.bar() {
foo(this)
}
@@ -0,0 +1,21 @@
// FILE: before.kt
interface Foo
fun foo(a: Foo.() -> Unit) {}
fun bar() {
foo {
<caret>this
}
}
// FILE: after.kt
interface Foo
fun foo(a: Foo.() -> Unit) {}
fun bar() {
foo <caret>{
this
}
}
@@ -0,0 +1,7 @@
// FILE: before.kt
val String.test: String
get() { return <caret>this }
// FILE: after.kt
val <caret>String.test: String
get() { return this }
@@ -65,4 +65,34 @@ public class GotoDeclarationTestGenerated extends AbstractGotoDeclarationTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itParameterInLambda.test");
doTest(fileName);
}
@TestMetadata("labeledThisToClass.test")
public void testLabeledThisToClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/labeledThisToClass.test");
doTest(fileName);
}
@TestMetadata("labeledThisToMemberExtension.test")
public void testLabeledThisToMemberExtension() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/labeledThisToMemberExtension.test");
doTest(fileName);
}
@TestMetadata("thisExtensionFunction.test")
public void testThisExtensionFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/thisExtensionFunction.test");
doTest(fileName);
}
@TestMetadata("thisExtensionLambda.test")
public void testThisExtensionLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/thisExtensionLambda.test");
doTest(fileName);
}
@TestMetadata("thisInExtensionPropertyAccessor.test")
public void testThisInExtensionPropertyAccessor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/thisInExtensionPropertyAccessor.test");
doTest(fileName);
}
}