Implement navigation to type aliases in libraries

This commit is contained in:
Pavel V. Talanov
2016-08-16 17:31:49 +03:00
parent ac3dfd93bd
commit 48d7e8c519
8 changed files with 70 additions and 31 deletions
@@ -47,10 +47,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.ModuleDescriptorKt; import org.jetbrains.kotlin.descriptors.ModuleDescriptorKt;
import org.jetbrains.kotlin.descriptors.ModuleParameters; import org.jetbrains.kotlin.descriptors.ModuleParameters;
import org.jetbrains.kotlin.frontend.di.InjectionKt; import org.jetbrains.kotlin.frontend.di.InjectionKt;
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex; import org.jetbrains.kotlin.idea.stubindex.*;
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope;
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex;
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyFqnNameIndex;
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil; import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.ClassId;
@@ -157,7 +154,7 @@ public class SourceNavigationHelper {
) { ) {
if (declaration instanceof KtPrimaryConstructor) { if (declaration instanceof KtPrimaryConstructor) {
KtClassOrObject sourceClassOrObject = KtClassOrObject sourceClassOrObject =
convertNamedClassOrObject(((KtPrimaryConstructor) declaration).getContainingClassOrObject(), navigationKind); findClassOrObject(((KtPrimaryConstructor) declaration).getContainingClassOrObject(), navigationKind);
KtPrimaryConstructor primaryConstructor = sourceClassOrObject != null ? sourceClassOrObject.getPrimaryConstructor() : null; KtPrimaryConstructor primaryConstructor = sourceClassOrObject != null ? sourceClassOrObject.getPrimaryConstructor() : null;
return primaryConstructor != null ? primaryConstructor : sourceClassOrObject; return primaryConstructor != null ? primaryConstructor : sourceClassOrObject;
} }
@@ -177,7 +174,7 @@ public class SourceNavigationHelper {
} }
else if (decompiledContainer instanceof KtClassBody) { else if (decompiledContainer instanceof KtClassBody) {
KtClassOrObject decompiledClassOrObject = (KtClassOrObject) decompiledContainer.getParent(); KtClassOrObject decompiledClassOrObject = (KtClassOrObject) decompiledContainer.getParent();
KtClassOrObject sourceClassOrObject = convertNamedClassOrObject(decompiledClassOrObject, navigationKind); KtClassOrObject sourceClassOrObject = findClassOrObject(decompiledClassOrObject, navigationKind);
//noinspection unchecked //noinspection unchecked
candidates = sourceClassOrObject == null candidates = sourceClassOrObject == null
@@ -224,8 +221,8 @@ public class SourceNavigationHelper {
//noinspection unchecked //noinspection unchecked
CallableDescriptor candidateDescriptor = (CallableDescriptor) analyzer.resolveToDescriptor(candidate); CallableDescriptor candidateDescriptor = (CallableDescriptor) analyzer.resolveToDescriptor(candidate);
if (receiversMatch(declaration, candidateDescriptor) if (receiversMatch(declaration, candidateDescriptor)
&& valueParametersTypesMatch(declaration, candidateDescriptor) && valueParametersTypesMatch(declaration, candidateDescriptor)
&& typeParametersMatch((KtTypeParameterListOwner) declaration, candidateDescriptor.getTypeParameters())) { && typeParametersMatch((KtTypeParameterListOwner) declaration, candidateDescriptor.getTypeParameters())) {
return candidate; return candidate;
} }
} }
@@ -271,23 +268,31 @@ public class SourceNavigationHelper {
} }
@Nullable @Nullable
private static KtClassOrObject convertNamedClassOrObject( private static <T extends KtNamedDeclaration> T findFirstMatchingInIndex(
@NotNull KtClassOrObject classOrObject, @NotNull T entity,
@NotNull NavigationKind navigationKind @NotNull NavigationKind navigationKind,
@NotNull StringStubIndexExtension<T> index
) { ) {
FqName classFqName = classOrObject.getFqName(); FqName classFqName = entity.getFqName();
assert classFqName != null; assert classFqName != null;
GlobalSearchScope librarySourcesScope = createLibraryOrSourcesScope(classOrObject, navigationKind); GlobalSearchScope librarySourcesScope = createLibraryOrSourcesScope(entity, navigationKind);
if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
return null; return null;
} }
Collection<KtClassOrObject> classes = KotlinFullClassNameIndex.getInstance() Collection<T> classes = index.get(classFqName.asString(), entity.getProject(), librarySourcesScope);
.get(classFqName.asString(), classOrObject.getProject(), librarySourcesScope);
if (classes.isEmpty()) { if (classes.isEmpty()) {
return null; return null;
} }
return classes.iterator().next(); // if there are more than one class with this FQ, find first of them return classes.iterator().next(); // if there are more than one with this FQ, find first of them
}
@Nullable
private static KtClassOrObject findClassOrObject(
@NotNull KtClassOrObject decompiledClassOrObject,
@NotNull NavigationKind navigationKind
) {
return findFirstMatchingInIndex(decompiledClassOrObject, navigationKind, KotlinFullClassNameIndex.getInstance());
} }
@NotNull @NotNull
@@ -439,7 +444,8 @@ public class SourceNavigationHelper {
@NotNull @NotNull
public static KtDeclaration navigateToDeclaration( public static KtDeclaration navigateToDeclaration(
@NotNull KtDeclaration from, @NotNull KtDeclaration from,
@NotNull NavigationKind navigationKind) { @NotNull NavigationKind navigationKind
) {
if (DumbService.isDumb(from.getProject())) return from; if (DumbService.isDumb(from.getProject())) return from;
switch (navigationKind) { switch (navigationKind) {
@@ -476,12 +482,17 @@ public class SourceNavigationHelper {
@Override @Override
public KtDeclaration visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) { public KtDeclaration visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) {
return convertNamedClassOrObject(declaration, navigationKind); return findClassOrObject(declaration, navigationKind);
} }
@Override @Override
public KtDeclaration visitClass(@NotNull KtClass klass, Void data) { public KtDeclaration visitClass(@NotNull KtClass klass, Void data) {
return convertNamedClassOrObject(klass, navigationKind); return findClassOrObject(klass, navigationKind);
}
@Override
public KtDeclaration visitTypeAlias(@NotNull KtTypeAlias typeAlias, Void data) {
return findFirstMatchingInIndex(typeAlias, navigationKind, KotlinTopLevelTypeAliasFqNameIndex.getInstance());
} }
@Override @Override
@@ -22,10 +22,7 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyFqnNameIndex
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.kotlin.JvmBuiltInsSettings import org.jetbrains.kotlin.load.kotlin.JvmBuiltInsSettings
import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtCallableDeclaration
@@ -84,20 +81,23 @@ private fun findCandidateDeclarationsInIndex(
} }
val topLevelDeclaration = DescriptorUtils.getParentOfType(referencedDescriptor, PropertyDescriptor::class.java, false) val topLevelDeclaration = DescriptorUtils.getParentOfType(referencedDescriptor, PropertyDescriptor::class.java, false)
?: DescriptorUtils.getParentOfType(referencedDescriptor, FunctionDescriptor::class.java, false) ?: return emptyList() ?: DescriptorUtils.getParentOfType(referencedDescriptor, FunctionDescriptor::class.java, false)
?: DescriptorUtils.getParentOfType(referencedDescriptor, TypeAliasDescriptor::class.java, false)
?: return emptyList()
// filter out synthetic descriptors // filter out synthetic descriptors
if (!DescriptorUtils.isTopLevelDeclaration(topLevelDeclaration)) return emptyList() if (!DescriptorUtils.isTopLevelDeclaration(topLevelDeclaration)) return emptyList()
val fqName = topLevelDeclaration.fqNameSafe.asString() val fqName = topLevelDeclaration.fqNameSafe.asString()
return when (topLevelDeclaration) { return when (topLevelDeclaration) {
is FunctionDescriptor -> {
KotlinTopLevelFunctionFqnNameIndex.getInstance().get(fqName, project, scope) is FunctionDescriptor -> KotlinTopLevelFunctionFqnNameIndex.getInstance().get(fqName, project, scope)
}
is PropertyDescriptor -> { is PropertyDescriptor -> KotlinTopLevelPropertyFqnNameIndex.getInstance().get(fqName, project, scope)
KotlinTopLevelPropertyFqnNameIndex.getInstance().get(fqName, project, scope)
} is TypeAliasDescriptor -> KotlinTopLevelTypeAliasFqNameIndex.getInstance().get(fqName, project, scope)
else -> error("Referenced non local declaration that is not inside top level function, property of class:\n $referencedDescriptor")
else -> error("Referenced non local declaration that is not inside top level function, property, class or typealias:\n $referencedDescriptor")
} }
} }
+2
View File
@@ -121,3 +121,5 @@ public fun <T: CharSequence> funWithTypeParam(t: T) {
public fun <T: Number> funWithTypeParam(t: T) { public fun <T: Number> funWithTypeParam(t: T) {
} }
typealias SimpleClassAlias = SimpleClass
@@ -0,0 +1,2 @@
MainKt.class
public typealias <1>SimpleClassAlias = testData.libraries.SimpleClass
@@ -0,0 +1,4 @@
import testData.libraries.*
fun foo(c: SimpleClassAlias) {
}
@@ -0,0 +1,2 @@
main.kt
typealias <1>SimpleClassAlias = SimpleClass
@@ -113,6 +113,12 @@ public class NavigateToDecompiledLibraryTestGenerated extends AbstractNavigateTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("TypeAlias.kt")
public void testTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeAlias.kt");
doTest(fileName);
}
@TestMetadata("TypeWithSameShortName.kt") @TestMetadata("TypeWithSameShortName.kt")
public void testTypeWithSameShortName() throws Exception { public void testTypeWithSameShortName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt");
@@ -115,6 +115,12 @@ public class NavigateToLibrarySourceTestGenerated extends AbstractNavigateToLibr
doTest(fileName); doTest(fileName);
} }
@TestMetadata("TypeAlias.kt")
public void testTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeAlias.kt");
doTest(fileName);
}
@TestMetadata("TypeWithSameShortName.kt") @TestMetadata("TypeWithSameShortName.kt")
public void testTypeWithSameShortName() throws Exception { public void testTypeWithSameShortName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt");
@@ -208,6 +214,12 @@ public class NavigateToLibrarySourceTestGenerated extends AbstractNavigateToLibr
doWithJSModuleTest(fileName); doWithJSModuleTest(fileName);
} }
@TestMetadata("TypeAlias.kt")
public void testTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeAlias.kt");
doWithJSModuleTest(fileName);
}
@TestMetadata("TypeWithSameShortName.kt") @TestMetadata("TypeWithSameShortName.kt")
public void testTypeWithSameShortName() throws Exception { public void testTypeWithSameShortName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt");