1 /*
2     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
3     SPDX-FileCopyrightText: 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 #include "topducontext.h"
9 #include "topducontextutils.h"
10 
11 #include <limits>
12 
13 #include "persistentsymboltable.h"
14 #include "problem.h"
15 #include "declaration.h"
16 #include "duchain.h"
17 #include "duchainlock.h"
18 #include "parsingenvironment.h"
19 #include "duchainpointer.h"
20 #include "declarationid.h"
21 #include "namespacealiasdeclaration.h"
22 #include "aliasdeclaration.h"
23 #include "uses.h"
24 #include "topducontextdata.h"
25 #include "duchainregister.h"
26 #include "topducontextdynamicdata.h"
27 #include <debug.h>
28 
29 #include <language/interfaces/iastcontainer.h>
30 
31 // #define DEBUG_SEARCH
32 
33 const uint maxApplyAliasesRecursion = 100;
34 
35 namespace KDevelop {
repository()36 Utils::BasicSetRepository* RecursiveImportRepository::repository()
37 {
38     static Utils::BasicSetRepository recursiveImportRepositoryObject(QStringLiteral(
39             "Recursive Imports"), &KDevelop::globalItemRepositoryRegistry());
40     return &recursiveImportRepositoryObject;
41 }
42 
ReferencedTopDUContext(TopDUContext * context)43 ReferencedTopDUContext::ReferencedTopDUContext(TopDUContext* context) : m_topContext(context)
44 {
45     if (m_topContext)
46         DUChain::self()->refCountUp(m_topContext);
47 }
48 
ReferencedTopDUContext(const ReferencedTopDUContext & rhs)49 ReferencedTopDUContext::ReferencedTopDUContext(const ReferencedTopDUContext& rhs) : m_topContext(rhs.m_topContext)
50 {
51     if (m_topContext)
52         DUChain::self()->refCountUp(m_topContext);
53 }
54 
~ReferencedTopDUContext()55 ReferencedTopDUContext::~ReferencedTopDUContext()
56 {
57     if (m_topContext && !DUChain::deleted())
58         DUChain::self()->refCountDown(m_topContext);
59 }
60 
operator =(const ReferencedTopDUContext & rhs)61 ReferencedTopDUContext& ReferencedTopDUContext::operator=(const ReferencedTopDUContext& rhs)
62 {
63     if (m_topContext == rhs.m_topContext)
64         return *this;
65 
66     if (m_topContext)
67         DUChain::self()->refCountDown(m_topContext);
68 
69     m_topContext = rhs.m_topContext;
70 
71     if (m_topContext)
72         DUChain::self()->refCountUp(m_topContext);
73     return *this;
74 }
75 
76 DEFINE_LIST_MEMBER_HASH(TopDUContextData, m_usedDeclarationIds, DeclarationId)
77 DEFINE_LIST_MEMBER_HASH(TopDUContextData, m_problems, LocalIndexedProblem)
78 REGISTER_DUCHAIN_ITEM(TopDUContext);
79 
80 QMutex importStructureMutex(QMutex::Recursive);
81 
82 //Contains data that is not shared among top-contexts that share their duchain entries
83 class TopDUContextLocalPrivate
84 {
85 public:
TopDUContextLocalPrivate(TopDUContext * ctxt,uint index)86     TopDUContextLocalPrivate (TopDUContext* ctxt, uint index) :
87         m_ctxt(ctxt)
88         , m_ownIndex(index)
89         , m_inDuChain(false)
90     {
91         m_indexedRecursiveImports.insert(index);
92     }
93 
~TopDUContextLocalPrivate()94     ~TopDUContextLocalPrivate()
95     {
96         //Either we use some other contexts data and have no users, or we own the data and have users that share it.
97         QMutexLocker lock(&importStructureMutex);
98 
99         for (const DUContext::Import& import : qAsConst(m_importedContexts)) {
100             if (DUChain::self()->isInMemory(import.topContextIndex()) &&
101                 dynamic_cast<TopDUContext*>(import.context(nullptr)))
102                 dynamic_cast<TopDUContext*>(import.context(nullptr))->m_local->m_directImporters.remove(m_ctxt);
103         }
104     }
105 
106     ///@todo Make all this work consistently together with import-caching
107 
108     //After loading, should rebuild the links
rebuildDynamicImportStructure()109     void rebuildDynamicImportStructure()
110     {
111         //Currently we do not store the whole data in TopDUContextLocalPrivate, so we reconstruct it from what was stored by DUContext.
112         Q_ASSERT(m_importedContexts.isEmpty());
113 
114         FOREACH_FUNCTION(const DUContext::Import& import, m_ctxt->d_func()->m_importedContexts) {
115             if (DUChain::self()->isInMemory(import.topContextIndex())) {
116                 Q_ASSERT(import.context(nullptr));
117                 TopDUContext* top = import.context(nullptr)->topContext();
118                 Q_ASSERT(top);
119                 addImportedContextRecursively(top, false, true);
120             }
121         }
122         FOREACH_FUNCTION(const IndexedDUContext &importer, m_ctxt->d_func()->m_importers) {
123             if (DUChain::self()->isInMemory(importer.topContextIndex())) {
124                 Q_ASSERT(importer.context());
125                 TopDUContext* top = importer.context()->topContext();
126                 Q_ASSERT(top);
127                 top->m_local->addImportedContextRecursively(m_ctxt, false, true);
128             }
129         }
130     }
131 
132     //Index of this top-context within the duchain
133     //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.
134     QVector<DUContext::Import> m_importedContexts;
135 //   mutable bool m_haveImportStructure : 1;
136     TopDUContext* m_ctxt;
137 
138     QSet<DUContext*> m_directImporters;
139 
140     ParsingEnvironmentFilePointer m_file;
141 
142     QExplicitlySharedDataPointer<IAstContainer> m_ast;
143 
144     uint m_ownIndex;
145 
146     bool m_inDuChain;
147 
clearImportedContextsRecursively()148     void clearImportedContextsRecursively()
149     {
150         QMutexLocker lock(&importStructureMutex);
151 
152 //     Q_ASSERT(m_recursiveImports.size() == m_indexedRecursiveImports.count()-1);
153 
154         QSet<QPair<TopDUContext*, const TopDUContext*>> rebuild;
155 
156         for (const DUContext::Import& import : qAsConst(m_importedContexts)) {
157             auto* top = dynamic_cast<TopDUContext*>(import.context(nullptr));
158             if (top) {
159                 top->m_local->m_directImporters.remove(m_ctxt);
160 
161                 if (!m_ctxt->usingImportsCache()) {
162                     removeImportedContextRecursion(top, top, 1, rebuild);
163 
164                     QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = top->m_local->m_recursiveImports;
165                     for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) {
166                         if (m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == top)
167                             removeImportedContextRecursion(top, it.key(), it->first + 1, rebuild); //Remove all contexts that are imported through the context
168                     }
169                 }
170             }
171         }
172 
173         m_importedContexts.clear();
174 
175         rebuildImportStructureRecursion(rebuild);
176 
177         Q_ASSERT(m_recursiveImports.isEmpty());
178 //     Q_ASSERT(m_recursiveImports.size() == m_indexedRecursiveImports.count()-1);
179     }
180 
181     //Adds the context to this and all contexts that import this, and manages m_recursiveImports
addImportedContextRecursively(TopDUContext * context,bool temporary,bool local)182     void addImportedContextRecursively(TopDUContext* context, bool temporary, bool local)
183     {
184         QMutexLocker lock(&importStructureMutex);
185 
186         context->m_local->m_directImporters.insert(m_ctxt);
187 
188         if (local) {
189             // note: m_importedContexts may end up with duplicate entries -- not sure whether we should protect against this --Kevin
190             m_importedContexts << DUContext::Import(context, m_ctxt);
191         }
192 
193         if (!m_ctxt->usingImportsCache()) {
194             addImportedContextRecursion(context, context, 1, temporary);
195 
196             QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = context->m_local->m_recursiveImports;
197             for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it)
198                 addImportedContextRecursion(context, it.key(), (*it).first + 1, temporary); //Add contexts that were imported earlier into the given one
199         }
200     }
201 
202     //Removes the context from this and all contexts that import this, and manages m_recursiveImports
removeImportedContextRecursively(TopDUContext * context,bool local)203     void removeImportedContextRecursively(TopDUContext* context, bool local)
204     {
205         QMutexLocker lock(&importStructureMutex);
206 
207         context->m_local->m_directImporters.remove(m_ctxt);
208 
209         if (local)
210             m_importedContexts.removeAll(DUContext::Import(context, m_ctxt));
211 
212         QSet<QPair<TopDUContext*, const TopDUContext*>> rebuild;
213         if (!m_ctxt->usingImportsCache()) {
214             removeImportedContextRecursion(context, context, 1, rebuild);
215 
216             QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = context->m_local->m_recursiveImports;
217             for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) {
218                 if (m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == context)
219                     removeImportedContextRecursion(context, it.key(), it->first + 1, rebuild); //Remove all contexts that are imported through the context
220             }
221         }
222 
223         rebuildImportStructureRecursion(rebuild);
224     }
225 
removeImportedContextsRecursively(const QList<TopDUContext * > & contexts,bool local)226     void removeImportedContextsRecursively(const QList<TopDUContext*>& contexts, bool local)
227     {
228         QMutexLocker lock(&importStructureMutex);
229 
230         QSet<QPair<TopDUContext*, const TopDUContext*>> rebuild;
231         for (TopDUContext* context : contexts) {
232             context->m_local->m_directImporters.remove(m_ctxt);
233 
234             if (local)
235                 m_importedContexts.removeAll(DUContext::Import(context, m_ctxt));
236 
237             if (!m_ctxt->usingImportsCache()) {
238                 removeImportedContextRecursion(context, context, 1, rebuild);
239 
240                 QHash<const TopDUContext*, QPair<int, const TopDUContext*>> b = context->m_local->m_recursiveImports;
241                 for (RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) {
242                     const auto recursiveImportIt = m_recursiveImports.constFind(it.key());
243                     if (recursiveImportIt != m_recursiveImports.constEnd() && recursiveImportIt->second == context)
244                         removeImportedContextRecursion(context, it.key(), it->first + 1, rebuild); //Remove all contexts that are imported through the context
245                 }
246             }
247         }
248 
249         rebuildImportStructureRecursion(rebuild);
250     }
251 
252     //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.
253     //This does not need to be stored to disk, because it is defined implicitly.
254     //What makes this most complicated is the fact that loops are allowed in the import structure.
255     using RecursiveImports = QHash<const TopDUContext*, QPair<int, const TopDUContext*>>;
256     mutable RecursiveImports m_recursiveImports;
257     mutable TopDUContext::IndexedRecursiveImports m_indexedRecursiveImports;
258 
259 private:
addImportedContextRecursion(const TopDUContext * traceNext,const TopDUContext * imported,int depth,bool temporary=false)260     void addImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int depth,
261                                      bool temporary = false)
262     {
263         if (m_ctxt->usingImportsCache())
264             return;
265 
266 //     if(!m_haveImportStructure)
267 //       return;
268 
269         if (imported == m_ctxt)
270             return;
271 
272         const bool computeShortestPaths = false; ///@todo We do not compute the shortest path. Think what's right.
273 
274 //     traceNext->m_local->needImportStructure();
275 //     imported->m_local->needImportStructure();
276 
277         RecursiveImports::iterator it = m_recursiveImports.find(imported);
278         if (it == m_recursiveImports.end()) {
279             //Insert new path to "imported"
280             m_recursiveImports[imported] = qMakePair(depth, traceNext);
281 
282             m_indexedRecursiveImports.insert(imported->indexed());
283 //       Q_ASSERT(m_indexedRecursiveImports.size() == m_recursiveImports.size()+1);
284 
285             Q_ASSERT(traceNext != m_ctxt);
286         } else {
287             if (!computeShortestPaths)
288                 return;
289 
290             if (temporary) //For temporary imports, we don't record the best path.
291                 return;
292             //It would be better if we would use the following code, but it creates too much cost in updateImportedContextRecursion when imports are removed again.
293 
294             //Check whether the new way to "imported" is shorter than the stored one
295             if ((*it).first > depth) {
296                 //Add a shorter path
297                 (*it).first = depth;
298                 Q_ASSERT(traceNext);
299                 (*it).second = traceNext;
300                 Q_ASSERT(traceNext == imported ||
301                          (traceNext->m_local->m_recursiveImports.contains(imported) &&
302                           traceNext->m_local->m_recursiveImports[imported].first < (*it).first));
303             } else {
304                 //The imported context is already imported through a same/better path, so we can just stop processing. This saves us from endless recursion.
305                 return;
306             }
307         }
308 
309         if (temporary)
310             return;
311 
312         for (auto* context : qAsConst(m_directImporters)) {
313             auto* top = dynamic_cast<TopDUContext*>(context);
314             if (top) ///@todo also record this for local imports
315                 top->m_local->addImportedContextRecursion(m_ctxt, imported, depth + 1);
316         }
317     }
318 
removeImportedContextRecursion(const TopDUContext * traceNext,const TopDUContext * imported,int distance,QSet<QPair<TopDUContext *,const TopDUContext * >> & rebuild)319     void removeImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int distance,
320                                         QSet<QPair<TopDUContext*, const TopDUContext*>>& rebuild)
321     {
322         if (m_ctxt->usingImportsCache())
323             return;
324 
325         if (imported == m_ctxt)
326             return;
327 
328 //     if(!m_haveImportStructure)
329 //       return;
330 
331         RecursiveImports::iterator it = m_recursiveImports.find(imported);
332         if (it == m_recursiveImports.end()) {
333             //We don't import. Just return, this saves us from endless recursion.
334             return;
335         } else {
336             //Check whether we have imported "imported" through "traceNext". If not, return. Else find a new trace.
337             if ((*it).second == traceNext && (*it).first == distance) {
338                 //We need to remove the import through traceNext. Check whether there is another imported context that imports it.
339 
340                 m_recursiveImports.erase(it); //In order to prevent problems, we completely remove everything, and re-add it.
341                                               //Just updating these complex structures is very hard.
342                 Q_ASSERT(imported != m_ctxt);
343 
344                 m_indexedRecursiveImports.remove(imported->indexed());
345 //         Q_ASSERT(m_indexedRecursiveImports.size() == m_recursiveImports.size());
346 
347                 rebuild.insert(qMakePair(m_ctxt, imported));
348                 //We MUST do this before finding another trace, because else we would create loops
349                 for (QSet<DUContext*>::const_iterator childIt = m_directImporters.constBegin();
350                      childIt != m_directImporters.constEnd(); ++childIt) {
351                     auto* top = dynamic_cast<TopDUContext*>(const_cast<DUContext*>(*childIt)); //Avoid detaching, so use const iterator
352                     if (top)
353                         top->m_local->removeImportedContextRecursion(m_ctxt, imported, distance + 1, rebuild); //Don't use 'it' from here on, it may be invalid
354                 }
355             }
356         }
357     }
358 
359     //Updates the trace to 'imported'
360     void rebuildStructure(const TopDUContext* imported);
361 
rebuildImportStructureRecursion(const QSet<QPair<TopDUContext *,const TopDUContext * >> & rebuild)362     void rebuildImportStructureRecursion(const QSet<QPair<TopDUContext*, const TopDUContext*>>& rebuild)
363     {
364         for (auto& rebuildPair : rebuild) {
365             //for(int a = rebuild.size()-1; a >= 0; --a) {
366             //Find the best imported parent
367             rebuildPair.first->m_local->rebuildStructure(rebuildPair.second);
368         }
369     }
370 };
371 
recursiveImportIndices() const372 const TopDUContext::IndexedRecursiveImports& TopDUContext::recursiveImportIndices() const
373 {
374 //   No lock-check for performance reasons
375     QMutexLocker lock(&importStructureMutex);
376     if (!d_func()->m_importsCache.isEmpty())
377         return d_func()->m_importsCache;
378 
379     return m_local->m_indexedRecursiveImports;
380 }
381 
updateImportCacheRecursion(uint baseIndex,IndexedTopDUContext currentContext,TopDUContext::IndexedRecursiveImports & visited)382 void TopDUContextData::updateImportCacheRecursion(uint baseIndex, IndexedTopDUContext currentContext,
383                                                   TopDUContext::IndexedRecursiveImports& visited)
384 {
385     if (visited.contains(currentContext.index()))
386         return;
387     Q_ASSERT(currentContext.index()); //The top-context must be in the repository when this is called
388     if (!currentContext.data()) {
389         qCDebug(LANGUAGE) << "importing invalid context";
390         return;
391     }
392     visited.insert(currentContext.index());
393 
394     const TopDUContextData* currentData = currentContext.data()->topContext()->d_func();
395     if (currentData->m_importsCache.contains(baseIndex) || currentData->m_importsCache.isEmpty()) {
396         //If we have a loop or no imports-cache is used, we have to look at each import separately.
397         const KDevelop::DUContext::Import* imports = currentData->m_importedContexts();
398         uint importsSize = currentData->m_importedContextsSize();
399         for (uint a = 0; a < importsSize; ++a) {
400             IndexedTopDUContext next(imports[a].topContextIndex());
401             if (next.isValid())
402                 updateImportCacheRecursion(baseIndex, next, visited);
403         }
404     } else {
405         //If we don't have a loop with baseIndex, we can safely just merge with the imported importscache
406         visited += currentData->m_importsCache;
407     }
408 }
409 
updateImportCacheRecursion(IndexedTopDUContext currentContext,std::set<uint> & visited)410 void TopDUContextData::updateImportCacheRecursion(IndexedTopDUContext currentContext, std::set<uint>& visited)
411 {
412     if (visited.find(currentContext.index()) != visited.end())
413         return;
414     Q_ASSERT(currentContext.index()); //The top-context must be in the repository when this is called
415     if (!currentContext.data()) {
416         qCDebug(LANGUAGE) << "importing invalid context";
417         return;
418     }
419     visited.insert(currentContext.index());
420     const TopDUContextData* currentData = currentContext.data()->topContext()->d_func();
421     const KDevelop::DUContext::Import* imports = currentData->m_importedContexts();
422     uint importsSize = currentData->m_importedContextsSize();
423     for (uint a = 0; a < importsSize; ++a) {
424         IndexedTopDUContext next(imports[a].topContextIndex());
425         if (next.isValid())
426             updateImportCacheRecursion(next, visited);
427     }
428 }
429 
updateImportsCache()430 void TopDUContext::updateImportsCache()
431 {
432     QMutexLocker lock(&importStructureMutex);
433 
434     const bool use_fully_recursive_import_cache_computation = false;
435 
436     if (use_fully_recursive_import_cache_computation) {
437         std::set<uint> visited;
438         TopDUContextData::updateImportCacheRecursion(this, visited);
439         Q_ASSERT(visited.find(ownIndex()) != visited.end());
440         d_func_dynamic()->m_importsCache = IndexedRecursiveImports(visited);
441     } else {
442         d_func_dynamic()->m_importsCache = IndexedRecursiveImports();
443         TopDUContextData::updateImportCacheRecursion(ownIndex(), this, d_func_dynamic()->m_importsCache);
444     }
445     Q_ASSERT(d_func_dynamic()->m_importsCache.contains(IndexedTopDUContext(this)));
446     Q_ASSERT(usingImportsCache());
447     Q_ASSERT(imports(this, CursorInRevision::invalid()));
448 
449     if (parsingEnvironmentFile())
450         parsingEnvironmentFile()->setImportsCache(d_func()->m_importsCache);
451 }
452 
usingImportsCache() const453 bool TopDUContext::usingImportsCache() const
454 {
455     return !d_func()->m_importsCache.isEmpty();
456 }
457 
importPosition(const DUContext * target) const458 CursorInRevision TopDUContext::importPosition(const DUContext* target) const
459 {
460     ENSURE_CAN_READ
461         DUCHAIN_D(DUContext);
462     Import import(const_cast<DUContext*>(target), const_cast<TopDUContext*>(this), CursorInRevision::invalid());
463     for (unsigned int a = 0; a < d->m_importedContextsSize(); ++a)
464         if (d->m_importedContexts()[a] == import)
465             return d->m_importedContexts()[a].position;
466 
467     return DUContext::importPosition(target);
468 }
469 
rebuildStructure(const TopDUContext * imported)470 void TopDUContextLocalPrivate::rebuildStructure(const TopDUContext* imported)
471 {
472     if (m_ctxt == imported)
473         return;
474 
475     for (auto& importedContext : qAsConst(m_importedContexts)) {
476         auto* top = dynamic_cast<TopDUContext*>(importedContext.context(nullptr));
477         if (top) {
478 //       top->m_local->needImportStructure();
479             if (top == imported) {
480                 addImportedContextRecursion(top, imported, 1);
481             } else {
482                 RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.constFind(imported);
483                 if (it2 != top->m_local->m_recursiveImports.constEnd()) {
484                     addImportedContextRecursion(top, imported, (*it2).first + 1);
485                 }
486             }
487         }
488     }
489 
490     for (unsigned int a = 0; a < m_ctxt->d_func()->m_importedContextsSize(); ++a) {
491         auto* top =
492             dynamic_cast<TopDUContext*>(const_cast<DUContext*>(m_ctxt->d_func()->m_importedContexts()[a].context(nullptr)));           //To avoid detaching, use const iterator
493         if (top) {
494 //       top->m_local->needImportStructure();
495             if (top == imported) {
496                 addImportedContextRecursion(top, imported, 1);
497             } else {
498                 RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.constFind(imported);
499                 if (it2 != top->m_local->m_recursiveImports.constEnd()) {
500                     addImportedContextRecursion(top, imported, (*it2).first + 1);
501                 }
502             }
503         }
504     }
505 }
506 
rebuildDynamicImportStructure()507 void TopDUContext::rebuildDynamicImportStructure()
508 {
509     m_local->rebuildDynamicImportStructure();
510 }
511 
rebuildDynamicData(DUContext * parent,uint ownIndex)512 void TopDUContext::rebuildDynamicData(DUContext* parent, uint ownIndex)
513 {
514     Q_ASSERT(parent == nullptr && ownIndex != 0);
515     m_local->m_ownIndex = ownIndex;
516 
517     DUContext::rebuildDynamicData(parent, 0);
518 }
519 
indexed() const520 IndexedTopDUContext TopDUContext::indexed() const
521 {
522     return IndexedTopDUContext(m_local->m_ownIndex);
523 }
524 
ownIndex() const525 uint TopDUContext::ownIndex() const
526 {
527     return m_local->m_ownIndex;
528 }
529 
TopDUContext(TopDUContextData & data)530 TopDUContext::TopDUContext(TopDUContextData& data) : DUContext(data)
531     , m_local(new TopDUContextLocalPrivate(this, data.m_ownIndex))
532     , m_dynamicData(new TopDUContextDynamicData(this))
533 {
534     initFromTopContext();
535 }
536 
TopDUContext(const IndexedString & url,const RangeInRevision & range,ParsingEnvironmentFile * file)537 TopDUContext::TopDUContext(const IndexedString& url, const RangeInRevision& range, ParsingEnvironmentFile* file)
538     : DUContext(*new TopDUContextData(url), range)
539     , m_local(new TopDUContextLocalPrivate(this, DUChain::newTopContextIndex()))
540     , m_dynamicData(new TopDUContextDynamicData(this))
541 {
542     initFromTopContext();
543 
544     Q_ASSERT(url.toUrl().isValid() && !url.toUrl().isRelative());
545     d_func_dynamic()->setClassId(this);
546     setType(Global);
547 
548     DUCHAIN_D_DYNAMIC(TopDUContext);
549     d->m_features = VisibleDeclarationsAndContexts;
550     d->m_ownIndex = m_local->m_ownIndex;
551     setParsingEnvironmentFile(file);
552     setInSymbolTable(true);
553 }
554 
parsingEnvironmentFile() const555 QExplicitlySharedDataPointer<ParsingEnvironmentFile> TopDUContext::parsingEnvironmentFile() const
556 {
557     return m_local->m_file;
558 }
559 
~TopDUContext()560 TopDUContext::~TopDUContext()
561 {
562     m_dynamicData->m_deleting = true;
563 
564     //Clear the AST, so that the 'feature satisfaction' cache is eventually updated
565     clearAst();
566 
567     if (!isOnDisk()) {
568         //Clear the 'feature satisfaction' cache which is managed in ParsingEnvironmentFile
569         setFeatures(Empty);
570 
571         clearUsedDeclarationIndices();
572     }
573 
574     deleteChildContextsRecursively();
575     deleteLocalDeclarations();
576     m_dynamicData->clear();
577 }
578 
deleteSelf()579 void TopDUContext::deleteSelf()
580 {
581     //We've got to make sure that m_dynamicData and m_local are still valid while all the sub-contexts are destroyed
582     TopDUContextLocalPrivate* local = m_local;
583     TopDUContextDynamicData* dynamicData = m_dynamicData;
584 
585     m_dynamicData->m_deleting = true;
586 
587     delete this;
588 
589     delete local;
590     delete dynamicData;
591 }
592 
features() const593 TopDUContext::Features TopDUContext::features() const
594 {
595     auto ret = d_func()->m_features;
596 
597     if (ast())
598         ret |= TopDUContext::AST;
599 
600     return ret;
601 }
602 
setFeatures(Features features)603 void TopDUContext::setFeatures(Features features)
604 {
605     features &= ~Recursive; //Remove the "Recursive" flag since that's only for searching
606     features &= ~ForceUpdateRecursive; //Remove the update flags
607     features &= ~AST; //Remove the AST flag, it's only used while updating
608     d_func_dynamic()->m_features = features;
609 
610     //Replicate features to ParsingEnvironmentFile
611     if (parsingEnvironmentFile())
612         parsingEnvironmentFile()->setFeatures(this->features());
613 }
614 
setAst(const QExplicitlySharedDataPointer<IAstContainer> & ast)615 void TopDUContext::setAst(const QExplicitlySharedDataPointer<IAstContainer>& ast)
616 {
617     ENSURE_CAN_WRITE
618     m_local->m_ast = ast;
619 
620     if (parsingEnvironmentFile())
621         parsingEnvironmentFile()->setFeatures(features());
622 }
623 
setParsingEnvironmentFile(ParsingEnvironmentFile * file)624 void TopDUContext::setParsingEnvironmentFile(ParsingEnvironmentFile* file)
625 {
626     if (m_local->m_file) //Clear the "feature satisfaction" cache
627         m_local->m_file->setFeatures(Empty);
628 
629     //We do not enforce a duchain lock here, since this is also used while loading a top-context
630     m_local->m_file = QExplicitlySharedDataPointer<ParsingEnvironmentFile>(file);
631 
632     //Replicate features to ParsingEnvironmentFile
633     if (file) {
634         file->setTopContext(IndexedTopDUContext(ownIndex()));
635         Q_ASSERT(file->indexedTopContext().isValid());
636         file->setFeatures(d_func()->m_features);
637 
638         file->setImportsCache(d_func()->m_importsCache);
639     }
640 }
641 
642 struct TopDUContext::FindDeclarationsAcceptor
643 {
FindDeclarationsAcceptorKDevelop::TopDUContext::FindDeclarationsAcceptor644     FindDeclarationsAcceptor(const TopDUContext* _top, DeclarationList& _target, const DeclarationChecker& _check,
645                              SearchFlags _flags) : top(_top)
646         , target(_target)
647         , check(_check)
648     {
649         flags = _flags;
650     }
651 
operator ()KDevelop::TopDUContext::FindDeclarationsAcceptor652     bool operator()(const QualifiedIdentifier& id)
653     {
654 #ifdef DEBUG_SEARCH
655         qCDebug(LANGUAGE) << "accepting" << id.toString();
656 #endif
657 
658         PersistentSymbolTable::Declarations allDecls;
659 
660         //This iterator efficiently filters the visible declarations out of all declarations
661         PersistentSymbolTable::FilteredDeclarationIterator filter;
662 
663         //This is used if filtering is disabled
664         PersistentSymbolTable::Declarations::Iterator unchecked;
665         if (check.flags & DUContext::NoImportsCheck) {
666             allDecls = PersistentSymbolTable::self().declarations(id);
667             unchecked = allDecls.iterator();
668         } else
669             filter = PersistentSymbolTable::self().filteredDeclarations(id, top->recursiveImportIndices());
670 
671         while (filter || unchecked) {
672             IndexedDeclaration iDecl;
673             if (filter) {
674                 iDecl = *filter;
675                 ++filter;
676             } else {
677                 iDecl = *unchecked;
678                 ++unchecked;
679             }
680             Declaration* decl = iDecl.data();
681 
682             if (!decl)
683                 continue;
684 
685             if (!check(decl))
686                 continue;
687 
688             if (!(flags & DontResolveAliases) && decl->kind() == Declaration::Alias) {
689                 //Apply alias declarations
690                 auto* alias = static_cast<AliasDeclaration*>(decl);
691                 if (alias->aliasedDeclaration().isValid()) {
692                     decl = alias->aliasedDeclaration().declaration();
693                 } else {
694                     qCDebug(LANGUAGE) << "lost aliased declaration";
695                 }
696             }
697 
698             target.append(decl);
699         }
700 
701         check.createVisibleCache = nullptr;
702 
703         return !top->foundEnough(target, flags);
704     }
705 
706     const TopDUContext* top;
707     DeclarationList& target;
708     const DeclarationChecker& check;
709     QFlags<KDevelop::DUContext::SearchFlag> flags;
710 };
711 
findDeclarationsInternal(const SearchItem::PtrList & identifiers,const CursorInRevision & position,const AbstractType::Ptr & dataType,DeclarationList & ret,const TopDUContext *,SearchFlags flags,uint) const712 bool TopDUContext::findDeclarationsInternal(const SearchItem::PtrList& identifiers, const CursorInRevision& position,
713                                             const AbstractType::Ptr& dataType, DeclarationList& ret,
714                                             const TopDUContext* /*source*/, SearchFlags flags, uint /*depth*/) const
715 {
716     ENSURE_CAN_READ
717 
718 #ifdef DEBUG_SEARCH
719     for (const SearchItem::Ptr& idTree : identifiers) {
720         const auto ids = idTree->toList();
721         for (const QualifiedIdentifier& id : ids) {
722             qCDebug(LANGUAGE) << "searching item" << id.toString();
723         }
724     }
725 
726 #endif
727 
728     DeclarationChecker check(this, position, dataType, flags);
729     FindDeclarationsAcceptor storer(this, ret, check, flags);
730 
731     ///The actual scopes are found within applyAliases, and each complete qualified identifier is given to FindDeclarationsAcceptor.
732     ///That stores the found declaration to the output.
733     applyAliases(identifiers, storer, position, false);
734 
735     return true;
736 }
737 
738 //This is used to prevent endless recursion due to "using namespace .." declarations, by storing all imports that are already being used.
739 struct TopDUContext::ApplyAliasesBuddyInfo
740 {
ApplyAliasesBuddyInfoKDevelop::TopDUContext::ApplyAliasesBuddyInfo741     ApplyAliasesBuddyInfo(uint importChainType, ApplyAliasesBuddyInfo* predecessor,
742                           const IndexedQualifiedIdentifier& importId) : m_importChainType(importChainType)
743         , m_predecessor(predecessor)
744         , m_importId(importId)
745     {
746         if (m_predecessor && m_predecessor->m_importChainType != importChainType)
747             m_predecessor = nullptr;
748     }
749 
alreadyImportingKDevelop::TopDUContext::ApplyAliasesBuddyInfo750     bool alreadyImporting(const IndexedQualifiedIdentifier& id)
751     {
752         ApplyAliasesBuddyInfo* current = this;
753         while (current) {
754             if (current->m_importId == id)
755                 return true;
756             current = current->m_predecessor;
757         }
758         return false;
759     }
760 
761     uint m_importChainType;
762     ApplyAliasesBuddyInfo* m_predecessor;
763     IndexedQualifiedIdentifier m_importId;
764 };
765 
766 ///@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
767 template <class Acceptor>
applyAliases(const QualifiedIdentifier & previous,const SearchItem::Ptr & identifier,Acceptor & accept,const CursorInRevision & position,bool canBeNamespace,ApplyAliasesBuddyInfo * buddy,uint recursionDepth) const768 bool TopDUContext::applyAliases(const QualifiedIdentifier& previous, const SearchItem::Ptr& identifier,
769                                 Acceptor& accept, const CursorInRevision& position, bool canBeNamespace,
770                                 ApplyAliasesBuddyInfo* buddy, uint recursionDepth) const
771 {
772     if (recursionDepth > maxApplyAliasesRecursion) {
773         const auto searches = identifier->toList();
774         QualifiedIdentifier id;
775         if (!searches.isEmpty())
776             id = searches.first();
777 
778         qCDebug(LANGUAGE) << "maximum apply-aliases recursion reached while searching" << id;
779     }
780     bool foundAlias = false;
781 
782     QualifiedIdentifier id(previous);
783     id.push(identifier->identifier);
784 
785     if (!id.inRepository())
786         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
787 
788     if (!identifier->next.isEmpty() || canBeNamespace) { //If it cannot be a namespace, the last part of the scope will be ignored
789         //Search for namespace-aliases, by using globalAliasIdentifier, which is inserted into the symbol-table by NamespaceAliasDeclaration
790         QualifiedIdentifier aliasId(id);
791         aliasId.push(globalIndexedAliasIdentifier());
792 
793 #ifdef DEBUG_SEARCH
794         qCDebug(LANGUAGE) << "checking" << id.toString();
795 #endif
796 
797         if (aliasId.inRepository()) {
798             //This iterator efficiently filters the visible declarations out of all declarations
799             PersistentSymbolTable::FilteredDeclarationIterator filter =
800                 PersistentSymbolTable::self().filteredDeclarations(aliasId, recursiveImportIndices());
801 
802             if (filter) {
803                 DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, nullptr);
804 
805                 //The first part of the identifier has been found as a namespace-alias.
806                 //In c++, we only need the first alias. However, just to be correct, follow them all for now.
807                 for (; filter; ++filter) {
808                     Declaration* aliasDecl = filter->data();
809                     if (!aliasDecl)
810                         continue;
811 
812                     if (!check(aliasDecl))
813                         continue;
814 
815                     if (aliasDecl->kind() != Declaration::NamespaceAlias)
816                         continue;
817 
818                     if (foundAlias)
819                         break;
820 
821                     Q_ASSERT(dynamic_cast<NamespaceAliasDeclaration*>(aliasDecl));
822 
823                     auto* alias = static_cast<NamespaceAliasDeclaration*>(aliasDecl);
824 
825                     foundAlias = true;
826 
827                     QualifiedIdentifier importIdentifier = alias->importIdentifier();
828 
829                     if (importIdentifier.isEmpty()) {
830                         qCDebug(LANGUAGE) << "found empty import";
831                         continue;
832                     }
833 
834                     if (buddy && buddy->alreadyImporting(importIdentifier))
835                         continue; //This import has already been applied to this search
836 
837                     ApplyAliasesBuddyInfo info(1, buddy, importIdentifier);
838 
839                     if (identifier->next.isEmpty()) {
840                         //Just insert the aliased namespace identifier
841                         if (!accept(importIdentifier))
842                             return false;
843                     } else {
844                         //Create an identifiers where namespace-alias part is replaced with the alias target
845                         for (const SearchItem::Ptr& item : qAsConst(identifier->next)) {
846                             if (!applyAliases(importIdentifier, item, accept, position, canBeNamespace, &info,
847                                               recursionDepth + 1))
848                                 return false;
849                         }
850                     }
851                 }
852             }
853         }
854     }
855 
856     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".
857         if (identifier->next.isEmpty()) {
858             if (!accept(id)) //We're at the end of a qualified identifier, accept it
859                 return false;
860         } else {
861             for (const SearchItem::Ptr& next : qAsConst(identifier->next)) {
862                 if (!applyAliases(id, next, accept, position, canBeNamespace, nullptr, recursionDepth + 1))
863                     return false;
864             }
865         }
866     }
867 
868     /*if( !prefix.explicitlyGlobal() || !prefix.isEmpty() ) {*/ ///@todo check iso c++ if using-directives should be respected on top-level when explicitly global
869     ///@todo this is bad for a very big repository(the chains should be walked for the top-context instead)
870 
871     //Find all namespace-imports at given scope
872 
873     {
874         QualifiedIdentifier importId(previous);
875         importId.push(globalIndexedImportIdentifier());
876 
877 #ifdef DEBUG_SEARCH
878 //   qCDebug(LANGUAGE) << "checking imports in" << (backPointer ? id.toString() : QStringLiteral("global"));
879 #endif
880 
881         if (importId.inRepository()) {
882             //This iterator efficiently filters the visible declarations out of all declarations
883             PersistentSymbolTable::FilteredDeclarationIterator filter =
884                 PersistentSymbolTable::self().filteredDeclarations(importId, recursiveImportIndices());
885 
886             if (filter) {
887                 DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, nullptr);
888 
889                 for (; filter; ++filter) {
890                     Declaration* importDecl = filter->data();
891                     if (!importDecl)
892                         continue;
893 
894                     //We must never break or return from this loop, because else we might be creating a bad cache
895                     if (!check(importDecl))
896                         continue;
897 
898                     //Search for the identifier with the import-identifier prepended
899                     Q_ASSERT(dynamic_cast<NamespaceAliasDeclaration*>(importDecl));
900                     auto* alias = static_cast<NamespaceAliasDeclaration*>(importDecl);
901 
902   #ifdef DEBUG_SEARCH
903                     qCDebug(LANGUAGE) << "found import of" << alias->importIdentifier().toString();
904   #endif
905 
906                     QualifiedIdentifier importIdentifier = alias->importIdentifier();
907 
908                     if (importIdentifier.isEmpty()) {
909                         qCDebug(LANGUAGE) << "found empty import";
910                         continue;
911                     }
912 
913                     if (buddy && buddy->alreadyImporting(importIdentifier))
914                         continue; //This import has already been applied to this search
915 
916                     ApplyAliasesBuddyInfo info(2, buddy, importIdentifier);
917 
918                     if (previous != importIdentifier)
919                         if (!applyAliases(importIdentifier, identifier, accept,
920                                           importDecl->topContext() == this ? importDecl->range().start : position,
921                                           canBeNamespace,
922                                           &info, recursionDepth + 1))
923                             return false;
924                 }
925             }
926         }
927     }
928     return true;
929 }
930 
931 template <class Acceptor>
applyAliases(const SearchItem::PtrList & identifiers,Acceptor & acceptor,const CursorInRevision & position,bool canBeNamespace) const932 void TopDUContext::applyAliases(const SearchItem::PtrList& identifiers, Acceptor& acceptor,
933                                 const CursorInRevision& position, bool canBeNamespace) const
934 {
935     QualifiedIdentifier emptyId;
936 
937     for (const SearchItem::Ptr& item : identifiers)
938         applyAliases(emptyId, item, acceptor, position, canBeNamespace, nullptr, 0);
939 }
940 
topContext() const941 TopDUContext* TopDUContext::topContext() const
942 {
943     return const_cast<TopDUContext*>(this);
944 }
945 
deleting() const946 bool TopDUContext::deleting() const
947 {
948     return m_dynamicData->m_deleting;
949 }
950 
problems() const951 QList<ProblemPointer> TopDUContext::problems() const
952 {
953     ENSURE_CAN_READ
954 
955     const auto data = d_func();
956     QList<ProblemPointer> ret;
957     ret.reserve(data->m_problemsSize());
958     for (uint i = 0; i < data->m_problemsSize(); ++i) {
959         ret << ProblemPointer(data->m_problems()[i].data(this));
960     }
961 
962     return ret;
963 }
964 
setProblems(const QList<ProblemPointer> & problems)965 void TopDUContext::setProblems(const QList<ProblemPointer>& problems)
966 {
967     ENSURE_CAN_WRITE
968         clearProblems();
969     for (const auto& problem : problems) {
970         addProblem(problem);
971     }
972 }
973 
addProblem(const ProblemPointer & problem)974 void TopDUContext::addProblem(const ProblemPointer& problem)
975 {
976     ENSURE_CAN_WRITE
977 
978         Q_ASSERT(problem);
979 
980     auto data = d_func_dynamic();
981     // store for indexing
982     LocalIndexedProblem indexedProblem(problem, this);
983     Q_ASSERT(indexedProblem.isValid());
984     data->m_problemsList().append(indexedProblem);
985     Q_ASSERT(indexedProblem.data(this));
986 }
987 
clearProblems()988 void TopDUContext::clearProblems()
989 {
990     ENSURE_CAN_WRITE
991         d_func_dynamic()->m_problemsList().clear();
992     m_dynamicData->clearProblems();
993 }
994 
importers() const995 QVector<DUContext*> TopDUContext::importers() const
996 {
997     ENSURE_CAN_READ
998 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
999     const QSet<DUContext*>& directImporters = m_local->m_directImporters;
1000     return QVector<DUContext*>(directImporters.begin(), directImporters.end());
1001 #else
1002     return QVector<DUContext*>::fromList(m_local->m_directImporters.values());
1003 #endif
1004 }
1005 
loadedImporters() const1006 QList<DUContext*> TopDUContext::loadedImporters() const
1007 {
1008     ENSURE_CAN_READ
1009     return m_local->m_directImporters.values();
1010 }
1011 
importedParentContexts() const1012 QVector<DUContext::Import> TopDUContext::importedParentContexts() const
1013 {
1014     ENSURE_CAN_READ
1015     return DUContext::importedParentContexts();
1016 }
1017 
imports(const DUContext * origin,const CursorInRevision & position) const1018 bool TopDUContext::imports(const DUContext* origin, const CursorInRevision& position) const
1019 {
1020     return importsPrivate(origin, position);
1021 }
1022 
importsPrivate(const DUContext * origin,const CursorInRevision & position) const1023 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 
clearImportedParentContexts()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 
addImportedParentContext(DUContext * context,const CursorInRevision & position,bool anonymous,bool temporary)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 
removeImportedParentContext(DUContext * context)1075 void TopDUContext::removeImportedParentContext(DUContext* context)
1076 {
1077     DUContext::removeImportedParentContext(context);
1078 
1079     m_local->removeImportedContextRecursively(static_cast<TopDUContext*>(context), true);
1080 }
1081 
addImportedParentContexts(const QVector<QPair<TopDUContext *,CursorInRevision>> & contexts,bool temporary)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 
removeImportedParentContexts(const QList<TopDUContext * > & contexts)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.)
inDUChain() const1102 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.
setInDuChain(bool b)1108 void TopDUContext::setInDuChain(bool b)
1109 {
1110     m_local->m_inDuChain = b;
1111 }
1112 
isOnDisk() const1113 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 
clearUsedDeclarationIndices()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 
deleteUsesRecursively()1128 void TopDUContext::deleteUsesRecursively()
1129 {
1130     clearUsedDeclarationIndices();
1131     KDevelop::DUContext::deleteUsesRecursively();
1132 }
1133 
usedDeclarationForIndex(unsigned int declarationIndex) const1134 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 
indexForUsedDeclaration(Declaration * declaration,bool create)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 
allUses(TopDUContext * context,Declaration * declaration,bool noEmptyRanges)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 
ast() const1206 QExplicitlySharedDataPointer<IAstContainer> TopDUContext::ast() const
1207 {
1208     return m_local->m_ast;
1209 }
1210 
clearAst()1211 void TopDUContext::clearAst()
1212 {
1213     setAst(QExplicitlySharedDataPointer<IAstContainer>(nullptr));
1214 }
1215 
url() const1216 IndexedString TopDUContext::url() const
1217 {
1218     return d_func()->m_url;
1219 }
1220 }
1221