diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt index 630f2282e2e..3bcde7d242c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt @@ -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 diff --git a/idea/testData/navigation/gotoDeclaration/labeledThisToClass.test b/idea/testData/navigation/gotoDeclaration/labeledThisToClass.test new file mode 100644 index 00000000000..ca0409a8aa0 --- /dev/null +++ b/idea/testData/navigation/gotoDeclaration/labeledThisToClass.test @@ -0,0 +1,13 @@ +// FILE: before.kt +class A { + fun String.foo() { + this@A + } +} + +// FILE: after.kt +class A { + fun String.foo() { + this@A + } +} diff --git a/idea/testData/navigation/gotoDeclaration/labeledThisToMemberExtension.test b/idea/testData/navigation/gotoDeclaration/labeledThisToMemberExtension.test new file mode 100644 index 00000000000..e074616d283 --- /dev/null +++ b/idea/testData/navigation/gotoDeclaration/labeledThisToMemberExtension.test @@ -0,0 +1,13 @@ +// FILE: before.kt +class A { + fun String.foo() { + this@foo + } +} + +// FILE: after.kt +class A { + fun String.foo() { + this@foo + } +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoDeclaration/thisExtensionFunction.test b/idea/testData/navigation/gotoDeclaration/thisExtensionFunction.test new file mode 100644 index 00000000000..59ed9b08813 --- /dev/null +++ b/idea/testData/navigation/gotoDeclaration/thisExtensionFunction.test @@ -0,0 +1,18 @@ +// FILE: before.kt +interface Foo + +fun foo(a: Any) {} + +fun Foo.bar() { + foo(this) +} + + +// FILE: after.kt +interface Foo + +fun foo(a: Any) {} + +fun Foo.bar() { + foo(this) +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoDeclaration/thisExtensionLambda.test b/idea/testData/navigation/gotoDeclaration/thisExtensionLambda.test new file mode 100644 index 00000000000..fcd36843700 --- /dev/null +++ b/idea/testData/navigation/gotoDeclaration/thisExtensionLambda.test @@ -0,0 +1,21 @@ +// FILE: before.kt +interface Foo + +fun foo(a: Foo.() -> Unit) {} + +fun bar() { + foo { + this + } +} + +// FILE: after.kt +interface Foo + +fun foo(a: Foo.() -> Unit) {} + +fun bar() { + foo { + this + } +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoDeclaration/thisInExtensionPropertyAccessor.test b/idea/testData/navigation/gotoDeclaration/thisInExtensionPropertyAccessor.test new file mode 100644 index 00000000000..4e5a8e6c8a4 --- /dev/null +++ b/idea/testData/navigation/gotoDeclaration/thisInExtensionPropertyAccessor.test @@ -0,0 +1,7 @@ +// FILE: before.kt +val String.test: String + get() { return this } + +// FILE: after.kt +val String.test: String + get() { return this } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoDeclarationTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoDeclarationTestGenerated.java index a9491d2cc80..ab521c41682 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoDeclarationTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoDeclarationTestGenerated.java @@ -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); + } }