@RequiredReadAction private static boolean processInheritors( @NotNull final Processor<DotNetTypeDeclaration> consumer, @NotNull final String baseVmQName, @NotNull final SearchScope searchScope, @NotNull final SearchParameters parameters) { if (DotNetTypes.System.Object.equals(baseVmQName)) { return AllTypesSearch.search( searchScope, parameters.getProject(), parameters.getNameCondition()) .forEach( new Processor<DotNetTypeDeclaration>() { @Override public boolean process(final DotNetTypeDeclaration aClass) { ProgressIndicatorProvider.checkCanceled(); final String qname1 = ApplicationManager.getApplication() .runReadAction( new Computable<String>() { @Override @Nullable public String compute() { return aClass.getVmQName(); } }); return DotNetTypes.System.Object.equals(qname1) || consumer.process(parameters.myTransformer.fun(aClass)); } }); } final Ref<String> currentBase = Ref.create(null); final Stack<String> stack = new Stack<String>(); // there are two sets for memory optimization: it's cheaper to hold FQN than PsiClass final Set<String> processedFqns = new THashSet<String>(); // FQN of processed classes if the class has one final Processor<DotNetTypeDeclaration> processor = new Processor<DotNetTypeDeclaration>() { @Override public boolean process(final DotNetTypeDeclaration candidate) { ProgressIndicatorProvider.checkCanceled(); final Ref<Boolean> result = new Ref<Boolean>(); final Ref<String> vmQNameRef = new Ref<String>(); ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override public void run() { vmQNameRef.set(candidate.getVmQName()); if (parameters.isCheckInheritance() || parameters.isCheckDeep()) { if (!candidate.isInheritor(currentBase.get(), false)) { result.set(true); return; } } if (PsiSearchScopeUtil.isInScope(searchScope, candidate)) { final String name = candidate.getName(); if (name != null && parameters.getNameCondition().value(name) && !consumer.process(parameters.myTransformer.fun(candidate))) { result.set(false); } } } }); if (!result.isNull()) { return result.get(); } if (parameters.isCheckDeep() && !isSealed(candidate)) { stack.push(vmQNameRef.get()); } return true; } }; stack.push(baseVmQName); final GlobalSearchScope projectScope = GlobalSearchScope.allScope(parameters.getProject()); while (!stack.isEmpty()) { ProgressIndicatorProvider.checkCanceled(); String vmQName = stack.pop(); if (!processedFqns.add(vmQName)) { continue; } currentBase.set(vmQName); if (!DirectTypeInheritorsSearch.search(parameters.getProject(), vmQName, projectScope, false) .forEach(processor)) { return false; } } return true; }