"Go to Type Declaration" for extension receiver and 'it' (KT-13013)
#KT-13013 Fixed
This commit is contained in:
@@ -18,24 +18,48 @@ package org.jetbrains.kotlin.idea.codeInsight
|
|||||||
|
|
||||||
import com.intellij.codeInsight.navigation.actions.TypeDeclarationProvider
|
import com.intellij.codeInsight.navigation.actions.TypeDeclarationProvider
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
|
||||||
|
|
||||||
class KotlinTypeDeclarationProvider : TypeDeclarationProvider {
|
class KotlinTypeDeclarationProvider : TypeDeclarationProvider {
|
||||||
override fun getSymbolTypeDeclarations(symbol: PsiElement): Array<PsiElement>? {
|
override fun getSymbolTypeDeclarations(symbol: PsiElement): Array<PsiElement>? {
|
||||||
if (symbol !is KtElement || symbol.getContainingFile() !is KtFile) return emptyArray()
|
if (symbol.containingFile !is KtFile) return emptyArray()
|
||||||
|
|
||||||
val bindingContext = symbol.analyze()
|
if (symbol is PsiWhiteSpace) {
|
||||||
val callableDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, symbol)
|
// Navigate to type of first parameter in lambda, works with the help of KotlinTargetElementEvaluator for the 'it' case
|
||||||
if (callableDescriptor !is CallableDescriptor) return emptyArray()
|
val lBraceElement = symbol.containingFile.findElementAt(maxOf(symbol.textOffset - 1, 0))
|
||||||
|
if (lBraceElement?.text == "{") {
|
||||||
|
val functionLiteral = lBraceElement.parent as? KtFunctionLiteral
|
||||||
|
if (functionLiteral != null) {
|
||||||
|
return functionLiteral.getTypeDeclarationFromCallable { descriptor -> descriptor.valueParameters.firstOrNull()?.type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val type = callableDescriptor.returnType ?: return emptyArray()
|
if (symbol is KtFunctionLiteral) {
|
||||||
|
// Navigate to receiver type of extension lambda
|
||||||
|
return symbol.getTypeDeclarationFromCallable { descriptor -> descriptor.extensionReceiverParameter?.type }
|
||||||
|
}
|
||||||
|
|
||||||
val classifierDescriptor = type.constructor.declarationDescriptor ?: return emptyArray()
|
if (symbol is KtTypeReference) {
|
||||||
return DescriptorToSourceUtilsIde.getAllDeclarations(symbol.project, classifierDescriptor).toTypedArray()
|
val declaration = symbol.parent
|
||||||
|
if (declaration is KtCallableDeclaration && declaration.receiverTypeReference == symbol) {
|
||||||
|
// Navigate to function receiver type, works with the help of KotlinTargetElementEvaluator for the 'this' in extension declaration
|
||||||
|
return declaration.getTypeDeclarationFromCallable { descriptor -> descriptor.extensionReceiverParameter?.type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol !is KtDeclaration) return emptyArray()
|
||||||
|
return symbol.getTypeDeclarationFromCallable { descriptor -> descriptor.returnType }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun KtDeclaration.getTypeDeclarationFromCallable(typeFromDescriptor: (CallableDescriptor) -> KotlinType?): Array<PsiElement>? {
|
||||||
|
val callableDescriptor = resolveToDescriptorIfAny() as? CallableDescriptor ?: return emptyArray()
|
||||||
|
val type = typeFromDescriptor(callableDescriptor) ?: return emptyArray()
|
||||||
|
val classifierDescriptor = type.constructor.declarationDescriptor ?: return emptyArray()
|
||||||
|
return DescriptorToSourceUtilsIde.getAllDeclarations(project, classifierDescriptor).toTypedArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo1
|
||||||
|
interface Bar1
|
||||||
|
|
||||||
|
fun Foo1.<caret>test(): Bar1 = null!!
|
||||||
|
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface Foo1
|
||||||
|
interface <caret>Bar1
|
||||||
|
|
||||||
|
fun Foo1.test(): Bar1 = null!!
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo1
|
||||||
|
interface Bar1
|
||||||
|
|
||||||
|
fun Foo1.test(): <caret>Bar1 = null!!
|
||||||
|
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface Foo1
|
||||||
|
interface Bar1
|
||||||
|
|
||||||
|
fun Foo1.test(): <caret>Bar1 = null!!
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo1
|
||||||
|
interface Bar1
|
||||||
|
|
||||||
|
fun <caret>Foo1.test(): Bar1 = null!!
|
||||||
|
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface Foo1
|
||||||
|
interface Bar1
|
||||||
|
|
||||||
|
fun <caret>Foo1.test(): Bar1 = null!!
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
interface Bar
|
||||||
|
|
||||||
|
fun foo(a: Foo.(Bar) -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo {
|
||||||
|
<caret>it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface Foo
|
||||||
|
interface <caret>Bar
|
||||||
|
|
||||||
|
fun foo(a: Foo.(Bar) -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
class Foo
|
||||||
|
|
||||||
|
fun <T> some(a: T, f: (T) -> Unit) {}
|
||||||
|
fun foo(a: Any) {}
|
||||||
|
|
||||||
|
fun baz() {
|
||||||
|
some(Foo()) {
|
||||||
|
foo(it<caret>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
class <caret>Foo
|
||||||
|
|
||||||
|
fun <T> some(a: T, f: (T) -> Unit) {}
|
||||||
|
fun foo(a: Any) {}
|
||||||
|
|
||||||
|
fun baz() {
|
||||||
|
some(Foo()) {
|
||||||
|
foo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
class Foo
|
||||||
|
|
||||||
|
val f: () -> Foo = {<caret> }
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
class Foo
|
||||||
|
|
||||||
|
val f: () -> Foo = {<caret> }
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
fun foo(a: Any) {}
|
||||||
|
|
||||||
|
fun Foo.bar() {
|
||||||
|
foo(this<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface <caret>Foo
|
||||||
|
|
||||||
|
fun foo(a: Any) {}
|
||||||
|
|
||||||
|
fun Foo.bar() {
|
||||||
|
foo(this)
|
||||||
|
}
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.TYPE)
|
||||||
|
annotation class A
|
||||||
|
|
||||||
|
fun @A Foo.bar(): Any = <caret>this
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface <caret>Foo
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.TYPE)
|
||||||
|
annotation class A
|
||||||
|
|
||||||
|
fun @A Foo.bar(): Any = this
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo<T, U>
|
||||||
|
|
||||||
|
fun Foo<Int, String>.bar(): Any = <caret>this
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface <caret>Foo<T, U>
|
||||||
|
|
||||||
|
fun Foo<Int, String>.bar(): Any = this
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
fun foo(a: Foo.() -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo {
|
||||||
|
<caret>this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface <caret>Foo
|
||||||
|
|
||||||
|
fun foo(a: Foo.() -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
interface Bar
|
||||||
|
|
||||||
|
val Foo.test: Bar
|
||||||
|
get() {
|
||||||
|
<caret>this
|
||||||
|
return null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface <caret>Foo
|
||||||
|
interface Bar
|
||||||
|
|
||||||
|
val Foo.test: Bar
|
||||||
|
get() {
|
||||||
|
this
|
||||||
|
return null!!
|
||||||
|
}
|
||||||
@@ -48,12 +48,78 @@ public class GotoTypeDeclarationTestGenerated extends AbstractGotoTypeDeclaratio
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fromFunctionDeclarationName.test")
|
||||||
|
public void testFromFunctionDeclarationName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/fromFunctionDeclarationName.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fromFunctionDeclarationReturn.test")
|
||||||
|
public void testFromFunctionDeclarationReturn() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/fromFunctionDeclarationReturn.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fromFunctionDeclrationExtension.test")
|
||||||
|
public void testFromFunctionDeclrationExtension() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/fromFunctionDeclrationExtension.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("functionCall.test")
|
@TestMetadata("functionCall.test")
|
||||||
public void testFunctionCall() throws Exception {
|
public void testFunctionCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/functionCall.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/functionCall.test");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("itExtensionLambda.test")
|
||||||
|
public void testItExtensionLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/itExtensionLambda.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("itParameterInLambda.test")
|
||||||
|
public void testItParameterInLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/itParameterInLambda.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noParametersLambda.test")
|
||||||
|
public void testNoParametersLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/noParametersLambda.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisExtensionFunction.test")
|
||||||
|
public void testThisExtensionFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/thisExtensionFunction.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisExtensionFunctionWithAnnotationOnReceiver.test")
|
||||||
|
public void testThisExtensionFunctionWithAnnotationOnReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/thisExtensionFunctionWithAnnotationOnReceiver.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisExtensionFunctionWithGenericReceiver.test")
|
||||||
|
public void testThisExtensionFunctionWithGenericReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/thisExtensionFunctionWithGenericReceiver.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisExtensionLambda.test")
|
||||||
|
public void testThisExtensionLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/thisExtensionLambda.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisInExtensionPropertyAccessor.test")
|
||||||
|
public void testThisInExtensionPropertyAccessor() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/thisInExtensionPropertyAccessor.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("variableType.test")
|
@TestMetadata("variableType.test")
|
||||||
public void testVariableType() throws Exception {
|
public void testVariableType() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/variableType.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/variableType.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user