From 567abd2a1c677ec17d4a261fbddab62adc9fdbb6 Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Wed, 22 Mar 2023 14:26:11 +0100 Subject: [PATCH] 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. --- .../kotlin/load/java/JavaClassFinderImpl.kt | 4 ++ .../javac/components/JavacBasedClassFinder.kt | 3 ++ .../resolve/jvm/KotlinJavaPsiFacade.java | 42 +++++++++++++++++++ .../kotlin/load/java/JavaClassFinder.kt | 12 ++++++ .../components/ReflectJavaClassFinder.kt | 3 ++ 5 files changed, 64 insertions(+) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt index 785d611474c..2015db9fdd9 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/JavaClassFinderImpl.kt @@ -44,6 +44,10 @@ class JavaClassFinderImpl : AbstractJavaClassFinder() { return javaFacade.findClass(request, javaSearchScope) } + override fun findClasses(request: JavaClassFinder.Request): List { + return javaFacade.findClasses(request, javaSearchScope) + } + override fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean): JavaPackage? { return javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope, mayHaveAnnotations) } } diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt index 6bb5d010836..99c183ff12c 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/components/JavacBasedClassFinder.kt @@ -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 = listOfNotNull(findClass(request)) + override fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean) = javac.findPackage(fqName, javaSearchScope) override fun knownClassNamesInPackage(packageFqName: FqName) = javac.knownClassNamesInPackage(packageFqName) diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java index 5f781b5cc99..2b290ef90c1 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java @@ -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 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 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 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 dirs = packageIndex.getDirsByPackageName(qualifiedName, true); diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt index 5ae2f5eb78b..44915b56878 100644 --- a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JavaClassFinder.kt @@ -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 + + fun findClasses(classId: ClassId): List = findClasses(Request(classId)) + fun findPackage(fqName: FqName, mayHaveAnnotations: Boolean = true): JavaPackage? fun knownClassNamesInPackage(packageFqName: FqName): Set? diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectJavaClassFinder.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectJavaClassFinder.kt index 6ac6d277d95..b38a92610d5 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectJavaClassFinder.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectJavaClassFinder.kt @@ -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 = 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)