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