KT-57207 Implement JavaClassFinder.findClasses

- This change is a prerequisite for allowing combined Java symbol
  providers (in LL FIR) to correctly disambiguate classpath order after
  getting classes with a combined scope, as the index access of the
  combined Java symbol provider is not guaranteed to return the class
  that should be first based on the original dependency order. To be
  able to disambiguate, a combined Java symbol provider needs access to
  all class candidates the index can find.
This commit is contained in:
Marco Pennekamp
2023-03-22 14:26:11 +01:00
committed by Space Team
parent f2e3c593a1
commit 567abd2a1c
5 changed files with 64 additions and 0 deletions
@@ -44,6 +44,10 @@ class JavaClassFinderImpl : AbstractJavaClassFinder() {
return javaFacade.findClass(request, javaSearchScope)
}
override fun findClasses(request: JavaClassFinder.Request): List<JavaClass> {
return javaFacade.findClasses(request, javaSearchScope)
}
override fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean): JavaPackage? {
return javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope, mayHaveAnnotations) }
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.java.AbstractJavaClassFinder
import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
@@ -42,6 +43,8 @@ class JavacBasedClassFinder : AbstractJavaClassFinder() {
// TODO: reuse previouslyFoundClassFileContent if it's possible in javac
javac.findClass(request.classId, javaSearchScope)
override fun findClasses(request: JavaClassFinder.Request): List<JavaClass> = listOfNotNull(findClass(request))
override fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean) = javac.findPackage(fqName, javaSearchScope)
override fun knownClassNamesInPackage(packageFqName: FqName) = javac.knownClassNamesInPackage(packageFqName)
@@ -39,6 +39,7 @@ import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.CommonProcessors;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.Query;
import com.intellij.util.SmartList;
import com.intellij.util.messages.MessageBusConnection;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
@@ -196,6 +197,31 @@ public class KotlinJavaPsiFacade implements Disposable {
return null;
}
@NotNull
public List<JavaClass> findClasses(@NotNull JavaClassFinder.Request request, @NotNull GlobalSearchScope scope) {
if (scope == GlobalSearchScope.EMPTY_SCOPE) return Collections.emptyList();
// We hope this method is being called often enough to cancel daemon processes smoothly
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
assert !shouldUseSlowResolve() : "`findClasses` should not be called from dumb mode, as results may be incomplete.";
ClassId classId = request.getClassId();
String qualifiedName = classId.asSingleFqName().asString();
List<JavaClass> javaClasses = new SmartList<>();
for (KotlinPsiElementFinderWrapper finder : finders()) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
for (PsiClass psiClass : finder.findClasses(qualifiedName, scope)) {
javaClasses.add(createJavaClass(classId, psiClass));
}
}
return javaClasses;
}
@NotNull
private static JavaClass createJavaClass(@NotNull ClassId classId, @NotNull PsiClass psiClass) {
JavaClassImpl javaClass = new JavaClassImpl(psiClass);
@@ -442,6 +468,7 @@ public class KotlinJavaPsiFacade implements Disposable {
interface KotlinPsiElementFinderWrapper {
PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope);
PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope);
PsiPackage findPackage(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope);
boolean isSameResultForAnyScope();
}
@@ -458,6 +485,11 @@ public class KotlinJavaPsiFacade implements Disposable {
return finder.findClass(qualifiedName, scope);
}
@Override
public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
return finder.findClasses(qualifiedName, scope);
}
@Override
public PsiPackage findPackage(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
// Original element finder can't search packages with scope
@@ -497,6 +529,11 @@ public class KotlinJavaPsiFacade implements Disposable {
return javaFileManager.findClass(request, scope);
}
@Override
public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
return javaFileManager.findClasses(qualifiedName, scope);
}
@Nullable
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName) {
return javaFileManager.knownClassNamesInPackage(packageFqName);
@@ -529,6 +566,11 @@ public class KotlinJavaPsiFacade implements Disposable {
return javaFileManager.findClass(qualifiedName, scope);
}
@Override
public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
return javaFileManager.findClasses(qualifiedName, scope);
}
@Override
public PsiPackage findPackage(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
Query<VirtualFile> dirs = packageIndex.getDirsByPackageName(qualifiedName, true);
@@ -21,6 +21,18 @@ interface JavaClassFinder {
fun findClass(request: Request): JavaClass?
fun findClass(classId: ClassId): JavaClass? = findClass(Request(classId))
/**
* Finds all classes with the specified [ClassId]. This function should be used if the search space permits such ambiguities and if
* [findClass] is not guaranteed to disambiguate by itself. For example, in an IDE context, a broad search scope might lead to multiple
* valid candidates, which need to be disambiguated according to classpath order.
*
* [findClasses] may return a single [JavaClass], even if more could be found, if the resulting [JavaClass] is guaranteed to be the
* first in the dependency order.
*/
fun findClasses(request: Request): List<JavaClass>
fun findClasses(classId: ClassId): List<JavaClass> = findClasses(Request(classId))
fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean = true): JavaPackage?
fun knownClassNamesInPackage(packageFqName: FqName): Set<String>?
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaPackage
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaClass
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaPackage
import org.jetbrains.kotlin.name.ClassId
class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFinder {
override fun findClass(request: JavaClassFinder.Request): JavaClass? {
@@ -36,6 +37,8 @@ class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFi
return if (klass != null) ReflectJavaClass(klass) else null
}
override fun findClasses(request: JavaClassFinder.Request): List<JavaClass> = listOfNotNull(findClass(request))
override fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean): JavaPackage? {
// We don't know which packages our class loader has and has not, so we behave as if it contains any package in the world
return ReflectJavaPackage(fqName)