"Go to Type Declaration" for extension receiver and 'it' (KT-13013)

#KT-13013 Fixed
This commit is contained in:
Nikolay Krasko
2017-03-17 15:09:28 +03:00
parent daaa59a1ad
commit e599688733
13 changed files with 276 additions and 13 deletions
@@ -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()
}
}
@@ -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!!
@@ -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!!
@@ -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)
}
@@ -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
@@ -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
}
}
@@ -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");