File indexing completed on 2024-05-12 04:38:04
0001 /* 0002 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org> 0003 SPDX-FileCopyrightText: 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "topducontext.h" 0009 #include "topducontextutils.h" 0010 0011 #include <limits> 0012 0013 #include "persistentsymboltable.h" 0014 #include "problem.h" 0015 #include "declaration.h" 0016 #include "duchain.h" 0017 #include "duchainlock.h" 0018 #include "parsingenvironment.h" 0019 #include "duchainpointer.h" 0020 #include "declarationid.h" 0021 #include "namespacealiasdeclaration.h" 0022 #include "aliasdeclaration.h" 0023 #include "uses.h" 0024 #include "topducontextdata.h" 0025 #include "duchainregister.h" 0026 #include "topducontextdynamicdata.h" 0027 #include <debug.h> 0028 0029 #include <language/interfaces/iastcontainer.h> 0030 0031 #include <QMutexLocker> 0032 #include <QRecursiveMutex> 0033 0034 // #define DEBUG_SEARCH 0035 0036 const uint maxApplyAliasesRecursion = 100; 0037 0038 namespace KDevelop { 0039 Utils::BasicSetRepository* RecursiveImportRepository::repository() 0040 { 0041 static QRecursiveMutex mutex; 0042 static Utils::BasicSetRepository recursiveImportRepositoryObject(QStringLiteral("Recursive Imports"), &mutex, 0043 &KDevelop::globalItemRepositoryRegistry()); 0044 return &recursiveImportRepositoryObject; 0045 } 0046 0047 ReferencedTopDUContext::ReferencedTopDUContext(TopDUContext* context) : m_topContext(context) 0048 { 0049 if (m_topContext) 0050 DUChain::self()->refCountUp(m_topContext); 0051 } 0052 0053 ReferencedTopDUContext::ReferencedTopDUContext(const ReferencedTopDUContext& rhs) : m_topContext(rhs.m_topContext) 0054 { 0055 if (m_topContext) 0056 DUChain::self()->refCountUp(m_topContext); 0057 } 0058 0059 ReferencedTopDUContext::~ReferencedTopDUContext() 0060 { 0061 if (m_topContext && !DUChain::deleted()) 0062 DUChain::self()->refCountDown(m_topContext); 0063 } 0064 0065 ReferencedTopDUContext& ReferencedTopDUContext::operator=(const ReferencedTopDUContext& rhs) 0066 { 0067 if (m_topContext == rhs.m_topContext) 0068 return *this; 0069 0070 if (m_topContext) 0071 DUChain::self()->refCountDown(m_topContext); 0072 0073 m_topContext = rhs.m_topContext; 0074 0075 if (m_topContext) 0076 DUChain::self()->refCountUp(m_topContext); 0077 return *this; 0078 } 0079 0080 DEFINE_LIST_MEMBER_HASH(TopDUContextData, m_usedDeclarationIds, DeclarationId) 0081 DEFINE_LIST_MEMBER_HASH(TopDUContextData, m_problems, LocalIndexedProblem) 0082 REGISTER_DUCHAIN_ITEM(TopDUContext); 0083 0084 QRecursiveMutex importStructureMutex; 0085 0086 //Contains data that is not shared among top-contexts that share their duchain entries 0087 class TopDUContextLocalPrivate 0088 { 0089 public: 0090 TopDUContextLocalPrivate (TopDUContext* ctxt, uint index) : 0091 m_ctxt(ctxt) 0092 , m_ownIndex(index) 0093 , m_inDuChain(false) 0094 { 0095 m_indexedRecursiveImports.insert(index); 0096 } 0097 0098 ~TopDUContextLocalPrivate() 0099 { 0100 //Either we use some other contexts data and have no users, or we own the data and have users that share it. 0101 QMutexLocker lock(&importStructureMutex); 0102 0103 for (const DUContext::Import& import : qAsConst(m_importedContexts)) { 0104 if (DUChain::self()->isInMemory(import.topContextIndex()) && 0105 dynamic_cast<TopDUContext*>(import.context(nullptr))) 0106 dynamic_cast<TopDUContext*>(import.context(nullptr))->m_local->m_directImporters.remove(m_ctxt); 0107 } 0108 } 0109 0110 ///@todo Make all this work consistently together with import-caching 0111 0112 //After loading, should rebuild the links 0113 void rebuildDynamicImportStructure() 0114 { 0115 //Currently we do not store the whole data in TopDUContextLocalPrivate, so we reconstruct it from what was stored by DUContext. 0116 Q_ASSERT(m_importedContexts.isEmpty()); 0117 0118 FOREACH_FUNCTION(const DUContext::Import& import, m_ctxt->d_func()->m_importedContexts) { 0119 if (DUChain::self()->isInMemory(import.topContextIndex())) { 0120 Q_ASSERT(import.context(nullptr)); 0121 TopDUContext* top = import.context(nullptr)->topContext(); 0122 Q_ASSERT(top); 0123 addImportedContextRecursively(top, false, true); 0124 } 0125 } 0126 FOREACH_FUNCTION(const IndexedDUContext &importer, m_ctxt->d_func()->m_importers) { 0127 if (DUChain::self()->isInMemory(importer.topContextIndex())) { 0128 Q_ASSERT(importer.context()); 0129 TopDUContext* top = importer.context()->topContext(); 0130 Q_ASSERT(top); 0131 top->m_local->addImportedContextRecursively(m_ctxt, false, true); 0132 } 0133 } 0134 } 0135 0136 //Index of this top-context within the duchain 0137 //Since the data of top-contexts can be shared among multiple, this can be used to add imports that are local to this top-context. 0138 QVector<DUContext::Import> m_importedContexts; 0139 // mutable bool m_haveImportStructure : 1; 0140 TopDUContext* m_ctxt; 0141 0142 QSet<DUContext*> m_directImporters; 0143 0144 ParsingEnvironmentFilePointer m_file; 0145 0146 QExplicitlySharedDataPointer<IAstContainer> m_ast; 0147 0148 uint m_ownIndex; 0149 0150 bool m_inDuChain; 0151 0152 void clearImportedContextsRecursively() 0153 { 0154 QMutexLocker lock(&importStructureMutex); 0155 0156 // Q_ASSERT(m_recursiveImports.size() == m_indexedRecursiveImports.count()-1); 0157 0158 QSet<QPair<TopDUContext*, const TopDUContext*>> rebuild; 0159 0160 for (const DUContext::Import& import : qAsConst(m_importedContexts)) { 0161 auto* top = dynamic_cast<TopDUContext*>(import.context(nullptr)); 0162 if (top) { 0163 top->m_local->m_directImporters.remove(m_ctxt); 0164 0165 if (!m_ctxt->usingImportsCache()) { 0166 removeImportedContextRecursion(top, top, 1, rebuild); 0167 0168 QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = top->m_local->m_recursiveImports; 0169 for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { 0170 if (m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == top) 0171 removeImportedContextRecursion(top, it.key(), it->first + 1, rebuild); //Remove all contexts that are imported through the context 0172 } 0173 } 0174 } 0175 } 0176 0177 m_importedContexts.clear(); 0178 0179 rebuildImportStructureRecursion(rebuild); 0180 0181 Q_ASSERT(m_recursiveImports.isEmpty()); 0182 // Q_ASSERT(m_recursiveImports.size() == m_indexedRecursiveImports.count()-1); 0183 } 0184 0185 //Adds the context to this and all contexts that import this, and manages m_recursiveImports 0186 void addImportedContextRecursively(TopDUContext* context, bool temporary, bool local) 0187 { 0188 QMutexLocker lock(&importStructureMutex); 0189 0190 context->m_local->m_directImporters.insert(m_ctxt); 0191 0192 if (local) { 0193 // note: m_importedContexts may end up with duplicate entries -- not sure whether we should protect against this --Kevin 0194 m_importedContexts << DUContext::Import(context, m_ctxt); 0195 } 0196 0197 if (!m_ctxt->usingImportsCache()) { 0198 addImportedContextRecursion(context, context, 1, temporary); 0199 0200 QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = context->m_local->m_recursiveImports; 0201 for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) 0202 addImportedContextRecursion(context, it.key(), (*it).first + 1, temporary); //Add contexts that were imported earlier into the given one 0203 } 0204 } 0205 0206 //Removes the context from this and all contexts that import this, and manages m_recursiveImports 0207 void removeImportedContextRecursively(TopDUContext* context, bool local) 0208 { 0209 QMutexLocker lock(&importStructureMutex); 0210 0211 context->m_local->m_directImporters.remove(m_ctxt); 0212 0213 if (local) 0214 m_importedContexts.removeAll(DUContext::Import(context, m_ctxt)); 0215 0216 QSet<QPair<TopDUContext*, const TopDUContext*>> rebuild; 0217 if (!m_ctxt->usingImportsCache()) { 0218 removeImportedContextRecursion(context, context, 1, rebuild); 0219 0220 QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = context->m_local->m_recursiveImports; 0221 for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { 0222 if (m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == context) 0223 removeImportedContextRecursion(context, it.key(), it->first + 1, rebuild); //Remove all contexts that are imported through the context 0224 } 0225 } 0226 0227 rebuildImportStructureRecursion(rebuild); 0228 } 0229 0230 void removeImportedContextsRecursively(const QList<TopDUContext*>& contexts, bool local) 0231 { 0232 QMutexLocker lock(&importStructureMutex); 0233 0234 QSet<QPair<TopDUContext*, const TopDUContext*>> rebuild; 0235 for (TopDUContext* context : contexts) { 0236 context->m_local->m_directImporters.remove(m_ctxt); 0237 0238 if (local) 0239 m_importedContexts.removeAll(DUContext::Import(context, m_ctxt)); 0240 0241 if (!m_ctxt->usingImportsCache()) { 0242 removeImportedContextRecursion(context, context, 1, rebuild); 0243 0244 QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = context->m_local->m_recursiveImports; 0245 for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { 0246 const auto recursiveImportIt = m_recursiveImports.constFind(it.key()); 0247 if (recursiveImportIt != m_recursiveImports.constEnd() && recursiveImportIt->second == context) 0248 removeImportedContextRecursion(context, it.key(), it->first + 1, rebuild); //Remove all contexts that are imported through the context 0249 } 0250 } 0251 } 0252 0253 rebuildImportStructureRecursion(rebuild); 0254 } 0255 0256 //Has an entry for every single recursively imported file, that contains the shortest path, and the next context on that path to the imported context. 0257 //This does not need to be stored to disk, because it is defined implicitly. 0258 //What makes this most complicated is the fact that loops are allowed in the import structure. 0259 using RecursiveImports = QHash<const TopDUContext*, QPair<int, const TopDUContext*>>; 0260 mutable RecursiveImports m_recursiveImports; 0261 mutable TopDUContext::IndexedRecursiveImports m_indexedRecursiveImports; 0262 0263 private: 0264 void addImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int depth, 0265 bool temporary = false) 0266 { 0267 if (m_ctxt->usingImportsCache()) 0268 return; 0269 0270 // if(!m_haveImportStructure) 0271 // return; 0272 0273 if (imported == m_ctxt) 0274 return; 0275 0276 const bool computeShortestPaths = false; ///@todo We do not compute the shortest path. Think what's right. 0277 0278 // traceNext->m_local->needImportStructure(); 0279 // imported->m_local->needImportStructure(); 0280 0281 RecursiveImports::iterator it = m_recursiveImports.find(imported); 0282 if (it == m_recursiveImports.end()) { 0283 //Insert new path to "imported" 0284 m_recursiveImports[imported] = qMakePair(depth, traceNext); 0285 0286 m_indexedRecursiveImports.insert(imported->indexed()); 0287 // Q_ASSERT(m_indexedRecursiveImports.size() == m_recursiveImports.size()+1); 0288 0289 Q_ASSERT(traceNext != m_ctxt); 0290 } else { 0291 if (!computeShortestPaths) 0292 return; 0293 0294 if (temporary) //For temporary imports, we don't record the best path. 0295 return; 0296 //It would be better if we would use the following code, but it creates too much cost in updateImportedContextRecursion when imports are removed again. 0297 0298 //Check whether the new way to "imported" is shorter than the stored one 0299 if ((*it).first > depth) { 0300 //Add a shorter path 0301 (*it).first = depth; 0302 Q_ASSERT(traceNext); 0303 (*it).second = traceNext; 0304 Q_ASSERT(traceNext == imported || 0305 (traceNext->m_local->m_recursiveImports.contains(imported) && 0306 traceNext->m_local->m_recursiveImports[imported].first < (*it).first)); 0307 } else { 0308 //The imported context is already imported through a same/better path, so we can just stop processing. This saves us from endless recursion. 0309 return; 0310 } 0311 } 0312 0313 if (temporary) 0314 return; 0315 0316 for (auto* context : qAsConst(m_directImporters)) { 0317 auto* top = dynamic_cast<TopDUContext*>(context); 0318 if (top) ///@todo also record this for local imports 0319 top->m_local->addImportedContextRecursion(m_ctxt, imported, depth + 1); 0320 } 0321 } 0322 0323 void removeImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int distance, 0324 QSet<QPair<TopDUContext*, const TopDUContext*>>& rebuild) 0325 { 0326 if (m_ctxt->usingImportsCache()) 0327 return; 0328 0329 if (imported == m_ctxt) 0330 return; 0331 0332 // if(!m_haveImportStructure) 0333 // return; 0334 0335 RecursiveImports::iterator it = m_recursiveImports.find(imported); 0336 if (it == m_recursiveImports.end()) { 0337 //We don't import. Just return, this saves us from endless recursion. 0338 return; 0339 } else { 0340 //Check whether we have imported "imported" through "traceNext". If not, return. Else find a new trace. 0341 if ((*it).second == traceNext && (*it).first == distance) { 0342 //We need to remove the import through traceNext. Check whether there is another imported context that imports it. 0343 0344 m_recursiveImports.erase(it); //In order to prevent problems, we completely remove everything, and re-add it. 0345 //Just updating these complex structures is very hard. 0346 Q_ASSERT(imported != m_ctxt); 0347 0348 m_indexedRecursiveImports.remove(imported->indexed()); 0349 // Q_ASSERT(m_indexedRecursiveImports.size() == m_recursiveImports.size()); 0350 0351 rebuild.insert(qMakePair(m_ctxt, imported)); 0352 //We MUST do this before finding another trace, because else we would create loops 0353 for (QSet<DUContext*>::const_iterator childIt = m_directImporters.constBegin(); 0354 childIt != m_directImporters.constEnd(); ++childIt) { 0355 auto* top = dynamic_cast<TopDUContext*>(const_cast<DUContext*>(*childIt)); //Avoid detaching, so use const iterator 0356 if (top) 0357 top->m_local->removeImportedContextRecursion(m_ctxt, imported, distance + 1, rebuild); //Don't use 'it' from here on, it may be invalid 0358 } 0359 } 0360 } 0361 } 0362 0363 //Updates the trace to 'imported' 0364 void rebuildStructure(const TopDUContext* imported); 0365 0366 void rebuildImportStructureRecursion(const QSet<QPair<TopDUContext*, const TopDUContext*>>& rebuild) 0367 { 0368 for (auto& rebuildPair : rebuild) { 0369 //for(int a = rebuild.size()-1; a >= 0; --a) { 0370 //Find the best imported parent 0371 rebuildPair.first->m_local->rebuildStructure(rebuildPair.second); 0372 } 0373 } 0374 }; 0375 0376 const TopDUContext::IndexedRecursiveImports& TopDUContext::recursiveImportIndices() const 0377 { 0378 // No lock-check for performance reasons 0379 QMutexLocker lock(&importStructureMutex); 0380 if (!d_func()->m_importsCache.isEmpty()) 0381 return d_func()->m_importsCache; 0382 0383 return m_local->m_indexedRecursiveImports; 0384 } 0385 0386 void TopDUContextData::updateImportCacheRecursion(uint baseIndex, IndexedTopDUContext currentContext, 0387 TopDUContext::IndexedRecursiveImports& visited) 0388 { 0389 if (visited.contains(currentContext.index())) 0390 return; 0391 Q_ASSERT(currentContext.index()); //The top-context must be in the repository when this is called 0392 if (!currentContext.data()) { 0393 qCDebug(LANGUAGE) << "importing invalid context"; 0394 return; 0395 } 0396 visited.insert(currentContext.index()); 0397 0398 const TopDUContextData* currentData = currentContext.data()->topContext()->d_func(); 0399 if (currentData->m_importsCache.contains(baseIndex) || currentData->m_importsCache.isEmpty()) { 0400 //If we have a loop or no imports-cache is used, we have to look at each import separately. 0401 const KDevelop::DUContext::Import* imports = currentData->m_importedContexts(); 0402 uint importsSize = currentData->m_importedContextsSize(); 0403 for (uint a = 0; a < importsSize; ++a) { 0404 IndexedTopDUContext next(imports[a].topContextIndex()); 0405 if (next.isValid()) 0406 updateImportCacheRecursion(baseIndex, next, visited); 0407 } 0408 } else { 0409 //If we don't have a loop with baseIndex, we can safely just merge with the imported importscache 0410 visited += currentData->m_importsCache; 0411 } 0412 } 0413 0414 void TopDUContextData::updateImportCacheRecursion(IndexedTopDUContext currentContext, std::set<uint>& visited) 0415 { 0416 if (visited.find(currentContext.index()) != visited.end()) 0417 return; 0418 Q_ASSERT(currentContext.index()); //The top-context must be in the repository when this is called 0419 if (!currentContext.data()) { 0420 qCDebug(LANGUAGE) << "importing invalid context"; 0421 return; 0422 } 0423 visited.insert(currentContext.index()); 0424 const TopDUContextData* currentData = currentContext.data()->topContext()->d_func(); 0425 const KDevelop::DUContext::Import* imports = currentData->m_importedContexts(); 0426 uint importsSize = currentData->m_importedContextsSize(); 0427 for (uint a = 0; a < importsSize; ++a) { 0428 IndexedTopDUContext next(imports[a].topContextIndex()); 0429 if (next.isValid()) 0430 updateImportCacheRecursion(next, visited); 0431 } 0432 } 0433 0434 void TopDUContext::updateImportsCache() 0435 { 0436 QMutexLocker lock(&importStructureMutex); 0437 0438 const bool use_fully_recursive_import_cache_computation = false; 0439 0440 if (use_fully_recursive_import_cache_computation) { 0441 std::set<uint> visited; 0442 TopDUContextData::updateImportCacheRecursion(this, visited); 0443 Q_ASSERT(visited.find(ownIndex()) != visited.end()); 0444 d_func_dynamic()->m_importsCache = IndexedRecursiveImports(visited); 0445 } else { 0446 d_func_dynamic()->m_importsCache = IndexedRecursiveImports(); 0447 TopDUContextData::updateImportCacheRecursion(ownIndex(), this, d_func_dynamic()->m_importsCache); 0448 } 0449 Q_ASSERT(d_func_dynamic()->m_importsCache.contains(IndexedTopDUContext(this))); 0450 Q_ASSERT(usingImportsCache()); 0451 Q_ASSERT(imports(this, CursorInRevision::invalid())); 0452 0453 if (parsingEnvironmentFile()) 0454 parsingEnvironmentFile()->setImportsCache(d_func()->m_importsCache); 0455 } 0456 0457 bool TopDUContext::usingImportsCache() const 0458 { 0459 return !d_func()->m_importsCache.isEmpty(); 0460 } 0461 0462 CursorInRevision TopDUContext::importPosition(const DUContext* target) const 0463 { 0464 ENSURE_CAN_READ 0465 DUCHAIN_D(DUContext); 0466 Import import(const_cast<DUContext*>(target), const_cast<TopDUContext*>(this), CursorInRevision::invalid()); 0467 for (unsigned int a = 0; a < d->m_importedContextsSize(); ++a) 0468 if (d->m_importedContexts()[a] == import) 0469 return d->m_importedContexts()[a].position; 0470 0471 return DUContext::importPosition(target); 0472 } 0473 0474 void TopDUContextLocalPrivate::rebuildStructure(const TopDUContext* imported) 0475 { 0476 if (m_ctxt == imported) 0477 return; 0478 0479 for (auto& importedContext : qAsConst(m_importedContexts)) { 0480 auto* top = dynamic_cast<TopDUContext*>(importedContext.context(nullptr)); 0481 if (top) { 0482 // top->m_local->needImportStructure(); 0483 if (top == imported) { 0484 addImportedContextRecursion(top, imported, 1); 0485 } else { 0486 RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.constFind(imported); 0487 if (it2 != top->m_local->m_recursiveImports.constEnd()) { 0488 addImportedContextRecursion(top, imported, (*it2).first + 1); 0489 } 0490 } 0491 } 0492 } 0493 0494 for (unsigned int a = 0; a < m_ctxt->d_func()->m_importedContextsSize(); ++a) { 0495 auto* top = 0496 dynamic_cast<TopDUContext*>(const_cast<DUContext*>(m_ctxt->d_func()->m_importedContexts()[a].context(nullptr))); //To avoid detaching, use const iterator 0497 if (top) { 0498 // top->m_local->needImportStructure(); 0499 if (top == imported) { 0500 addImportedContextRecursion(top, imported, 1); 0501 } else { 0502 RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.constFind(imported); 0503 if (it2 != top->m_local->m_recursiveImports.constEnd()) { 0504 addImportedContextRecursion(top, imported, (*it2).first + 1); 0505 } 0506 } 0507 } 0508 } 0509 } 0510 0511 void TopDUContext::rebuildDynamicImportStructure() 0512 { 0513 m_local->rebuildDynamicImportStructure(); 0514 } 0515 0516 void TopDUContext::rebuildDynamicData(DUContext* parent, uint ownIndex) 0517 { 0518 Q_ASSERT(parent == nullptr && ownIndex != 0); 0519 m_local->m_ownIndex = ownIndex; 0520 0521 DUContext::rebuildDynamicData(parent, 0); 0522 } 0523 0524 IndexedTopDUContext TopDUContext::indexed() const 0525 { 0526 return IndexedTopDUContext(m_local->m_ownIndex); 0527 } 0528 0529 uint TopDUContext::ownIndex() const 0530 { 0531 return m_local->m_ownIndex; 0532 } 0533 0534 TopDUContext::TopDUContext(TopDUContextData& data) : DUContext(data) 0535 , m_local(new TopDUContextLocalPrivate(this, data.m_ownIndex)) 0536 , m_dynamicData(new TopDUContextDynamicData(this)) 0537 { 0538 initFromTopContext(); 0539 } 0540 0541 TopDUContext::TopDUContext(const IndexedString& url, const RangeInRevision& range, ParsingEnvironmentFile* file) 0542 : DUContext(*new TopDUContextData(url), range) 0543 , m_local(new TopDUContextLocalPrivate(this, DUChain::newTopContextIndex())) 0544 , m_dynamicData(new TopDUContextDynamicData(this)) 0545 { 0546 initFromTopContext(); 0547 0548 Q_ASSERT(url.toUrl().isValid() && !url.toUrl().isRelative()); 0549 d_func_dynamic()->setClassId(this); 0550 setType(Global); 0551 0552 DUCHAIN_D_DYNAMIC(TopDUContext); 0553 d->m_features = VisibleDeclarationsAndContexts; 0554 d->m_ownIndex = m_local->m_ownIndex; 0555 setParsingEnvironmentFile(file); 0556 setInSymbolTable(true); 0557 } 0558 0559 QExplicitlySharedDataPointer<ParsingEnvironmentFile> TopDUContext::parsingEnvironmentFile() const 0560 { 0561 return m_local->m_file; 0562 } 0563 0564 TopDUContext::~TopDUContext() 0565 { 0566 m_dynamicData->m_deleting = true; 0567 0568 //Clear the AST, so that the 'feature satisfaction' cache is eventually updated 0569 clearAst(); 0570 0571 if (!isOnDisk()) { 0572 //Clear the 'feature satisfaction' cache which is managed in ParsingEnvironmentFile 0573 setFeatures(Empty); 0574 0575 clearUsedDeclarationIndices(); 0576 } 0577 0578 deleteChildContextsRecursively(); 0579 deleteLocalDeclarations(); 0580 m_dynamicData->clear(); 0581 } 0582 0583 void TopDUContext::deleteSelf() 0584 { 0585 //We've got to make sure that m_dynamicData and m_local are still valid while all the sub-contexts are destroyed 0586 TopDUContextLocalPrivate* local = m_local; 0587 TopDUContextDynamicData* dynamicData = m_dynamicData; 0588 0589 m_dynamicData->m_deleting = true; 0590 0591 delete this; 0592 0593 delete local; 0594 delete dynamicData; 0595 } 0596 0597 TopDUContext::Features TopDUContext::features() const 0598 { 0599 auto ret = d_func()->m_features; 0600 0601 if (ast()) 0602 ret |= TopDUContext::AST; 0603 0604 return ret; 0605 } 0606 0607 void TopDUContext::setFeatures(Features features) 0608 { 0609 features &= ~Recursive; //Remove the "Recursive" flag since that's only for searching 0610 features &= ~ForceUpdateRecursive; //Remove the update flags 0611 features &= ~AST; //Remove the AST flag, it's only used while updating 0612 d_func_dynamic()->m_features = features; 0613 0614 //Replicate features to ParsingEnvironmentFile 0615 if (parsingEnvironmentFile()) 0616 parsingEnvironmentFile()->setFeatures(this->features()); 0617 } 0618 0619 void TopDUContext::setAst(const QExplicitlySharedDataPointer<IAstContainer>& ast) 0620 { 0621 ENSURE_CAN_WRITE 0622 m_local->m_ast = ast; 0623 0624 if (parsingEnvironmentFile()) 0625 parsingEnvironmentFile()->setFeatures(features()); 0626 } 0627 0628 void TopDUContext::setParsingEnvironmentFile(ParsingEnvironmentFile* file) 0629 { 0630 if (m_local->m_file) //Clear the "feature satisfaction" cache 0631 m_local->m_file->setFeatures(Empty); 0632 0633 //We do not enforce a duchain lock here, since this is also used while loading a top-context 0634 m_local->m_file = QExplicitlySharedDataPointer<ParsingEnvironmentFile>(file); 0635 0636 //Replicate features to ParsingEnvironmentFile 0637 if (file) { 0638 file->setTopContext(IndexedTopDUContext(ownIndex())); 0639 Q_ASSERT(file->indexedTopContext().isValid()); 0640 file->setFeatures(d_func()->m_features); 0641 0642 file->setImportsCache(d_func()->m_importsCache); 0643 } 0644 } 0645 0646 struct TopDUContext::FindDeclarationsAcceptor 0647 { 0648 FindDeclarationsAcceptor(const TopDUContext* _top, DeclarationList& _target, const DeclarationChecker& _check, 0649 SearchFlags _flags) : top(_top) 0650 , target(_target) 0651 , check(_check) 0652 { 0653 flags = _flags; 0654 } 0655 0656 bool operator()(const QualifiedIdentifier& id) 0657 { 0658 #ifdef DEBUG_SEARCH 0659 qCDebug(LANGUAGE) << "accepting" << id.toString(); 0660 #endif 0661 0662 auto visitDeclaration = [&](const IndexedDeclaration& iDecl) { 0663 Declaration* decl = iDecl.data(); 0664 0665 if (!decl) 0666 return PersistentSymbolTable::VisitorState::Continue; 0667 0668 if (!check(decl)) 0669 return PersistentSymbolTable::VisitorState::Continue; 0670 0671 if (!(flags & DontResolveAliases) && decl->kind() == Declaration::Alias) { 0672 //Apply alias declarations 0673 auto* alias = static_cast<AliasDeclaration*>(decl); 0674 if (alias->aliasedDeclaration().isValid()) { 0675 decl = alias->aliasedDeclaration().declaration(); 0676 } else { 0677 qCDebug(LANGUAGE) << "lost aliased declaration"; 0678 } 0679 } 0680 0681 target.append(decl); 0682 return PersistentSymbolTable::VisitorState::Continue; 0683 }; 0684 0685 if (check.flags & DUContext::NoImportsCheck) { 0686 PersistentSymbolTable::self().visitDeclarations(id, visitDeclaration); 0687 } else { 0688 PersistentSymbolTable::self().visitFilteredDeclarations(id, top->recursiveImportIndices(), 0689 visitDeclaration); 0690 } 0691 0692 check.createVisibleCache = nullptr; 0693 0694 return !top->foundEnough(target, flags); 0695 } 0696 0697 const TopDUContext* top; 0698 DeclarationList& target; 0699 const DeclarationChecker& check; 0700 QFlags<KDevelop::DUContext::SearchFlag> flags; 0701 }; 0702 0703 bool TopDUContext::findDeclarationsInternal(const SearchItem::PtrList& identifiers, const CursorInRevision& position, 0704 const AbstractType::Ptr& dataType, DeclarationList& ret, 0705 const TopDUContext* /*source*/, SearchFlags flags, uint /*depth*/) const 0706 { 0707 ENSURE_CAN_READ 0708 0709 #ifdef DEBUG_SEARCH 0710 for (const SearchItem::Ptr& idTree : identifiers) { 0711 const auto ids = idTree->toList(); 0712 for (const QualifiedIdentifier& id : ids) { 0713 qCDebug(LANGUAGE) << "searching item" << id.toString(); 0714 } 0715 } 0716 0717 #endif 0718 0719 DeclarationChecker check(this, position, dataType, flags); 0720 FindDeclarationsAcceptor storer(this, ret, check, flags); 0721 0722 ///The actual scopes are found within applyAliases, and each complete qualified identifier is given to FindDeclarationsAcceptor. 0723 ///That stores the found declaration to the output. 0724 applyAliases(identifiers, storer, position, false); 0725 0726 return true; 0727 } 0728 0729 //This is used to prevent endless recursion due to "using namespace .." declarations, by storing all imports that are already being used. 0730 struct TopDUContext::ApplyAliasesBuddyInfo 0731 { 0732 ApplyAliasesBuddyInfo(uint importChainType, ApplyAliasesBuddyInfo* predecessor, 0733 const IndexedQualifiedIdentifier& importId) : m_importChainType(importChainType) 0734 , m_predecessor(predecessor) 0735 , m_importId(importId) 0736 { 0737 if (m_predecessor && m_predecessor->m_importChainType != importChainType) 0738 m_predecessor = nullptr; 0739 } 0740 0741 bool alreadyImporting(const IndexedQualifiedIdentifier& id) 0742 { 0743 ApplyAliasesBuddyInfo* current = this; 0744 while (current) { 0745 if (current->m_importId == id) 0746 return true; 0747 current = current->m_predecessor; 0748 } 0749 return false; 0750 } 0751 0752 uint m_importChainType; 0753 ApplyAliasesBuddyInfo* m_predecessor; 0754 IndexedQualifiedIdentifier m_importId; 0755 }; 0756 0757 ///@todo Implement a cache so at least the global import checks don't need to be done repeatedly. The cache should be thread-local, using DUChainPointer for the hashed items, and when an item was deleted, it should be discarded 0758 template <class Acceptor> 0759 bool TopDUContext::applyAliases(const QualifiedIdentifier& previous, const SearchItem::Ptr& identifier, 0760 Acceptor& accept, const CursorInRevision& position, bool canBeNamespace, 0761 ApplyAliasesBuddyInfo* buddy, uint recursionDepth) const 0762 { 0763 if (recursionDepth > maxApplyAliasesRecursion) { 0764 const auto searches = identifier->toList(); 0765 QualifiedIdentifier id; 0766 if (!searches.isEmpty()) 0767 id = searches.first(); 0768 0769 qCDebug(LANGUAGE) << "maximum apply-aliases recursion reached while searching" << id; 0770 } 0771 bool foundAlias = false; 0772 0773 QualifiedIdentifier id(previous); 0774 id.push(identifier->identifier); 0775 0776 if (!id.inRepository()) 0777 return true; //If the qualified identifier is not in the identifier repository, it cannot be registered anywhere, so there's nothing we need to do 0778 0779 if (!identifier->next.isEmpty() || canBeNamespace) { //If it cannot be a namespace, the last part of the scope will be ignored 0780 //Search for namespace-aliases, by using globalAliasIdentifier, which is inserted into the symbol-table by NamespaceAliasDeclaration 0781 QualifiedIdentifier aliasId(id); 0782 aliasId.push(globalIndexedAliasIdentifier()); 0783 0784 #ifdef DEBUG_SEARCH 0785 qCDebug(LANGUAGE) << "checking" << id.toString(); 0786 #endif 0787 0788 if (aliasId.inRepository()) { 0789 DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, nullptr); 0790 0791 bool isDone = false; 0792 // The first part of the identifier has been found as a namespace-alias. 0793 // In c++, we only need the first alias. However, just to be correct, follow them all for now. 0794 // This efficiently filters the visible declarations out of all declarations 0795 PersistentSymbolTable::self().visitFilteredDeclarations( 0796 aliasId, recursiveImportIndices(), [&](const IndexedDeclaration& indexedAliasDecl) { 0797 auto* aliasDecl = indexedAliasDecl.data(); 0798 if (!aliasDecl) 0799 return PersistentSymbolTable::VisitorState::Continue; 0800 0801 if (!check(aliasDecl)) 0802 return PersistentSymbolTable::VisitorState::Continue; 0803 0804 if (aliasDecl->kind() != Declaration::NamespaceAlias) 0805 return PersistentSymbolTable::VisitorState::Continue; 0806 0807 if (foundAlias) 0808 return PersistentSymbolTable::VisitorState::Break; 0809 0810 Q_ASSERT(dynamic_cast<NamespaceAliasDeclaration*>(aliasDecl)); 0811 0812 auto* alias = static_cast<NamespaceAliasDeclaration*>(aliasDecl); 0813 0814 foundAlias = true; 0815 0816 QualifiedIdentifier importIdentifier = alias->importIdentifier(); 0817 0818 if (importIdentifier.isEmpty()) { 0819 qCDebug(LANGUAGE) << "found empty import"; 0820 return PersistentSymbolTable::VisitorState::Continue; 0821 } 0822 0823 if (buddy && buddy->alreadyImporting(importIdentifier)) { 0824 // This import has already been applied to this search 0825 return PersistentSymbolTable::VisitorState::Continue; 0826 } 0827 0828 ApplyAliasesBuddyInfo info(1, buddy, importIdentifier); 0829 0830 if (identifier->next.isEmpty()) { 0831 // Just insert the aliased namespace identifier 0832 if (!accept(importIdentifier)) { 0833 isDone = true; 0834 return PersistentSymbolTable::VisitorState::Break; 0835 } 0836 } else { 0837 // Create an identifiers where namespace-alias part is replaced with the alias target 0838 for (const SearchItem::Ptr& item : qAsConst(identifier->next)) { 0839 if (!applyAliases(importIdentifier, item, accept, position, canBeNamespace, &info, 0840 recursionDepth + 1)) { 0841 isDone = true; 0842 return PersistentSymbolTable::VisitorState::Break; 0843 } 0844 } 0845 } 0846 return PersistentSymbolTable::VisitorState::Continue; 0847 }); 0848 0849 if (isDone) { 0850 return false; 0851 } 0852 } 0853 } 0854 0855 if (!foundAlias) { //If we haven't found an alias, put the current versions into the result list. Additionally we will compute the identifiers transformed through "using". 0856 if (identifier->next.isEmpty()) { 0857 if (!accept(id)) //We're at the end of a qualified identifier, accept it 0858 return false; 0859 } else { 0860 for (const SearchItem::Ptr& next : qAsConst(identifier->next)) { 0861 if (!applyAliases(id, next, accept, position, canBeNamespace, nullptr, recursionDepth + 1)) 0862 return false; 0863 } 0864 } 0865 } 0866 0867 /*if( !prefix.explicitlyGlobal() || !prefix.isEmpty() ) {*/ ///@todo check iso c++ if using-directives should be respected on top-level when explicitly global 0868 ///@todo this is bad for a very big repository(the chains should be walked for the top-context instead) 0869 0870 //Find all namespace-imports at given scope 0871 0872 { 0873 QualifiedIdentifier importId(previous); 0874 importId.push(globalIndexedImportIdentifier()); 0875 0876 #ifdef DEBUG_SEARCH 0877 // qCDebug(LANGUAGE) << "checking imports in" << (backPointer ? id.toString() : QStringLiteral("global")); 0878 #endif 0879 0880 if (importId.inRepository()) { 0881 DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, nullptr); 0882 bool isDone = false; 0883 //This iterator efficiently filters the visible declarations out of all declarations 0884 PersistentSymbolTable::self().visitFilteredDeclarations( 0885 importId, recursiveImportIndices(), [&](const IndexedDeclaration& indexedImportDecl) { 0886 Declaration* importDecl = indexedImportDecl.data(); 0887 if (!importDecl) 0888 return PersistentSymbolTable::VisitorState::Continue; 0889 0890 //We must never break or return from this loop, because else we might be creating a bad cache 0891 if (!check(importDecl)) 0892 return PersistentSymbolTable::VisitorState::Continue; 0893 0894 //Search for the identifier with the import-identifier prepended 0895 Q_ASSERT(dynamic_cast<NamespaceAliasDeclaration*>(importDecl)); 0896 auto* alias = static_cast<NamespaceAliasDeclaration*>(importDecl); 0897 0898 #ifdef DEBUG_SEARCH 0899 qCDebug(LANGUAGE) << "found import of" << alias->importIdentifier().toString(); 0900 #endif 0901 0902 QualifiedIdentifier importIdentifier = alias->importIdentifier(); 0903 0904 if (importIdentifier.isEmpty()) { 0905 qCDebug(LANGUAGE) << "found empty import"; 0906 return PersistentSymbolTable::VisitorState::Continue; 0907 } 0908 0909 if (buddy && buddy->alreadyImporting(importIdentifier)) { 0910 // This import has already been applied to this search 0911 return PersistentSymbolTable::VisitorState::Continue; 0912 } 0913 0914 ApplyAliasesBuddyInfo info(2, buddy, importIdentifier); 0915 0916 if (previous != importIdentifier) { 0917 if (!applyAliases(importIdentifier, identifier, accept, 0918 importDecl->topContext() == this ? importDecl->range().start : position, 0919 canBeNamespace, &info, recursionDepth + 1)) { 0920 isDone = true; 0921 return PersistentSymbolTable::VisitorState::Break; 0922 } 0923 } 0924 return PersistentSymbolTable::VisitorState::Continue; 0925 }); 0926 0927 if (isDone) { 0928 return false; 0929 } 0930 } 0931 } 0932 return true; 0933 } 0934 0935 template <class Acceptor> 0936 void TopDUContext::applyAliases(const SearchItem::PtrList& identifiers, Acceptor& acceptor, 0937 const CursorInRevision& position, bool canBeNamespace) const 0938 { 0939 QualifiedIdentifier emptyId; 0940 0941 for (const SearchItem::Ptr& item : identifiers) 0942 applyAliases(emptyId, item, acceptor, position, canBeNamespace, nullptr, 0); 0943 } 0944 0945 TopDUContext* TopDUContext::topContext() const 0946 { 0947 return const_cast<TopDUContext*>(this); 0948 } 0949 0950 bool TopDUContext::deleting() const 0951 { 0952 return m_dynamicData->m_deleting; 0953 } 0954 0955 QList<ProblemPointer> TopDUContext::problems() const 0956 { 0957 ENSURE_CAN_READ 0958 0959 const auto data = d_func(); 0960 QList<ProblemPointer> ret; 0961 ret.reserve(data->m_problemsSize()); 0962 for (uint i = 0; i < data->m_problemsSize(); ++i) { 0963 ret << ProblemPointer(data->m_problems()[i].data(this)); 0964 } 0965 0966 return ret; 0967 } 0968 0969 void TopDUContext::setProblems(const QList<ProblemPointer>& problems) 0970 { 0971 ENSURE_CAN_WRITE 0972 clearProblems(); 0973 for (const auto& problem : problems) { 0974 addProblem(problem); 0975 } 0976 } 0977 0978 void TopDUContext::addProblem(const ProblemPointer& problem) 0979 { 0980 ENSURE_CAN_WRITE 0981 0982 Q_ASSERT(problem); 0983 0984 auto data = d_func_dynamic(); 0985 // store for indexing 0986 LocalIndexedProblem indexedProblem(problem, this); 0987 Q_ASSERT(indexedProblem.isValid()); 0988 data->m_problemsList().append(indexedProblem); 0989 Q_ASSERT(indexedProblem.data(this)); 0990 } 0991 0992 void TopDUContext::clearProblems() 0993 { 0994 ENSURE_CAN_WRITE 0995 d_func_dynamic()->m_problemsList().clear(); 0996 m_dynamicData->clearProblems(); 0997 } 0998 0999 QVector<DUContext*> TopDUContext::importers() const 1000 { 1001 ENSURE_CAN_READ 1002 const QSet<DUContext*>& directImporters = m_local->m_directImporters; 1003 return QVector<DUContext*>(directImporters.begin(), directImporters.end()); 1004 } 1005 1006 QList<DUContext*> TopDUContext::loadedImporters() const 1007 { 1008 ENSURE_CAN_READ 1009 return m_local->m_directImporters.values(); 1010 } 1011 1012 QVector<DUContext::Import> TopDUContext::importedParentContexts() const 1013 { 1014 ENSURE_CAN_READ 1015 return DUContext::importedParentContexts(); 1016 } 1017 1018 bool TopDUContext::imports(const DUContext* origin, const CursorInRevision& position) const 1019 { 1020 return importsPrivate(origin, position); 1021 } 1022 1023 bool TopDUContext::importsPrivate(const DUContext* origin, const CursorInRevision& position) const 1024 { 1025 Q_UNUSED(position); 1026 1027 if (const auto* top = dynamic_cast<const TopDUContext*>(origin)) { 1028 QMutexLocker lock(&importStructureMutex); 1029 bool ret = recursiveImportIndices().contains(IndexedTopDUContext(const_cast<TopDUContext*>(top))); 1030 if (top == this) 1031 Q_ASSERT(ret); 1032 return ret; 1033 } else { 1034 //Cannot import a non top-context 1035 return false; 1036 } 1037 } 1038 1039 void TopDUContext::clearImportedParentContexts() 1040 { 1041 if (usingImportsCache()) { 1042 d_func_dynamic()->m_importsCache = IndexedRecursiveImports(); 1043 d_func_dynamic()->m_importsCache.insert(IndexedTopDUContext(this)); 1044 } 1045 1046 DUContext::clearImportedParentContexts(); 1047 1048 m_local->clearImportedContextsRecursively(); 1049 1050 Q_ASSERT(m_local->m_recursiveImports.count() == 0); 1051 1052 Q_ASSERT(m_local->m_indexedRecursiveImports.count() == 1); 1053 1054 Q_ASSERT(imports(this, CursorInRevision::invalid())); 1055 } 1056 1057 void TopDUContext::addImportedParentContext(DUContext* context, const CursorInRevision& position, bool anonymous, 1058 bool temporary) 1059 { 1060 if (context == this) 1061 return; 1062 1063 if (!dynamic_cast<TopDUContext*>(context)) { 1064 //We cannot do this, because of the extended way we treat top-context imports. 1065 qCDebug(LANGUAGE) << "tried to import a non top-context into a top-context. This is not possible."; 1066 return; 1067 } 1068 1069 //Always make the contexts anonymous, because we care about importers in TopDUContextLocalPrivate 1070 DUContext::addImportedParentContext(context, position, anonymous, temporary); 1071 1072 m_local->addImportedContextRecursively(static_cast<TopDUContext*>(context), temporary, true); 1073 } 1074 1075 void TopDUContext::removeImportedParentContext(DUContext* context) 1076 { 1077 DUContext::removeImportedParentContext(context); 1078 1079 m_local->removeImportedContextRecursively(static_cast<TopDUContext*>(context), true); 1080 } 1081 1082 void TopDUContext::addImportedParentContexts(const QVector<QPair<TopDUContext*, CursorInRevision>>& contexts, 1083 bool temporary) 1084 { 1085 using Pair = QPair<TopDUContext*, CursorInRevision>; 1086 1087 for (const Pair pair : contexts) { 1088 addImportedParentContext(pair.first, pair.second, false, temporary); 1089 } 1090 } 1091 1092 void TopDUContext::removeImportedParentContexts(const QList<TopDUContext*>& contexts) 1093 { 1094 for (TopDUContext* context : contexts) { 1095 DUContext::removeImportedParentContext(context); 1096 } 1097 1098 m_local->removeImportedContextsRecursively(contexts, true); 1099 } 1100 1101 /// Returns true if this object is registered in the du-chain. If it is not, all sub-objects(context, declarations, etc.) 1102 bool TopDUContext::inDUChain() const 1103 { 1104 return m_local->m_inDuChain; 1105 } 1106 1107 /// This flag is only used by DUChain, never change it from outside. 1108 void TopDUContext::setInDuChain(bool b) 1109 { 1110 m_local->m_inDuChain = b; 1111 } 1112 1113 bool TopDUContext::isOnDisk() const 1114 { 1115 ///@todo Change this to releasingToDisk, and only enable it while saving a top-context to disk. 1116 return m_dynamicData->isOnDisk(); 1117 } 1118 1119 void TopDUContext::clearUsedDeclarationIndices() 1120 { 1121 ENSURE_CAN_WRITE 1122 for (unsigned int a = 0; a < d_func()->m_usedDeclarationIdsSize(); ++a) 1123 DUChain::uses()->removeUse(d_func()->m_usedDeclarationIds()[a], this); 1124 1125 d_func_dynamic()->m_usedDeclarationIdsList().clear(); 1126 } 1127 1128 void TopDUContext::deleteUsesRecursively() 1129 { 1130 clearUsedDeclarationIndices(); 1131 KDevelop::DUContext::deleteUsesRecursively(); 1132 } 1133 1134 Declaration* TopDUContext::usedDeclarationForIndex(unsigned int declarationIndex) const 1135 { 1136 ENSURE_CAN_READ 1137 if (declarationIndex & (1 << 31)) { 1138 //We use the highest bit to mark direct indices into the local declarations 1139 declarationIndex &= ~(1 << 31); //unset the highest bit 1140 return m_dynamicData->declarationForIndex(declarationIndex); 1141 } else if (declarationIndex < d_func()->m_usedDeclarationIdsSize()) 1142 return d_func()->m_usedDeclarationIds()[declarationIndex].declaration(this); 1143 else 1144 return nullptr; 1145 } 1146 1147 int TopDUContext::indexForUsedDeclaration(Declaration* declaration, bool create) 1148 { 1149 if (create) { 1150 ENSURE_CAN_WRITE 1151 } else { 1152 ENSURE_CAN_READ 1153 } 1154 1155 if (!declaration) { 1156 return std::numeric_limits<int>::max(); 1157 } 1158 1159 if (declaration->topContext() == this && !declaration->inSymbolTable() && 1160 !m_dynamicData->isTemporaryDeclarationIndex(declaration->ownIndex())) { 1161 uint index = declaration->ownIndex(); 1162 Q_ASSERT(!(index & (1 << 31))); 1163 return ( int )(index | (1 << 31)); //We don't put context-local declarations into the list, that's a waste. We just use the mark them with the highest bit. 1164 } 1165 1166 // if the declaration can not be found from this top-context, we create a direct 1167 // reference by index, to ensure that the use can be resolved in 1168 // usedDeclarationForIndex 1169 bool useDirectId = !recursiveImportIndices().contains(declaration->topContext()); 1170 DeclarationId id(declaration->id(useDirectId)); 1171 1172 int index = -1; 1173 1174 uint size = d_func()->m_usedDeclarationIdsSize(); 1175 const DeclarationId* ids = d_func()->m_usedDeclarationIds(); 1176 1177 ///@todo Make m_usedDeclarationIds sorted, and find the decl. using binary search 1178 for (unsigned int a = 0; a < size; ++a) 1179 if (ids[a] == id) { 1180 index = a; 1181 break; 1182 } 1183 1184 if (index != -1) 1185 return index; 1186 if (!create) 1187 return std::numeric_limits<int>::max(); 1188 1189 d_func_dynamic()->m_usedDeclarationIdsList().append(id); 1190 1191 if (declaration->topContext() != this) 1192 DUChain::uses()->addUse(id, this); 1193 1194 return d_func()->m_usedDeclarationIdsSize() - 1; 1195 } 1196 1197 QVector<RangeInRevision> allUses(TopDUContext* context, Declaration* declaration, bool noEmptyRanges) 1198 { 1199 QVector<RangeInRevision> ret; 1200 int declarationIndex = context->indexForUsedDeclaration(declaration, false); 1201 if (declarationIndex == std::numeric_limits<int>::max()) 1202 return ret; 1203 return allUses(context, declarationIndex, noEmptyRanges); 1204 } 1205 1206 QExplicitlySharedDataPointer<IAstContainer> TopDUContext::ast() const 1207 { 1208 return m_local->m_ast; 1209 } 1210 1211 void TopDUContext::clearAst() 1212 { 1213 setAst(QExplicitlySharedDataPointer<IAstContainer>(nullptr)); 1214 } 1215 1216 IndexedString TopDUContext::url() const 1217 { 1218 return d_func()->m_url; 1219 } 1220 }