"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.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class KotlinTypeDeclarationProvider : TypeDeclarationProvider {
|
||||
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()
|
||||
val callableDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, symbol)
|
||||
if (callableDescriptor !is CallableDescriptor) return emptyArray()
|
||||
if (symbol is PsiWhiteSpace) {
|
||||
// Navigate to type of first parameter in lambda, works with the help of KotlinTargetElementEvaluator for the 'it' case
|
||||
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()
|
||||
return DescriptorToSourceUtilsIde.getAllDeclarations(symbol.project, classifierDescriptor).toTypedArray()
|
||||
if (symbol is KtTypeReference) {
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testFunctionCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/functionCall.test");
|
||||
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")
|
||||
public void testVariableType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoTypeDeclaration/variableType.test");
|
||||
|
||||
Reference in New Issue
Block a user