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.ModuleParameters;
import org.jetbrains.kotlin.frontend.di.InjectionKt;
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex;
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.stubindex.*;
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.ClassId;
@@ -157,7 +154,7 @@ public class SourceNavigationHelper {
) {
if (declaration instanceof KtPrimaryConstructor) {
KtClassOrObject sourceClassOrObject =
convertNamedClassOrObject(((KtPrimaryConstructor) declaration).getContainingClassOrObject(), navigationKind);
findClassOrObject(((KtPrimaryConstructor) declaration).getContainingClassOrObject(), navigationKind);
KtPrimaryConstructor primaryConstructor = sourceClassOrObject != null ? sourceClassOrObject.getPrimaryConstructor() : null;
return primaryConstructor != null ? primaryConstructor : sourceClassOrObject;
}
@@ -177,7 +174,7 @@ public class SourceNavigationHelper {
}
else if (decompiledContainer instanceof KtClassBody) {
KtClassOrObject decompiledClassOrObject = (KtClassOrObject) decompiledContainer.getParent();
KtClassOrObject sourceClassOrObject = convertNamedClassOrObject(decompiledClassOrObject, navigationKind);
KtClassOrObject sourceClassOrObject = findClassOrObject(decompiledClassOrObject, navigationKind);
//noinspection unchecked
candidates = sourceClassOrObject == null
@@ -224,8 +221,8 @@ public class SourceNavigationHelper {
//noinspection unchecked
CallableDescriptor candidateDescriptor = (CallableDescriptor) analyzer.resolveToDescriptor(candidate);
if (receiversMatch(declaration, candidateDescriptor)
&& valueParametersTypesMatch(declaration, candidateDescriptor)
&& typeParametersMatch((KtTypeParameterListOwner) declaration, candidateDescriptor.getTypeParameters())) {
&& valueParametersTypesMatch(declaration, candidateDescriptor)
&& typeParametersMatch((KtTypeParameterListOwner) declaration, candidateDescriptor.getTypeParameters())) {
return candidate;
}
}
@@ -271,23 +268,31 @@ public class SourceNavigationHelper {
}
@Nullable
private static KtClassOrObject convertNamedClassOrObject(
@NotNull KtClassOrObject classOrObject,
@NotNull NavigationKind navigationKind
private static <T extends KtNamedDeclaration> T findFirstMatchingInIndex(
@NotNull T entity,
@NotNull NavigationKind navigationKind,
@NotNull StringStubIndexExtension<T> index
) {
FqName classFqName = classOrObject.getFqName();
FqName classFqName = entity.getFqName();
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
return null;
}
Collection<KtClassOrObject> classes = KotlinFullClassNameIndex.getInstance()
.get(classFqName.asString(), classOrObject.getProject(), librarySourcesScope);
Collection<T> classes = index.get(classFqName.asString(), entity.getProject(), librarySourcesScope);
if (classes.isEmpty()) {
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
@@ -439,7 +444,8 @@ public class SourceNavigationHelper {
@NotNull
public static KtDeclaration navigateToDeclaration(
@NotNull KtDeclaration from,
@NotNull NavigationKind navigationKind) {
@NotNull NavigationKind navigationKind
) {
if (DumbService.isDumb(from.getProject())) return from;
switch (navigationKind) {
@@ -476,12 +482,17 @@ public class SourceNavigationHelper {
@Override
public KtDeclaration visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) {
return convertNamedClassOrObject(declaration, navigationKind);
return findClassOrObject(declaration, navigationKind);
}
@Override
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
@@ -22,10 +22,7 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
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.stubindex.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.kotlin.JvmBuiltInsSettings
import org.jetbrains.kotlin.psi.KtCallableDeclaration
@@ -84,20 +81,23 @@ private fun findCandidateDeclarationsInIndex(
}
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
if (!DescriptorUtils.isTopLevelDeclaration(topLevelDeclaration)) return emptyList()
val fqName = topLevelDeclaration.fqNameSafe.asString()
return when (topLevelDeclaration) {
is FunctionDescriptor -> {
KotlinTopLevelFunctionFqnNameIndex.getInstance().get(fqName, project, scope)
}
is PropertyDescriptor -> {
KotlinTopLevelPropertyFqnNameIndex.getInstance().get(fqName, project, scope)
}
else -> error("Referenced non local declaration that is not inside top level function, property of class:\n $referencedDescriptor")
is FunctionDescriptor -> KotlinTopLevelFunctionFqnNameIndex.getInstance().get(fqName, project, scope)
is PropertyDescriptor -> 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, 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) {
}
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);
}
@TestMetadata("TypeAlias.kt")
public void testTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeAlias.kt");
doTest(fileName);
}
@TestMetadata("TypeWithSameShortName.kt")
public void testTypeWithSameShortName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt");
@@ -115,6 +115,12 @@ public class NavigateToLibrarySourceTestGenerated extends AbstractNavigateToLibr
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")
public void testTypeWithSameShortName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt");
@@ -208,6 +214,12 @@ public class NavigateToLibrarySourceTestGenerated extends AbstractNavigateToLibr
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")
public void testTypeWithSameShortName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/navigation/usercode/TypeWithSameShortName.kt");