Improve package caching in KotlinJavaPsiFacade
^KT-33394 Fixed
This commit is contained in:
@@ -404,7 +404,7 @@ class JavaSymbolProvider(
|
||||
|
||||
private fun hasTopLevelClassOf(classId: ClassId): Boolean {
|
||||
val knownNames = knownClassNamesInPackage.getOrPut(classId.packageFqName) {
|
||||
facade.knownClassNamesInPackage(classId.packageFqName)
|
||||
facade.knownClassNamesInPackage(classId.packageFqName, searchScope)
|
||||
} ?: return true
|
||||
return classId.relativeClassName.topLevelName() in knownNames
|
||||
}
|
||||
|
||||
+15
-4
@@ -23,6 +23,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopPackageNamesProvider
|
||||
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
|
||||
import javax.annotation.PostConstruct
|
||||
import javax.inject.Inject
|
||||
@@ -32,10 +33,14 @@ abstract class AbstractJavaClassFinder : JavaClassFinder {
|
||||
protected lateinit var project: Project
|
||||
protected lateinit var javaSearchScope: GlobalSearchScope
|
||||
|
||||
|
||||
@Inject
|
||||
fun setScope(scope: GlobalSearchScope) {
|
||||
javaSearchScope = FilterOutKotlinSourceFilesScope(scope)
|
||||
javaSearchScope =
|
||||
if (scope == GlobalSearchScope.EMPTY_SCOPE) {
|
||||
GlobalSearchScope.EMPTY_SCOPE
|
||||
} else {
|
||||
FilterOutKotlinSourceFilesScope(scope)
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -48,9 +53,15 @@ abstract class AbstractJavaClassFinder : JavaClassFinder {
|
||||
CodeAnalyzerInitializer.getInstance(project).initialize(trace, codeAnalyzer.moduleDescriptor, codeAnalyzer)
|
||||
}
|
||||
|
||||
inner class FilterOutKotlinSourceFilesScope(baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
|
||||
inner class FilterOutKotlinSourceFilesScope(baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope),
|
||||
TopPackageNamesProvider {
|
||||
|
||||
override fun contains(file: VirtualFile) = myBaseScope.contains(file) && (file.isDirectory || file.fileType !== KotlinFileType.INSTANCE)
|
||||
override val topPackageNames: Set<String>?
|
||||
get() = (myBaseScope as? TopPackageNamesProvider)?.topPackageNames
|
||||
|
||||
override fun contains(file: VirtualFile) =
|
||||
(file.isDirectory || file.fileType !== KotlinFileType.INSTANCE) &&
|
||||
myBaseScope.contains(file)
|
||||
|
||||
val base: GlobalSearchScope = myBaseScope
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.load.java
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaPackage
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaPackageImpl
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
@@ -33,11 +34,16 @@ class JavaClassFinderImpl : AbstractJavaClassFinder() {
|
||||
javaFacade = KotlinJavaPsiFacade.getInstance(project)
|
||||
}
|
||||
|
||||
override fun findClass(request: JavaClassFinder.Request): JavaClass? = javaFacade.findClass(request, javaSearchScope)
|
||||
override fun findClass(request: JavaClassFinder.Request): JavaClass? {
|
||||
return javaFacade.findClass(request, javaSearchScope)
|
||||
}
|
||||
|
||||
override fun findPackage(fqName: FqName) =
|
||||
javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope) }
|
||||
override fun findPackage(fqName: FqName): JavaPackage? {
|
||||
return javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope) }
|
||||
}
|
||||
|
||||
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? = javaFacade.knownClassNamesInPackage(packageFqName)
|
||||
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? {
|
||||
return javaFacade.knownClassNamesInPackage(packageFqName, javaSearchScope)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-4
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
|
||||
class IncrementalPackageFragmentProvider(
|
||||
@@ -56,10 +57,8 @@ class IncrementalPackageFragmentProvider(
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> = emptySet()
|
||||
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||
return listOfNotNull(fqNameToPackageFragment[fqName])
|
||||
}
|
||||
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
|
||||
packageFragments.addIfNotNull(fqNameToPackageFragment[fqName])
|
||||
|
||||
inner class IncrementalPackageFragment(fqName: FqName) : PackageFragmentDescriptorImpl(moduleDescriptor, fqName) {
|
||||
val target: TargetId
|
||||
|
||||
+3
-2
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.storage.NotNullLazyValue
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class OptionalAnnotationPackageFragmentProvider(
|
||||
module: ModuleDescriptor,
|
||||
@@ -79,8 +80,8 @@ class OptionalAnnotationPackageFragmentProvider(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> =
|
||||
packages[fqName]?.let(::listOf).orEmpty()
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
|
||||
packageFragments.addIfNotNull(packages[fqName])
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> =
|
||||
emptyList()
|
||||
|
||||
@@ -241,10 +241,10 @@ private class DelegatingPackageFragmentProvider<M : ModuleInfo>(
|
||||
) : PackageFragmentProvider {
|
||||
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
|
||||
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||
if (certainlyDoesNotExist(fqName)) return emptyList()
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
if (certainlyDoesNotExist(fqName)) return
|
||||
|
||||
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.getPackageFragments(fqName)
|
||||
resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.collectPackageFragments(fqName, packageFragments)
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
||||
|
||||
@@ -129,23 +129,28 @@ class LazyModuleDependencies<M : ModuleInfo>(
|
||||
firstDependency: M? = null,
|
||||
private val resolverForProject: AbstractResolverForProject<M>
|
||||
) : ModuleDependencies {
|
||||
|
||||
private val dependencies = storageManager.createLazyValue {
|
||||
|
||||
val moduleDescriptors = mutableSetOf<ModuleDescriptorImpl>()
|
||||
firstDependency?.let {
|
||||
moduleDescriptors.add(resolverForProject.descriptorForModule(it))
|
||||
}
|
||||
val moduleDescriptor = resolverForProject.descriptorForModule(module)
|
||||
sequence {
|
||||
if (firstDependency != null) {
|
||||
yield(resolverForProject.descriptorForModule(firstDependency))
|
||||
}
|
||||
if (module.dependencyOnBuiltIns() == ModuleInfo.DependencyOnBuiltIns.AFTER_SDK) {
|
||||
yield(moduleDescriptor.builtIns.builtInsModule)
|
||||
}
|
||||
for (dependency in module.dependencies()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
yield(resolverForProject.descriptorForModule(dependency as M))
|
||||
}
|
||||
if (module.dependencyOnBuiltIns() == ModuleInfo.DependencyOnBuiltIns.LAST) {
|
||||
yield(moduleDescriptor.builtIns.builtInsModule)
|
||||
}
|
||||
}.toList()
|
||||
val dependencyOnBuiltIns = module.dependencyOnBuiltIns()
|
||||
if (dependencyOnBuiltIns == ModuleInfo.DependencyOnBuiltIns.AFTER_SDK) {
|
||||
moduleDescriptors.add(moduleDescriptor.builtIns.builtInsModule)
|
||||
}
|
||||
for (dependency in module.dependencies()) {
|
||||
if (dependency == firstDependency) continue
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
moduleDescriptors.add(resolverForProject.descriptorForModule(dependency as M))
|
||||
}
|
||||
if (dependencyOnBuiltIns == ModuleInfo.DependencyOnBuiltIns.LAST) {
|
||||
moduleDescriptors.add(moduleDescriptor.builtIns.builtInsModule)
|
||||
}
|
||||
moduleDescriptors.toList()
|
||||
}
|
||||
|
||||
override val allDependencies: List<ModuleDescriptorImpl> get() = dependencies()
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionUtilCore
|
||||
import com.intellij.psi.impl.source.DummyHolder
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
@@ -573,6 +574,9 @@ class QualifiedExpressionResolver(val languageVersionSettings: LanguageVersionSe
|
||||
context: ExpressionTypingContext
|
||||
): Qualifier? {
|
||||
val name = expression.getReferencedNameAsName()
|
||||
if (!expression.isPhysical && !name.isSpecial && name.asString().endsWith(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val location = KotlinLookupLocation(expression)
|
||||
val qualifierDescriptor = when (receiver) {
|
||||
|
||||
@@ -180,6 +180,16 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext {
|
||||
this.declarationProviderFactory = declarationProviderFactory;
|
||||
|
||||
this.packageFragmentProvider = new PackageFragmentProvider() {
|
||||
@Override
|
||||
public void collectPackageFragments(
|
||||
@NotNull FqName fqName, @NotNull Collection<PackageFragmentDescriptor> packageFragments
|
||||
) {
|
||||
LazyPackageDescriptor fragment = getPackageFragment(fqName);
|
||||
if (fragment != null) {
|
||||
packageFragments.add(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PackageFragmentDescriptor> getPackageFragments(@NotNull FqName fqName) {
|
||||
|
||||
+174
-61
@@ -21,9 +21,13 @@ import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.PackageIndex;
|
||||
import com.intellij.openapi.util.LowMemoryWatcher;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiElementFinderImpl;
|
||||
import com.intellij.psi.impl.file.PsiPackageImpl;
|
||||
@@ -31,13 +35,11 @@ import com.intellij.psi.impl.file.impl.JavaFileManager;
|
||||
import com.intellij.psi.impl.light.LightModifierList;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.Query;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -60,11 +62,26 @@ public class KotlinJavaPsiFacade {
|
||||
private volatile KotlinPsiElementFinderWrapper[] elementFinders;
|
||||
|
||||
private static class PackageCache {
|
||||
// long term cache
|
||||
final ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> packageInLibScopeCache = ContainerUtil.newConcurrentMap();
|
||||
|
||||
// short term caches
|
||||
final ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> packageInScopeCache = ContainerUtil.newConcurrentMap();
|
||||
final ConcurrentMap<String, Boolean> hasPackageInAllScopeCache = ContainerUtil.newConcurrentMap();
|
||||
|
||||
void clear() {
|
||||
packageInScopeCache.clear();
|
||||
hasPackageInAllScopeCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private volatile SoftReference<PackageCache> packageCache;
|
||||
private static final PsiPackage NULL_PACKAGE = new PsiPackageImpl(null, "NULL_PACKAGE");
|
||||
|
||||
private static @Nullable PsiPackage unwrap(@NotNull PsiPackage psiPackage) {
|
||||
return psiPackage == NULL_PACKAGE ? null : psiPackage;
|
||||
}
|
||||
|
||||
private volatile PackageCache packageCache;
|
||||
|
||||
private final Project project;
|
||||
private final LightModifierList emptyModifierList;
|
||||
@@ -78,27 +95,60 @@ public class KotlinJavaPsiFacade {
|
||||
|
||||
emptyModifierList = new LightModifierList(PsiManager.getInstance(project), KotlinLanguage.INSTANCE);
|
||||
|
||||
PsiModificationTracker modificationTracker = PsiManager.getInstance(project).getModificationTracker();
|
||||
MessageBus bus = project.getMessageBus();
|
||||
// drop entire cache when it is low free memory
|
||||
LowMemoryWatcher.register(this::clearPackageCaches, project);
|
||||
|
||||
bus.connect().subscribe(PsiModificationTracker.TOPIC, new PsiModificationTracker.Listener() {
|
||||
MessageBusConnection connection = project.getMessageBus().connect();
|
||||
|
||||
// VFS changes like create/delete/copy/move directory are subject to clean up short term caches
|
||||
connection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
boolean relevant = false;
|
||||
for (VFileEvent event : events) {
|
||||
VirtualFile file = event.getFile();
|
||||
relevant = ((event instanceof VFileCreateEvent && ((VFileCreateEvent) event).isDirectory()) ||
|
||||
(file != null && file.isDirectory() &&
|
||||
(event instanceof VFileDeleteEvent ||
|
||||
event instanceof VFileMoveEvent ||
|
||||
event instanceof VFileCopyEvent)));
|
||||
|
||||
if (relevant) break;
|
||||
}
|
||||
if (relevant) {
|
||||
clearPackageCaches(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// PSI changes (like in R files) could lead to creating virtual packages
|
||||
// therefore it has to clean up short term caches
|
||||
PsiModificationTracker modificationTracker = PsiManager.getInstance(project).getModificationTracker();
|
||||
connection.subscribe(PsiModificationTracker.TOPIC, new PsiModificationTracker.Listener() {
|
||||
private long lastTimeSeen = -1L;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void modificationCountChanged() {
|
||||
long now = modificationTracker.getJavaStructureModificationCount();
|
||||
long now = modificationTracker.getModificationCount();
|
||||
if (lastTimeSeen != now) {
|
||||
lastTimeSeen = now;
|
||||
|
||||
packageCache = null;
|
||||
clearPackageCaches(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clearPackageCaches() {
|
||||
packageCache = null;
|
||||
clearPackageCaches(true);
|
||||
}
|
||||
|
||||
private void clearPackageCaches(boolean force) {
|
||||
if (force) {
|
||||
packageCache = null;
|
||||
} else {
|
||||
obtainPackageCache().clear();
|
||||
}
|
||||
}
|
||||
|
||||
public LightModifierList getEmptyModifierList() {
|
||||
@@ -106,7 +156,10 @@ public class KotlinJavaPsiFacade {
|
||||
}
|
||||
|
||||
public JavaClass findClass(@NotNull JavaClassFinder.Request request, @NotNull GlobalSearchScope scope) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); // We hope this method is being called often enough to cancel daemon processes smoothly
|
||||
if (scope == GlobalSearchScope.EMPTY_SCOPE) return null;
|
||||
|
||||
// We hope this method is being called often enough to cancel daemon processes smoothly
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
|
||||
ClassId classId = request.getClassId();
|
||||
String qualifiedName = classId.asSingleFqName().asString();
|
||||
@@ -150,7 +203,9 @@ public class KotlinJavaPsiFacade {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName) {
|
||||
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
|
||||
if (scope == GlobalSearchScope.EMPTY_SCOPE) return null;
|
||||
|
||||
KotlinPsiElementFinderWrapper[] finders = finders();
|
||||
|
||||
if (finders.length == 1 && finders[0] instanceof CliFinder) {
|
||||
@@ -207,17 +262,19 @@ public class KotlinJavaPsiFacade {
|
||||
: new NonCliFinder(project, javaFileManager)
|
||||
);
|
||||
|
||||
List<PsiElementFinder> nonKotlinFinders = ArraysKt.filter(
|
||||
PsiElementFinder.EP.getPoint(getProject()).getExtensions(),
|
||||
finder -> (finder instanceof KotlinSafeClassFinder) ||
|
||||
!(finder instanceof NonClasspathClassFinder ||
|
||||
finder instanceof KotlinFinderMarker ||
|
||||
finder instanceof PsiElementFinderImpl)
|
||||
);
|
||||
List<PsiElementFinder> nonKotlinFinders = new ArrayList<>();
|
||||
for (PsiElementFinder finder : PsiElementFinder.EP.getExtensions(getProject())) {
|
||||
if ((finder instanceof KotlinSafeClassFinder) ||
|
||||
!(finder instanceof NonClasspathClassFinder ||
|
||||
finder instanceof KotlinFinderMarker ||
|
||||
finder instanceof PsiElementFinderImpl)) {
|
||||
nonKotlinFinders.add(finder);
|
||||
}
|
||||
}
|
||||
|
||||
elementFinders.addAll(CollectionsKt.map(nonKotlinFinders, KotlinJavaPsiFacade::wrap));
|
||||
|
||||
return elementFinders.toArray(new KotlinPsiElementFinderWrapper[elementFinders.size()]);
|
||||
return elementFinders.toArray(new KotlinPsiElementFinderWrapper[0]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -230,60 +287,116 @@ public class KotlinJavaPsiFacade {
|
||||
}
|
||||
|
||||
public PsiPackage findPackage(@NotNull String qualifiedName, GlobalSearchScope searchScope) {
|
||||
if (searchScope == GlobalSearchScope.EMPTY_SCOPE) return null;
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
if (certainlyDoesNotExist(qualifiedName, searchScope)) return null;
|
||||
|
||||
PackageCache cache = SoftReference.dereference(packageCache);
|
||||
if (cache == null) {
|
||||
packageCache = new SoftReference<>(cache = new PackageCache());
|
||||
}
|
||||
|
||||
Pair<String, GlobalSearchScope> key = new Pair<>(qualifiedName, searchScope);
|
||||
PsiPackage aPackage = cache.packageInScopeCache.get(key);
|
||||
if (aPackage != null) {
|
||||
return aPackage;
|
||||
}
|
||||
|
||||
KotlinPsiElementFinderWrapper[] finders = filteredFinders();
|
||||
PackageCache cache = obtainPackageCache();
|
||||
|
||||
Boolean packageFoundInAllScope = cache.hasPackageInAllScopeCache.get(qualifiedName);
|
||||
if (packageFoundInAllScope != null) {
|
||||
if (!packageFoundInAllScope.booleanValue()) return null;
|
||||
if (packageFoundInAllScope != null && !packageFoundInAllScope.booleanValue()) return null;
|
||||
|
||||
// Package was found in AllScope with some of finders but is absent in packageCache for current scope.
|
||||
// We check only finders that depend on scope.
|
||||
for (KotlinPsiElementFinderWrapper finder : finders) {
|
||||
if (!finder.isSameResultForAnyScope()) {
|
||||
aPackage = finder.findPackage(qualifiedName, searchScope);
|
||||
if (aPackage != null) {
|
||||
return ConcurrencyUtil.cacheOrGet(cache.packageInScopeCache, key, aPackage);
|
||||
Pair<String, GlobalSearchScope> key = new Pair<>(qualifiedName, searchScope);
|
||||
PsiPackage pkg = cache.packageInLibScopeCache.get(key);
|
||||
if (pkg != null) {
|
||||
return unwrap(pkg);
|
||||
}
|
||||
pkg = cache.packageInScopeCache.get(key);
|
||||
if (pkg != null) {
|
||||
return unwrap(pkg);
|
||||
}
|
||||
|
||||
boolean isALibrarySearchScope = isALibrarySearchScope(searchScope);
|
||||
|
||||
{
|
||||
// store found package in a long term cache if package is found in library search scope
|
||||
ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> existedPackageInScopeCache =
|
||||
isALibrarySearchScope ? cache.packageInLibScopeCache : cache.packageInScopeCache;
|
||||
|
||||
KotlinPsiElementFinderWrapper[] finders = filteredFinders();
|
||||
if (packageFoundInAllScope != null) {
|
||||
// Package was found in AllScope with some of finders but is absent in packageCache for current scope.
|
||||
// We check only finders that depend on scope.
|
||||
for (KotlinPsiElementFinderWrapper finder : finders) {
|
||||
if (!finder.isSameResultForAnyScope()) {
|
||||
PsiPackage aPackage = finder.findPackage(qualifiedName, searchScope);
|
||||
if (aPackage != null) {
|
||||
return unwrap(ConcurrencyUtil.cacheOrGet(existedPackageInScopeCache, key, aPackage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (KotlinPsiElementFinderWrapper finder : finders) {
|
||||
aPackage = finder.findPackage(qualifiedName, searchScope);
|
||||
else {
|
||||
for (KotlinPsiElementFinderWrapper finder : finders) {
|
||||
PsiPackage aPackage = finder.findPackage(qualifiedName, searchScope);
|
||||
|
||||
if (aPackage != null) {
|
||||
return ConcurrencyUtil.cacheOrGet(cache.packageInScopeCache, key, aPackage);
|
||||
}
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
for (KotlinPsiElementFinderWrapper finder : finders) {
|
||||
if (!finder.isSameResultForAnyScope()) {
|
||||
aPackage = finder.findPackage(qualifiedName, GlobalSearchScope.allScope(project));
|
||||
if (aPackage != null) {
|
||||
found = true;
|
||||
break;
|
||||
return unwrap(ConcurrencyUtil.cacheOrGet(existedPackageInScopeCache, key, aPackage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache.hasPackageInAllScopeCache.put(qualifiedName, found);
|
||||
boolean found = false;
|
||||
for (KotlinPsiElementFinderWrapper finder : finders) {
|
||||
if (!finder.isSameResultForAnyScope()) {
|
||||
PsiPackage aPackage = finder.findPackage(qualifiedName, GlobalSearchScope.allScope(project));
|
||||
if (aPackage != null) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache.hasPackageInAllScopeCache.put(qualifiedName, found);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// qualifiedName could be like a proper package name, e.g `org.jetbrains.kotlin`
|
||||
// but it could be as well part of typed text like `fooba`
|
||||
//
|
||||
// all those temporary names and those don't even look like a package name should be stored in a short term cache
|
||||
// while names those are potentially proper package name could be stored for a long time
|
||||
// (till PROJECT_ROOTS or specific VFS changes)
|
||||
boolean packageLikeQName = qualifiedName.indexOf('.') > 0;
|
||||
|
||||
ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> notFoundPackageInScopeCache =
|
||||
// store NULL_PACKAGE (attribute that package not found) in a long term cache if:
|
||||
// - library search scope
|
||||
// - qualifiedName looks like package (has `.` in its name)
|
||||
isALibrarySearchScope && packageLikeQName ?
|
||||
cache.packageInLibScopeCache : cache.packageInScopeCache;
|
||||
|
||||
return unwrap(ConcurrencyUtil.cacheOrGet(notFoundPackageInScopeCache, key, NULL_PACKAGE));
|
||||
}
|
||||
|
||||
private PackageCache obtainPackageCache() {
|
||||
PackageCache cache = packageCache;
|
||||
if (cache == null) {
|
||||
packageCache = cache = new PackageCache();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static boolean isALibrarySearchScope(GlobalSearchScope searchScope) {
|
||||
return searchScope.isSearchInLibraries();
|
||||
}
|
||||
|
||||
private static boolean certainlyDoesNotExist(@NotNull String qualifiedName, GlobalSearchScope searchScope) {
|
||||
if (searchScope instanceof TopPackageNamesProvider) {
|
||||
TopPackageNamesProvider topPackageAwareSearchScope = (TopPackageNamesProvider) searchScope;
|
||||
Set<String> topPackageNames = topPackageAwareSearchScope.getTopPackageNames();
|
||||
if (topPackageNames != null) {
|
||||
String topPackageName = qualifiedName;
|
||||
int index = topPackageName.indexOf('.');
|
||||
if (index > 0) {
|
||||
topPackageName = topPackageName.substring(0, index);
|
||||
}
|
||||
if (!topPackageNames.contains(topPackageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -292,7 +405,7 @@ public class KotlinJavaPsiFacade {
|
||||
KotlinPsiElementFinderWrapper[] finders = finders();
|
||||
if (dumbService.isDumb()) {
|
||||
List<KotlinPsiElementFinderWrapper> list = dumbService.filterByDumbAwareness(Arrays.asList(finders));
|
||||
finders = list.toArray(new KotlinPsiElementFinderWrapper[list.size()]);
|
||||
finders = list.toArray(new KotlinPsiElementFinderWrapper[0]);
|
||||
}
|
||||
return finders;
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm
|
||||
|
||||
interface TopPackageNamesProvider {
|
||||
val topPackageNames: Set<String>?
|
||||
}
|
||||
+5
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.java.lazy
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.storage.CacheWithNotNullValues
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class LazyJavaPackageFragmentProvider(
|
||||
components: JavaResolverComponents
|
||||
@@ -41,6 +43,9 @@ class LazyJavaPackageFragmentProvider(
|
||||
|
||||
override fun getPackageFragments(fqName: FqName) = listOfNotNull(getPackageFragment(fqName))
|
||||
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
|
||||
packageFragments.addIfNotNull(getPackageFragment(fqName))
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) =
|
||||
getPackageFragment(fqName)?.getSubPackageFqNames().orEmpty()
|
||||
}
|
||||
|
||||
@@ -20,13 +20,19 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
interface PackageFragmentProvider {
|
||||
fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor>
|
||||
fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>)
|
||||
|
||||
fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||
val packageFragments = mutableListOf<PackageFragmentDescriptor>()
|
||||
collectPackageFragments(fqName, packageFragments)
|
||||
return packageFragments
|
||||
}
|
||||
|
||||
fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName>
|
||||
|
||||
|
||||
object Empty : PackageFragmentProvider {
|
||||
override fun getPackageFragments(fqName: FqName) = emptyList<PackageFragmentDescriptor>()
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) = emptySet<FqName>()
|
||||
}
|
||||
|
||||
+8
-7
@@ -20,14 +20,15 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class PackageFragmentProviderImpl(
|
||||
private val packageFragments: Collection<PackageFragmentDescriptor>
|
||||
private val packageFragments: Collection<PackageFragmentDescriptor>
|
||||
) : PackageFragmentProvider {
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> =
|
||||
packageFragments.filter { it.fqName == fqName }
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
this.packageFragments.filterTo(packageFragments) { it.fqName == fqName }
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> =
|
||||
packageFragments.asSequence()
|
||||
.map { it.fqName }
|
||||
.filter { !it.isRoot && it.parent() == fqName }
|
||||
.toList()
|
||||
packageFragments.asSequence()
|
||||
.map { it.fqName }
|
||||
.filter { !it.isRoot && it.parent() == fqName }
|
||||
.toList()
|
||||
}
|
||||
|
||||
+11
-6
@@ -23,14 +23,19 @@ import java.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class CompositePackageFragmentProvider(// can be modified from outside
|
||||
private val providers: List<PackageFragmentProvider>) : PackageFragmentProvider {
|
||||
private val providers: List<PackageFragmentProvider>
|
||||
) : PackageFragmentProvider {
|
||||
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||
val result = ArrayList<PackageFragmentDescriptor>()
|
||||
for (provider in providers) {
|
||||
result.addAll(provider.getPackageFragments(fqName))
|
||||
init {
|
||||
assert(providers.size == providers.toSet().size) {
|
||||
"providers.size is ${providers.size} while only ${providers.toSet().size} unique providers"
|
||||
}
|
||||
}
|
||||
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
for (provider in providers) {
|
||||
provider.collectPackageFragments(fqName, packageFragments)
|
||||
}
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
||||
|
||||
+3
-3
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
|
||||
class LazyPackageViewDescriptorImpl(
|
||||
override val module: ModuleDescriptorImpl,
|
||||
override val fqName: FqName,
|
||||
storageManager: StorageManager
|
||||
override val module: ModuleDescriptorImpl,
|
||||
override val fqName: FqName,
|
||||
storageManager: StorageManager
|
||||
) : DeclarationDescriptorImpl(Annotations.EMPTY, fqName.shortNameOrSpecial()), PackageViewDescriptor {
|
||||
|
||||
override val fragments: List<PackageFragmentDescriptor> by storageManager.createLazyValue {
|
||||
|
||||
+4
-1
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
abstract class AbstractDeserializedPackageFragmentProvider(
|
||||
protected val storageManager: StorageManager,
|
||||
@@ -38,7 +39,9 @@ abstract class AbstractDeserializedPackageFragmentProvider(
|
||||
|
||||
protected abstract fun findPackage(fqName: FqName): DeserializedPackageFragment?
|
||||
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> = listOfNotNull(fragments(fqName))
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
packageFragments.addIfNotNull(fragments(fqName))
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> = emptySet()
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFile
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.SmartList
|
||||
@@ -58,6 +60,7 @@ import org.jetbrains.kotlin.platform.konan.NativePlatform
|
||||
import org.jetbrains.kotlin.platform.konan.NativePlatforms
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopPackageNamesProvider
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.types.typeUtil.closure
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
@@ -459,9 +462,40 @@ object NotUnderContentRootModuleInfo : IdeaModuleInfo {
|
||||
get() = platform.single().findAnalyzerServices()
|
||||
}
|
||||
|
||||
internal open class PoweredLibraryScopeBase(project: Project, classes: Array<VirtualFile>, sources: Array<VirtualFile>) :
|
||||
LibraryScopeBase(project, classes, sources), TopPackageNamesProvider {
|
||||
|
||||
private val entriesVirtualFileSystems: Set<NewVirtualFileSystem>? = run {
|
||||
val fileSystems = mutableSetOf<NewVirtualFileSystem>()
|
||||
for (file in classes + sources) {
|
||||
val newVirtualFile = file as? NewVirtualFile ?: return@run null
|
||||
fileSystems.add(newVirtualFile.fileSystem)
|
||||
}
|
||||
fileSystems
|
||||
}
|
||||
|
||||
override val topPackageNames: Set<String> by lazy {
|
||||
(classes + sources)
|
||||
.flatMap { it.children.toList() }
|
||||
.filter(VirtualFile::isDirectory)
|
||||
.map(VirtualFile::getName)
|
||||
.toSet() + "" // empty package is always present
|
||||
}
|
||||
|
||||
override fun contains(file: VirtualFile): Boolean {
|
||||
((file as? NewVirtualFile)?.fileSystem)?.let {
|
||||
if (entriesVirtualFileSystems != null && !entriesVirtualFileSystems.contains(it)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return super.contains(file)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
|
||||
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
|
||||
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
|
||||
PoweredLibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf()) {
|
||||
|
||||
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getClassRootForFile(file)
|
||||
|
||||
@@ -474,7 +508,7 @@ private class LibraryWithoutSourceScope(project: Project, private val library: L
|
||||
|
||||
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
|
||||
private class LibrarySourceScope(project: Project, private val library: Library) :
|
||||
LibraryScopeBase(project, arrayOf<VirtualFile>(), library.getFiles(OrderRootType.SOURCES)) {
|
||||
PoweredLibraryScopeBase(project, arrayOf(), library.getFiles(OrderRootType.SOURCES)) {
|
||||
|
||||
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getSourceRootForFile(file)
|
||||
|
||||
@@ -488,7 +522,7 @@ private class LibrarySourceScope(project: Project, private val library: Library)
|
||||
//TODO: (module refactoring) android sdk has modified scope
|
||||
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
|
||||
private class SdkScope(project: Project, val sdk: Sdk) :
|
||||
LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
|
||||
PoweredLibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf()) {
|
||||
|
||||
override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk
|
||||
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ abstract class DeserializerForDecompilerBase(val directoryPackageFqName: FqName)
|
||||
protected val moduleDescriptor: ModuleDescriptorImpl = createDummyModule("module for building decompiled sources")
|
||||
|
||||
protected val packageFragmentProvider: PackageFragmentProvider = object : PackageFragmentProvider {
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||
return listOf(createDummyPackageFragment(fqName))
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
packageFragments.add(createDummyPackageFragment(fqName))
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.intellij.ProjectTopics;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootEvent;
|
||||
import com.intellij.openapi.roots.ModuleRootListener;
|
||||
import com.intellij.openapi.startup.StartupActivity;
|
||||
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -27,6 +30,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter;
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs;
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm;
|
||||
import org.jetbrains.kotlin.resolve.konan.diagnostics.ErrorsNative;
|
||||
|
||||
@@ -44,6 +48,13 @@ public class PluginStartupActivity implements StartupActivity {
|
||||
StartupCompatKt.runActivity(project);
|
||||
PluginStartupService.Companion.getInstance(project).register(project);
|
||||
|
||||
project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
|
||||
@Override
|
||||
public void rootsChanged(@NotNull ModuleRootEvent event) {
|
||||
KotlinJavaPsiFacade.getInstance(project).clearPackageCaches();
|
||||
}
|
||||
});
|
||||
|
||||
initializeDiagnostics();
|
||||
|
||||
try {
|
||||
|
||||
+3
-2
@@ -17,8 +17,9 @@ internal class CommonizedPackageFragmentProvider : PackageFragmentProvider {
|
||||
this.packageFragments += packageFragment
|
||||
}
|
||||
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> =
|
||||
packageFragments.filter { it.fqName == fqName }
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
this.packageFragments.filterTo(packageFragments) { it.fqName == fqName }
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> =
|
||||
packageFragments.asSequence()
|
||||
|
||||
+3
-1
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension {
|
||||
protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager?
|
||||
@@ -132,7 +133,8 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
|
||||
class AndroidSyntheticPackageFragmentProvider(
|
||||
val packages: Map<FqName, () -> PackageFragmentDescriptor>
|
||||
) : PackageFragmentProvider {
|
||||
override fun getPackageFragments(fqName: FqName) = listOfNotNull(packages[fqName]?.invoke())
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
|
||||
packageFragments.addIfNotNull(packages[fqName]?.invoke())
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): List<FqName> {
|
||||
return packages.asSequence()
|
||||
|
||||
Reference in New Issue
Block a user