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)