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