File indexing completed on 2024-05-12 04:38:00

0001 /*
0002     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0003     SPDX-FileCopyrightText: 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "duchainutils.h"
0009 
0010 #include <algorithm>
0011 
0012 #include <interfaces/icore.h>
0013 #include <interfaces/ilanguagecontroller.h>
0014 
0015 #include "../interfaces/ilanguagesupport.h"
0016 #include "../assistant/staticassistantsmanager.h"
0017 #include <debug.h>
0018 
0019 #include "declaration.h"
0020 #include "classfunctiondeclaration.h"
0021 #include "ducontext.h"
0022 #include "duchain.h"
0023 #include "use.h"
0024 #include "duchainlock.h"
0025 #include "classmemberdeclaration.h"
0026 #include "functiondefinition.h"
0027 #include "specializationstore.h"
0028 #include "persistentsymboltable.h"
0029 #include "classdeclaration.h"
0030 #include "parsingenvironment.h"
0031 
0032 #include <QStandardPaths>
0033 
0034 using namespace KDevelop;
0035 using namespace KTextEditor;
0036 
0037 CodeCompletionModel::CompletionProperties DUChainUtils::completionProperties(const Declaration* dec)
0038 {
0039   CodeCompletionModel::CompletionProperties p;
0040 
0041   if(dec->context()->type() == DUContext::Class) {
0042     if (const auto* member = dynamic_cast<const ClassMemberDeclaration*>(dec)) {
0043       switch (member->accessPolicy()) {
0044         case Declaration::Public:
0045           p |= CodeCompletionModel::Public;
0046           break;
0047         case Declaration::Protected:
0048           p |= CodeCompletionModel::Protected;
0049           break;
0050         case Declaration::Private:
0051           p |= CodeCompletionModel::Private;
0052           break;
0053         default:
0054           break;
0055       }
0056 
0057       if (member->isStatic())
0058         p |= CodeCompletionModel::Static;
0059       if (member->isAuto())
0060         {}//TODO
0061       if (member->isFriend())
0062         p |= CodeCompletionModel::Friend;
0063       if (member->isRegister())
0064         {}//TODO
0065       if (member->isExtern())
0066         {}//TODO
0067       if (member->isMutable())
0068         {}//TODO
0069     }
0070   }
0071 
0072   if (const auto* function = dynamic_cast<const AbstractFunctionDeclaration*>(dec)) {
0073     p |= CodeCompletionModel::Function;
0074     if (function->isVirtual())
0075       p |= CodeCompletionModel::Virtual;
0076     if (function->isInline())
0077       p |= CodeCompletionModel::Inline;
0078     if (function->isExplicit())
0079       {}//TODO
0080   }
0081 
0082   if( dec->isTypeAlias() )
0083     p |= CodeCompletionModel::TypeAlias;
0084 
0085   if (dec->abstractType()) {
0086     switch (dec->abstractType()->whichType()) {
0087       case AbstractType::TypeIntegral:
0088         p |= CodeCompletionModel::Variable;
0089         break;
0090       case AbstractType::TypePointer:
0091         p |= CodeCompletionModel::Variable;
0092         break;
0093       case AbstractType::TypeReference:
0094         p |= CodeCompletionModel::Variable;
0095         break;
0096       case AbstractType::TypeFunction:
0097         p |= CodeCompletionModel::Function;
0098         break;
0099       case AbstractType::TypeStructure:
0100         p |= CodeCompletionModel::Class;
0101         break;
0102       case AbstractType::TypeArray:
0103         p |= CodeCompletionModel::Variable;
0104         break;
0105       case AbstractType::TypeEnumeration:
0106         p |= CodeCompletionModel::Enum;
0107         break;
0108       case AbstractType::TypeEnumerator:
0109         p |= CodeCompletionModel::Variable;
0110         break;
0111       case AbstractType::TypeAbstract:
0112       case AbstractType::TypeDelayed:
0113       case AbstractType::TypeUnsure:
0114       case AbstractType::TypeAlias:
0115         // TODO
0116         break;
0117     }
0118 
0119     if( dec->abstractType()->modifiers() & AbstractType::ConstModifier )
0120       p |= CodeCompletionModel::Const;
0121 
0122     if( dec->kind() == Declaration::Instance && !dec->isFunctionDeclaration() )
0123       p |= CodeCompletionModel::Variable;
0124   }
0125 
0126   if (dec->context()) {
0127     if( dec->context()->type() == DUContext::Global )
0128       p |= CodeCompletionModel::GlobalScope;
0129     else if( dec->context()->type() == DUContext::Namespace )
0130       p |= CodeCompletionModel::NamespaceScope;
0131     else if( dec->context()->type() != DUContext::Class && dec->context()->type() != DUContext::Enum )
0132       p |= CodeCompletionModel::LocalScope;
0133   }
0134 
0135   return p;
0136 }
0137 /**We have to construct the item from the pixmap, else the icon will be marked as "load on demand",
0138  * and for some reason will be loaded every time it's used(this function returns a QIcon marked "load on demand"
0139  * each time this is called). And the loading is very slow. Seems like a bug somewhere, it cannot be meant to be that slow.
0140  */
0141 #define RETURN_CACHED_ICON(name) {static QIcon icon(QIcon( \
0142       QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevelop/pics/" name ".png"))\
0143     ).pixmap(QSize(16, 16)));\
0144     return icon;}
0145 
0146 QIcon DUChainUtils::iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p)
0147 {
0148 
0149   if( (p & CodeCompletionModel::Variable) )
0150     if( (p & CodeCompletionModel::Protected) )
0151       RETURN_CACHED_ICON("CVprotected_var")
0152     else if( p & CodeCompletionModel::Private )
0153       RETURN_CACHED_ICON("CVprivate_var")
0154     else
0155       RETURN_CACHED_ICON("CVpublic_var")
0156   else
0157   if( (p & CodeCompletionModel::Union) && (p & CodeCompletionModel::Protected) )
0158     RETURN_CACHED_ICON("protected_union")
0159 
0160   else if( p & CodeCompletionModel::Enum )
0161     if( p & CodeCompletionModel::Protected )
0162       RETURN_CACHED_ICON("protected_enum")
0163     else if( p & CodeCompletionModel::Private )
0164       RETURN_CACHED_ICON("private_enum")
0165     else
0166       RETURN_CACHED_ICON("enum")
0167 
0168   else if( p & CodeCompletionModel::Struct )
0169     if( p & CodeCompletionModel::Private )
0170       RETURN_CACHED_ICON("private_struct")
0171     else
0172       RETURN_CACHED_ICON("struct")
0173 
0174   else if( p & CodeCompletionModel::Slot )
0175     if( p & CodeCompletionModel::Protected )
0176       RETURN_CACHED_ICON("CVprotected_slot")
0177     else if( p & CodeCompletionModel::Private )
0178       RETURN_CACHED_ICON("CVprivate_slot")
0179     else if(p & CodeCompletionModel::Public )
0180       RETURN_CACHED_ICON("CVpublic_slot")
0181     else RETURN_CACHED_ICON("slot")
0182   else if( p & CodeCompletionModel::Signal )
0183     if( p & CodeCompletionModel::Protected )
0184       RETURN_CACHED_ICON("CVprotected_signal")
0185     else
0186       RETURN_CACHED_ICON("signal")
0187 
0188   else if( p & CodeCompletionModel::Class )
0189     if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Protected) )
0190       RETURN_CACHED_ICON("protected_class")
0191     else if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Private) )
0192       RETURN_CACHED_ICON("private_class")
0193     else
0194       RETURN_CACHED_ICON("code-class")
0195 
0196   else if( p & CodeCompletionModel::Union )
0197     if( p & CodeCompletionModel::Private )
0198       RETURN_CACHED_ICON("private_union")
0199     else
0200       RETURN_CACHED_ICON("union")
0201 
0202   else if( p & CodeCompletionModel::TypeAlias )
0203     if ((p & CodeCompletionModel::Const) /*||  (p & CodeCompletionModel::Volatile)*/)
0204       RETURN_CACHED_ICON("CVtypedef")
0205     else
0206       RETURN_CACHED_ICON("typedef")
0207 
0208   else if( p & CodeCompletionModel::Function ) {
0209     if( p & CodeCompletionModel::Protected )
0210       RETURN_CACHED_ICON("protected_function")
0211     else if( p & CodeCompletionModel::Private )
0212       RETURN_CACHED_ICON("private_function")
0213     else
0214       RETURN_CACHED_ICON("code-function")
0215   }
0216 
0217   if( p & CodeCompletionModel::Protected )
0218     RETURN_CACHED_ICON("protected_field")
0219   else if( p & CodeCompletionModel::Private )
0220     RETURN_CACHED_ICON("private_field")
0221   else
0222     RETURN_CACHED_ICON("field")
0223 
0224   return QIcon();
0225 }
0226 
0227 QIcon DUChainUtils::iconForDeclaration(const Declaration* dec)
0228 {
0229   return iconForProperties(completionProperties(dec));
0230 }
0231 
0232 TopDUContext* DUChainUtils::contentContextFromProxyContext(TopDUContext* top)
0233 {
0234   if(!top)
0235     return nullptr;
0236   if(top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->isProxyContext()) {
0237     if(!top->importedParentContexts().isEmpty())
0238     {
0239       DUContext* ctx = top->importedParentContexts().at(0).context(nullptr);
0240       if(!ctx)
0241         return nullptr;
0242       TopDUContext* ret = ctx->topContext();
0243       if(!ret)
0244         return nullptr;
0245       if(ret->url() != top->url())
0246         qCDebug(LANGUAGE) << "url-mismatch between content and proxy:" << top->url().toUrl() << ret->url().toUrl();
0247       if(ret->url() == top->url() && !ret->parsingEnvironmentFile()->isProxyContext())
0248         return ret;
0249     }
0250     else {
0251       qCDebug(LANGUAGE) << "Proxy-context imports no content-context";
0252     }
0253   } else
0254     return top;
0255   return nullptr;
0256 }
0257 
0258 TopDUContext* DUChainUtils::standardContextForUrl(const QUrl& url, bool preferProxyContext) {
0259   KDevelop::TopDUContext* chosen = nullptr;
0260 
0261   const auto languages = ICore::self()->languageController()->languagesForUrl(url);
0262 
0263   for (const auto language : languages) {
0264     if(!chosen)
0265     {
0266       chosen = language->standardContext(url, preferProxyContext);
0267     }
0268   }
0269 
0270   if(!chosen)
0271     chosen = DUChain::self()->chainForDocument(IndexedString(url), preferProxyContext);
0272 
0273   if(!chosen && preferProxyContext)
0274     return standardContextForUrl(url, false); // Fall back to a normal context
0275 
0276   return chosen;
0277 }
0278 
0279 struct ItemUnderCursorInternal
0280 {
0281   Declaration* declaration;
0282   DUContext* context;
0283   RangeInRevision range;
0284 };
0285 
0286 ItemUnderCursorInternal itemUnderCursorInternal(const CursorInRevision& c, DUContext* ctx, RangeInRevision::ContainsBehavior behavior)
0287 {
0288   //Search all collapsed sub-contexts. In C++, those can contain declarations that have ranges out of the context
0289   const auto childContexts = ctx->childContexts();
0290   for (DUContext* subCtx : childContexts) {
0291     //This is a little hacky, but we need it in case of foreach macros and similar stuff
0292     if(subCtx->range().contains(c, behavior) || subCtx->range().isEmpty() || subCtx->range().start.line == c.line || subCtx->range().end.line == c.line) {
0293       ItemUnderCursorInternal sub = itemUnderCursorInternal(c, subCtx, behavior);
0294       if(sub.declaration) {
0295         return sub;
0296       }
0297     }
0298   }
0299 
0300   const auto localDeclarations = ctx->localDeclarations();
0301   for (Declaration* decl : localDeclarations) {
0302     if(decl->range().contains(c, behavior)) {
0303       return {decl, ctx, decl->range()};
0304     }
0305   }
0306 
0307   //Try finding a use under the cursor
0308   for(int a = 0; a < ctx->usesCount(); ++a) {
0309     if(ctx->uses()[a].m_range.contains(c, behavior)) {
0310       return {ctx->topContext()->usedDeclarationForIndex(ctx->uses()[a].m_declarationIndex), ctx, ctx->uses()[a].m_range};
0311     }
0312   }
0313 
0314   return {nullptr, nullptr, RangeInRevision()};
0315 }
0316 
0317 DUChainUtils::ItemUnderCursor DUChainUtils::itemUnderCursor(const QUrl& url, const KTextEditor::Cursor& cursor)
0318 {
0319   KDevelop::TopDUContext* top = standardContextForUrl(url.adjusted(QUrl::NormalizePathSegments));
0320 
0321   if(!top) {
0322     return {nullptr, nullptr, KTextEditor::Range()};
0323   }
0324 
0325   ItemUnderCursorInternal decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::Default);
0326   if (decl.declaration == nullptr)
0327   {
0328     decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::IncludeBackEdge);
0329   }
0330   return {decl.declaration, decl.context, top->transformFromLocalRevision(decl.range)};
0331 }
0332 
0333 Declaration* DUChainUtils::declarationForDefinition(Declaration* definition, TopDUContext* topContext)
0334 {
0335   if(!definition)
0336     return nullptr;
0337 
0338   if(!topContext)
0339     topContext = definition->topContext();
0340 
0341   if(dynamic_cast<FunctionDefinition*>(definition)) {
0342     Declaration* ret = static_cast<FunctionDefinition*>(definition)->declaration();
0343     if(ret)
0344       return ret;
0345   }
0346 
0347   return definition;
0348 }
0349 
0350 Declaration* DUChainUtils::declarationInLine(const KTextEditor::Cursor& _cursor, DUContext* ctx) {
0351   if(!ctx)
0352     return nullptr;
0353 
0354   CursorInRevision cursor = ctx->transformToLocalRevision(_cursor);
0355 
0356   const auto localDeclarations = ctx->localDeclarations();
0357   for (Declaration* decl : localDeclarations) {
0358     if(decl->range().start.line == cursor.line)
0359       return decl;
0360     DUContext* funCtx = functionContext(decl);
0361     if(funCtx && funCtx->range().contains(cursor))
0362       return decl;
0363   }
0364 
0365   const auto childContexts = ctx->childContexts();
0366   for (DUContext* child : childContexts){
0367     Declaration* decl = declarationInLine(_cursor, child);
0368     if(decl)
0369       return decl;
0370   }
0371 
0372   return nullptr;
0373 }
0374 
0375 DUChainUtils::DUChainItemFilter::~DUChainItemFilter() {
0376 }
0377 
0378 void DUChainUtils::collectItems( DUContext* context, DUChainItemFilter& filter ) {
0379 
0380   QVector<DUContext*> children = context->childContexts();
0381   QVector<Declaration*> localDeclarations = context->localDeclarations();
0382 
0383   QVector<DUContext*>::const_iterator childIt = children.constBegin();
0384   QVector<Declaration*>::const_iterator declIt = localDeclarations.constBegin();
0385 
0386   while(childIt != children.constEnd() || declIt != localDeclarations.constEnd()) {
0387 
0388     DUContext* child = nullptr;
0389     if(childIt != children.constEnd())
0390       child = *childIt;
0391 
0392     Declaration* decl = nullptr;
0393     if(declIt != localDeclarations.constEnd())
0394       decl = *declIt;
0395 
0396     if(decl) {
0397       if(child && child->range().start.line >= decl->range().start.line)
0398         child = nullptr;
0399     }
0400 
0401     if(child) {
0402       if(decl && decl->range().start >= child->range().start)
0403         decl = nullptr;
0404     }
0405 
0406     if(decl) {
0407       if( filter.accept(decl) ) {
0408         //Action is done in the filter
0409       }
0410 
0411       ++declIt;
0412       continue;
0413     }
0414 
0415     if(child) {
0416       if( filter.accept(child) )
0417         collectItems(child, filter);
0418       ++childIt;
0419       continue;
0420     }
0421   }
0422 }
0423 
0424 KDevelop::DUContext* DUChainUtils::argumentContext(KDevelop::Declaration* decl) {
0425   DUContext* internal = decl->internalContext();
0426   if( !internal )
0427     return nullptr;
0428   if( internal->type() == DUContext::Function )
0429     return internal;
0430   const auto importedParentContexts = internal->importedParentContexts();
0431   for (const DUContext::Import& ctx : importedParentContexts) {
0432     if( ctx.context(decl->topContext()) )
0433       if( ctx.context(decl->topContext())->type() == DUContext::Function )
0434         return ctx.context(decl->topContext());
0435   }
0436   return nullptr;
0437 }
0438 
0439 QList<IndexedDeclaration> DUChainUtils::collectAllVersions(Declaration* decl) {
0440     const auto indexedDecl = IndexedDeclaration(decl);
0441 
0442     QList<IndexedDeclaration> ret;
0443     ret << indexedDecl;
0444 
0445     if (decl->inSymbolTable()) {
0446         auto visitor = [&](const IndexedDeclaration& indexed) {
0447             if (indexed != indexedDecl) {
0448                 ret << indexed;
0449             }
0450             return PersistentSymbolTable::VisitorState::Continue;
0451         };
0452         PersistentSymbolTable::self().visitDeclarations(decl->qualifiedIdentifier(), visitor);
0453     }
0454 
0455   return ret;
0456 }
0457 
0458 static QList<Declaration*> inheritersInternal(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions)
0459 {
0460   QList<Declaration*> ret;
0461 
0462   if(!dynamic_cast<const ClassDeclaration*>(decl))
0463     return ret;
0464 
0465   if(maxAllowedSteps == 0)
0466     return ret;
0467 
0468   if(decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
0469     const auto indexedImporters = decl->internalContext()->indexedImporters();
0470     for (const IndexedDUContext importer : indexedImporters) {
0471 
0472       DUContext* imp = importer.data();
0473 
0474       if(!imp)
0475         continue;
0476 
0477       if(imp->type() == DUContext::Class && imp->owner())
0478         ret << imp->owner();
0479 
0480       --maxAllowedSteps;
0481 
0482       if(maxAllowedSteps == 0)
0483         return ret;
0484     }
0485   }
0486 
0487   if(collectVersions && decl->inSymbolTable()) {
0488       auto visitor = [&](const IndexedDeclaration& indexedDeclaration) {
0489           ++maxAllowedSteps;
0490           auto declaration = indexedDeclaration.data();
0491           if (declaration && declaration != decl) {
0492               ret += inheritersInternal(declaration, maxAllowedSteps, false);
0493           }
0494 
0495           if (maxAllowedSteps == 0) {
0496               return PersistentSymbolTable::VisitorState::Break;
0497           } else {
0498               return PersistentSymbolTable::VisitorState::Continue;
0499           }
0500       };
0501       PersistentSymbolTable::self().visitDeclarations(decl->qualifiedIdentifier(), visitor);
0502   }
0503 
0504   return ret;
0505 }
0506 
0507 QList<Declaration*> DUChainUtils::inheriters(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions)
0508 {
0509   auto inheriters = inheritersInternal(decl, maxAllowedSteps, collectVersions);
0510 
0511   // remove duplicates
0512   std::sort(inheriters.begin(), inheriters.end());
0513   inheriters.erase(std::unique(inheriters.begin(), inheriters.end()), inheriters.end());
0514 
0515   return inheriters;
0516 }
0517 
0518 QList<Declaration*> DUChainUtils::overriders(const Declaration* currentClass, const Declaration* overriddenDeclaration, uint& maxAllowedSteps) {
0519   QList<Declaration*> ret;
0520 
0521   if(maxAllowedSteps == 0)
0522     return ret;
0523 
0524   if(currentClass != overriddenDeclaration->context()->owner() && currentClass->internalContext())
0525     ret += currentClass->internalContext()->findLocalDeclarations(overriddenDeclaration->identifier(), CursorInRevision::invalid(), currentClass->topContext(), overriddenDeclaration->abstractType());
0526 
0527   const auto inheriters = DUChainUtils::inheriters(currentClass, maxAllowedSteps);
0528   for (Declaration* inheriter : inheriters) {
0529     ret += overriders(inheriter, overriddenDeclaration, maxAllowedSteps);
0530   }
0531 
0532   return ret;
0533 }
0534 
0535 static bool hasUse(DUContext* context, int usedDeclarationIndex) {
0536   if(usedDeclarationIndex == std::numeric_limits<int>::max())
0537     return false;
0538 
0539   for(int a = 0; a < context->usesCount(); ++a)
0540     if(context->uses()[a].m_declarationIndex == usedDeclarationIndex)
0541       return true;
0542 
0543   const auto childContexts = context->childContexts();
0544   return std::any_of(childContexts.begin(), childContexts.end(), [&](DUContext* child) {
0545     return hasUse(child, usedDeclarationIndex);
0546   });
0547 }
0548 
0549 bool DUChainUtils::contextHasUse(DUContext* context, Declaration* declaration) {
0550   return hasUse(context, context->topContext()->indexForUsedDeclaration(declaration, false));
0551 }
0552 
0553 static uint countUses(DUContext* context, int usedDeclarationIndex) {
0554   if(usedDeclarationIndex == std::numeric_limits<int>::max())
0555     return 0;
0556 
0557   uint ret = 0;
0558 
0559   for(int a = 0; a < context->usesCount(); ++a)
0560     if(context->uses()[a].m_declarationIndex == usedDeclarationIndex)
0561       ++ret;
0562 
0563   const auto childContexts = context->childContexts();
0564   for (DUContext* child : childContexts) {
0565     ret += countUses(child, usedDeclarationIndex);
0566   }
0567 
0568   return ret;
0569 }
0570 
0571 uint DUChainUtils::contextCountUses(DUContext* context, Declaration* declaration) {
0572   return countUses(context, context->topContext()->indexForUsedDeclaration(declaration, false));
0573 }
0574 
0575 Declaration* DUChainUtils::overridden(const Declaration* decl) {
0576   const auto* classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(decl);
0577   if(!classFunDecl || !classFunDecl->isVirtual())
0578     return nullptr;
0579 
0580   QList<Declaration*> decls;
0581 
0582   const auto importedParentContexts = decl->context()->importedParentContexts();
0583   for (const DUContext::Import &import : importedParentContexts) {
0584     DUContext* ctx = import.context(decl->topContext());
0585     if(ctx)
0586       decls += ctx->findDeclarations(QualifiedIdentifier(decl->identifier()),
0587                                             CursorInRevision::invalid(), decl->abstractType(), decl->topContext(), DUContext::DontSearchInParent);
0588   }
0589 
0590   auto it = std::find_if(decls.constBegin(), decls.constEnd(), [&](Declaration* found) {
0591     const auto* foundClassFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(found);
0592     return (foundClassFunDecl && foundClassFunDecl->isVirtual());
0593   });
0594 
0595   return (it != decls.constEnd()) ? *it : nullptr;
0596 }
0597 
0598 DUContext* DUChainUtils::functionContext(Declaration* decl) {
0599   DUContext* functionContext = decl->internalContext();
0600   if(functionContext && functionContext->type() != DUContext::Function) {
0601     const auto importedParentContexts = functionContext->importedParentContexts();
0602     for (const DUContext::Import& import : importedParentContexts) {
0603       DUContext* ctx = import.context(decl->topContext());
0604       if(ctx && ctx->type() == DUContext::Function)
0605         functionContext = ctx;
0606     }
0607   }
0608 
0609   if(functionContext && functionContext->type() == DUContext::Function)
0610     return functionContext;
0611   return nullptr;
0612 }
0613 
0614 QVector<KDevelop::Problem::Ptr> KDevelop::DUChainUtils::allProblemsForContext(const KDevelop::ReferencedTopDUContext& top)
0615 {
0616     QVector<KDevelop::Problem::Ptr> ret;
0617 
0618     const auto problems = top->problems();
0619     const auto contextProblems = ICore::self()->languageController()->staticAssistantsManager()->problemsForContext(top);
0620     ret.reserve(problems.size() + contextProblems.size());
0621 
0622     for (const auto& p : problems) {
0623         ret << p;
0624     }
0625     for (const auto& p : contextProblems) {
0626         ret << p;
0627     }
0628 
0629     return ret;
0630 }
0631