Doxygen
Loading...
Searching...
No Matches
symbolresolver.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2020 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <unordered_map>
17#include <string>
18#include <vector>
19#include <algorithm>
20#include <cassert>
21
22#include "symbolresolver.h"
23#include "util.h"
24#include "doxygen.h"
25#include "namespacedef.h"
26#include "config.h"
27#include "defargs.h"
28#include "trace.h"
29
30#if !ENABLE_SYMBOLRESOLVER_TRACING
31#undef AUTO_TRACE
32#undef AUTO_TRACE_ADD
33#undef AUTO_TRACE_EXIT
34#define AUTO_TRACE(...) (void)0
35#define AUTO_TRACE_ADD(...) (void)0
36#define AUTO_TRACE_EXIT(...) (void)0
37#endif
38
39static std::recursive_mutex g_cacheTypedefMutex;
40
42{
43 std::mutex mutex;
44 size_t size = 0;
45 size_t capacity = 0;
46 uint64_t hits = 0;
47 uint64_t misses = 0;
48};
49
52
54
56{
57 std::lock_guard lock(stats.mutex);
58 stats.size = std::max(stats.size, cache.size());
59 stats.capacity = std::max(stats.capacity, cache.capacity());
60 stats.hits = std::max(stats.hits, cache.hits());
61 stats.misses = std::max(stats.misses, cache.misses());
62}
63
65{
66 public:
67 CacheStatsWrapper(CacheStatistics &stats,size_t capacity) : m_statistics(stats), m_cache(capacity) {}
69 {
70 // merge the cache statistics at the end of a worker thread's life
72 }
73 LookupCache &cache() { return m_cache; }
74 private:
77};
78
79static size_t getCacheSize()
80{
81 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
82 if (cacheSize<0) cacheSize=0;
83 if (cacheSize>9) cacheSize=9;
84 return 65536u << cacheSize;
85}
86
88{
90 return wrapper.cache();
91}
92
94{
96 return wrapper.cache();
97}
98
99THREAD_LOCAL std::unordered_map<std::string, std::pair<DString,const MemberDef *> > g_substMap;
100
101//--------------------------------------------------------------------------------------
102
104{
105 return defType==Definition::TypeClass || defType==Definition::TypeNamespace ||
106 defType==Definition::TypeModule || defType==Definition::TypeMember ||
108}
109
110//--------------------------------------------------------------------------------------
111
112/** Helper class representing the stack of items considered while resolving
113 * the scope.
114 */
116{
117 /** Element in the stack. */
119 {
120 AccessElem(const Definition *d,const FileDef *f,const Definition *i) : scope(d), fileScope(f), item(i) {}
121 AccessElem(const Definition *d,const FileDef *f,const Definition *i,const DString &e) : scope(d), fileScope(f), item(i), expScope(e) {}
126 };
127 public:
128 void push(const Definition *scope,const FileDef *fileScope,const Definition *item)
129 {
130 m_elements.emplace_back(scope,fileScope,item);
131 }
132 void push(const Definition *scope,const FileDef *fileScope,const Definition *item,const DString &expScope)
133 {
134 m_elements.emplace_back(scope,fileScope,item,expScope);
135 }
136 void pop()
137 {
138 if (!m_elements.empty()) m_elements.pop_back();
139 }
140 bool find(const Definition *scope,const FileDef *fileScope, const Definition *item)
141 {
142 auto it = std::find_if(m_elements.begin(),m_elements.end(),
143 [&](const AccessElem &e) { return e.scope==scope && e.fileScope==fileScope && e.item==item; });
144 return it!=m_elements.end();
145 }
146 bool find(const Definition *scope,const FileDef *fileScope, const Definition *item,const DString &expScope)
147 {
148 auto it = std::find_if(m_elements.begin(),m_elements.end(),
149 [&](const AccessElem &e) { return e.scope==scope && e.fileScope==fileScope && e.item==item && e.expScope==expScope; });
150 return it!=m_elements.end();
151 }
152 void clear()
153 {
154 m_elements.clear();
155 }
156
157 private:
158 std::vector<AccessElem> m_elements;
159};
160
161//--------------------------------------------------------------------------------------
162
165using VisitedNamespaces = std::unordered_map<std::string,const Definition *>;
166
167//--------------------------------------------------------------------------------------
168
170{
171 public:
172 Private(const FileDef *f) : m_fileScope(f) {}
173 void reset()
174 {
175 m_resolvedTypedefs.clear();
177 typeDef = nullptr;
179 }
181 {
183 }
184 const FileDef *fileScope() const { return m_fileScope; }
185
187 const MemberDef *typeDef = nullptr;
189
191 LookupCache &cache, // inout
192 VisitedKeys &visitedKeys, // in
193 const Definition *scope, // in
194 const DString &n, // in
195 const MemberDef **pTypeDef, // out
196 DString *pTemplSpec, // out
197 DString *pResolvedType); // out
198
200 LookupCache &cache, // inout
201 VisitedKeys &visitedKeys, // in
202 const Definition *scope, // in
203 const DString &n, // in
204 const DString &args, // in
205 bool checkCV, // in
206 bool insideCode, // in
207 bool onlyLinkable, // in
208 const MemberDef **pTypeDef, // out
209 DString *pTemplSpec, // out
210 DString *pResolvedType); // out
211
212 int isAccessibleFrom( VisitedKeys &visitedKeys, // in
213 AccessStack &accessStack,
214 const Definition *scope,
215 const Definition *item);
216
218 VisitedKeys &visitedKeys, // in
219 VisitedNamespaces &visitedNamespaces,
220 AccessStack &accessStack,
221 const Definition *scope,
222 const Definition *item,
223 const DString &explicitScopePart);
224
225 private:
226 void getResolvedType( LookupCache &cache, // inout
227 VisitedKeys &visitedKeys,
228 const Definition *scope, // in
229 const Definition *d, // in
230 const DString &explicitScopePart, // in
231 const ArgumentList *actTemplParams, // in
232 int &minDistance, // input
233 const ClassDef *&bestMatch, // out
234 const MemberDef *&bestTypedef, // out
235 DString &bestTemplSpec, // out
236 DString &bestResolvedType // out
237 );
238
239 void getResolvedSymbol(VisitedKeys &visitedKeys, // in
240 const Definition *scope, // in
241 const Definition *d, // in
242 const DString &args, // in
243 bool checkCV, // in
244 bool insideCode, // in
245 const DString &explicitScopePart, // in
246 const DString &strippedTemplateParams, // in
247 bool forceCallable, // in
248 int &minDistance, // inout
249 const Definition *&bestMatch, // out
250 const MemberDef *&bestTypedef, // out
251 DString &bestTemplSpec, // out
252 DString &bestResolvedType // out
253 );
254
256 LookupCache &cache, // in
257 VisitedKeys &visitedKeys, // in
258 const Definition *scope, // in
259 const MemberDef *md, // in
260 const MemberDef **pMemType, // out
261 DString *pTemplSpec, // out
262 DString *pResolvedType, // out
263 const ArgumentList *actTemplParams = nullptr
264 );
265
266 const Definition *followPath(VisitedKeys &visitedKeys,
267 const Definition *start,const DString &path);
268
270
272 VisitedNamespaceKeys &visitedNamespaces,
274 const Definition *item,
275 const DString &explicitScopePart="",
276 int level=0);
279 const Definition *item,
280 const DString &explicitScopePart=""
281 );
282 DString substTypedef(VisitedKeys &visitedKeys,
283 const Definition *scope,const DString &name,
284 const MemberDef **pTypeDef=nullptr);
285
287 std::unordered_map<std::string,const MemberDef*> m_resolvedTypedefs;
288};
289
290
291
293 LookupCache &cache,
294 VisitedKeys &visitedKeys,
295 const Definition *scope,
296 const DString &n,
297 const MemberDef **pTypeDef,
298 DString *pTemplSpec,
299 DString *pResolvedType)
300{
301 AUTO_TRACE("scope={} name={}",scope->name(),n);
302 if (n.empty()) return nullptr;
303 DString explicitScopePart;
304 DString strippedTemplateParams;
305 DString scopeName=scope!=Doxygen::globalScope ? scope->name() : DString();
306 DString name=stripTemplateSpecifiersFromScope(n,true,&strippedTemplateParams,scopeName);
307 std::unique_ptr<ArgumentList> actTemplParams;
308 if (!strippedTemplateParams.empty()) // template part that was stripped
309 {
310 actTemplParams = stringToArgumentList(scope->getLanguage(),strippedTemplateParams);
311 }
312
313 int qualifierIndex = computeQualifiedIndex(name);
314 //printf("name=%s qualifierIndex=%d\n",qPrint(name),qualifierIndex);
315 if (qualifierIndex!=-1) // qualified name
316 {
317 // split off the explicit scope part
318 explicitScopePart=name.left(qualifierIndex);
319 // todo: improve namespace alias substitution
320 replaceNamespaceAliases(explicitScopePart);
321 name=name.mid(qualifierIndex+2);
322 }
323
324 if (name.empty())
325 {
326 AUTO_TRACE_EXIT("empty name");
327 return nullptr; // empty name
328 }
329
330 auto &range = Doxygen::symbolMap->find(name);
331 if (range.empty())
332 {
333 AUTO_TRACE_EXIT("no symbol with this name");
334 return nullptr;
335 }
336
337 bool hasUsingStatements =
338 (m_fileScope && (!m_fileScope->getUsedNamespaces().empty() ||
340 );
341 // Since it is often the case that the same name is searched in the same
342 // scope over an over again (especially for the linked source code generation)
343 // we use a cache to collect previous results. This is possible since the
344 // result of a lookup is deterministic. As the key we use the concatenated
345 // scope, the name to search for and the explicit scope prefix. The speedup
346 // achieved by this simple cache can be enormous.
347 size_t scopeNameLen = scope->name().length()+1;
348 size_t nameLen = name.length()+1;
349 size_t explicitPartLen = explicitScopePart.length();
350 size_t fileScopeLen = hasUsingStatements ? 1+m_fileScope->absFilePath().length() : 0;
351
352 // below is a more efficient coding of
353 // DString key=scope->name()+"+"+name+"+"+explicitScopePart+args+typesOnly?'T':'F';
354 DString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen, DString::ExplicitSize);
355 char *pk=key.rawData();
356 dstrcpy(pk,scope->name().data()); *(pk+scopeNameLen-1)='+';
357 pk+=scopeNameLen;
358 dstrcpy(pk,name.data()); *(pk+nameLen-1)='+';
359 pk+=nameLen;
360 dstrcpy(pk,explicitScopePart.data());
361 pk+=explicitPartLen;
362
363 // if a file scope is given and it contains using statements we should
364 // also use the file part in the key (as a class name can be in
365 // two different namespaces and a using statement in a file can select
366 // one of them).
367 if (hasUsingStatements)
368 {
369 // below is a more efficient coding of
370 // key+="+"+m_fileScope->name();
371 *pk++='+';
373 pk+=fileScopeLen-1;
374 }
375 *pk='\0';
376
377 const ClassDef *bestMatch=nullptr;
378 {
379 if (std::find(visitedKeys.begin(),visitedKeys.end(),key.str())!=std::end(visitedKeys))
380 {
381 // we are already in the middle of find the definition for this key.
382 // avoid recursion
383 AUTO_TRACE_EXIT("recursion detected");
384 return nullptr;
385 }
386 // remember the key
387 visitedKeys.push_back(key.str());
388
389 LookupInfo *pval = cache.find(key.str());
390 AUTO_TRACE_ADD("key={} found={}",key,pval!=nullptr);
391 if (pval)
392 {
393 if (pTemplSpec) *pTemplSpec=pval->templSpec;
394 if (pTypeDef) *pTypeDef=pval->typeDef;
395 if (pResolvedType) *pResolvedType=pval->resolvedType;
396 AUTO_TRACE_EXIT("found cached name={} templSpec={} typeDef={} resolvedTypedef={}",
397 pval->definition?pval->definition->name():DString(),
398 pval->templSpec,
399 pval->typeDef?pval->typeDef->name():DString(),
400 pval->resolvedType);
401
402 return toClassDef(pval->definition);
403 }
404
405 const MemberDef *bestTypedef=nullptr;
406 DString bestTemplSpec;
407 DString bestResolvedType;
408 int minDistance=10000; // init at "infinite"
409
410 for (Definition *d : range)
411 {
412 if (isCodeSymbol(d->definitionType()))
413 {
414 getResolvedType(cache,visitedKeys,scope,d,explicitScopePart,actTemplParams.get(),
415 minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType);
416 }
417 if (minDistance==0) break; // we can stop reaching if we already reached distance 0
418 }
419
420 if (pTypeDef)
421 {
422 *pTypeDef = bestTypedef;
423 }
424 if (pTemplSpec)
425 {
426 *pTemplSpec = bestTemplSpec;
427 }
428 if (pResolvedType)
429 {
430 *pResolvedType = bestResolvedType;
431 }
432
433 cache.insert(key.str(),LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
434 visitedKeys.erase(std::remove(visitedKeys.begin(), visitedKeys.end(), key.str()), visitedKeys.end());
435
436 AUTO_TRACE_EXIT("found name={} templSpec={} typeDef={} resolvedTypedef={}",
437 bestMatch?bestMatch->name():DString(),
438 bestTemplSpec,
439 bestTypedef?bestTypedef->name():DString(),
440 bestResolvedType);
441 }
442 return bestMatch;
443}
444
446 LookupCache &cache,
447 VisitedKeys &visitedKeys,
448 const Definition *scope,
449 const DString &n,
450 const DString &args,
451 bool checkCV,
452 bool insideCode,
453 bool onlyLinkable,
454 const MemberDef **pTypeDef,
455 DString *pTemplSpec,
456 DString *pResolvedType)
457{
458 AUTO_TRACE("scope={} name={} args={} checkCV={} insideCode={}",
459 scope->name(),n,args,checkCV,insideCode);
460 if (n.empty()) return nullptr;
461 DString explicitScopePart;
462 DString strippedTemplateParams;
463 DString scopeName=scope!=Doxygen::globalScope ? scope->name() : DString();
464 DString name=stripTemplateSpecifiersFromScope(n,true,&strippedTemplateParams,scopeName);
465 std::unique_ptr<ArgumentList> actTemplParams;
466 if (!strippedTemplateParams.empty()) // template part that was stripped
467 {
468 actTemplParams = stringToArgumentList(scope->getLanguage(),strippedTemplateParams);
469 }
470
471 int qualifierIndex = computeQualifiedIndex(name);
472 //printf("name=%s qualifierIndex=%d\n",qPrint(name),qualifierIndex);
473 if (qualifierIndex!=-1) // qualified name
474 {
475 // split off the explicit scope part
476 explicitScopePart=name.left(qualifierIndex);
477 // todo: improve namespace alias substitution
478 replaceNamespaceAliases(explicitScopePart);
479 name=name.mid(qualifierIndex+2);
480 }
481 AUTO_TRACE_ADD("qualifierIndex={} name={} explicitScopePart={} strippedTemplateParams={}",
482 qualifierIndex,name,explicitScopePart,strippedTemplateParams);
483
484 if (name.empty())
485 {
486 AUTO_TRACE_EXIT("empty name qualifierIndex={}",qualifierIndex);
487 return nullptr; // empty name
488 }
489
490 size_t i=0;
491 const auto &range1 = Doxygen::symbolMap->find(name);
492 const auto &range = (range1.empty() && (i=name.find('<'))!=DString::npos) ?
493 Doxygen::symbolMap->find(name.left(i)) : range1;
494 if (range.empty())
495 {
496 AUTO_TRACE_ADD("no symbols with name '{}' (including unspecialized)",name);
497 return nullptr;
498 }
499 AUTO_TRACE_ADD("{} -> {} candidates",name,range.size());
500
501 bool hasUsingStatements =
502 (m_fileScope && (!m_fileScope->getUsedNamespaces().empty() ||
503 !m_fileScope->getUsedDefinitions().empty())
504 );
505 // Since it is often the case that the same name is searched in the same
506 // scope over an over again (especially for the linked source code generation)
507 // we use a cache to collect previous results. This is possible since the
508 // result of a lookup is deterministic. As the key we use the concatenated
509 // scope, the name to search for and the explicit scope prefix. The speedup
510 // achieved by this simple cache can be enormous.
511 size_t scopeNameLen = scope!=Doxygen::globalScope ? scope->name().length()+1 : 0;
512 size_t nameLen = name.length()+1;
513 size_t explicitPartLen = explicitScopePart.length();
514 size_t strippedTemplateParamsLen = strippedTemplateParams.length();
515 size_t fileScopeLen = hasUsingStatements ? 1+m_fileScope->absFilePath().length() : 0;
516 size_t argsLen = args.length()+1;
517
518 // below is a more efficient coding of
519 // DString key=scope->name()+"+"+name+"+"+explicitScopePart+args+typesOnly?'T':'F';
520 std::string key;
521 key.reserve(scopeNameLen+nameLen+explicitPartLen+strippedTemplateParamsLen+fileScopeLen+argsLen);
522 if (scope!=Doxygen::globalScope)
523 {
524 key+=scope->name().str();
525 key+='+';
526 }
527 key+=name.str();
528 key+='+';
529 key+=explicitScopePart.str();
530 key+=strippedTemplateParams.str();
531
532 // if a file scope is given and it contains using statements we should
533 // also use the file part in the key (as a class name can be in
534 // two different namespaces and a using statement in a file can select
535 // one of them).
536 if (hasUsingStatements)
537 {
538 // below is a more efficient coding of
539 // key+="+"+m_fileScope->name();
540 key+='+';
541 key+=m_fileScope->absFilePath().str();
542 }
543 if (argsLen>0)
544 {
545 key+='+';
546 key+=args.str();
547 }
548
549 const Definition *bestMatch=nullptr;
550 {
551 if (std::find(visitedKeys.begin(),visitedKeys.end(),key)!=std::end(visitedKeys))
552 {
553 // we are already in the middle of find the definition for this key.
554 // avoid recursion
555 return nullptr;
556 }
557 // remember the key
558 visitedKeys.push_back(key);
559 LookupInfo *pval = cache.find(key);
560 AUTO_TRACE_ADD("key={} found={}",key,pval!=nullptr);
561 if (pval)
562 {
563 if (pTemplSpec) *pTemplSpec=pval->templSpec;
564 if (pTypeDef) *pTypeDef=pval->typeDef;
565 if (pResolvedType) *pResolvedType=pval->resolvedType;
566 AUTO_TRACE_EXIT("found cached name={} templSpec={} typeDef={} resolvedTypedef={}",
567 pval->definition?pval->definition->name():DString(),
568 pval->templSpec,
569 pval->typeDef?pval->typeDef->name():DString(),
570 pval->resolvedType);
571 return pval->definition;
572 }
573
574 const MemberDef *bestTypedef=nullptr;
575 DString bestTemplSpec;
576 DString bestResolvedType;
577 int minDistance=10000; // init at "infinite"
578
579 // helper to skip symbol definitions that should not be considered for lookup
580 auto skipDefinition = [this,&explicitScopePart](const Definition *d) -> bool {
581 if (d->definitionType()==Definition::TypeMember)
582 {
583 const MemberDef *emd = dynamic_cast<const MemberDef *>(d);
584 if (emd &&
585 emd->isEnumValue() &&
586 emd->getEnumScope() &&
587 emd->getEnumScope()->isStrong() &&
588 explicitScopePart.empty())
589 {
590 // skip lookup for strong enum values without explicit scope, see issue #11799
591 return true;
592 }
593 if (emd &&
594 emd->isStatic() && // a static function or variable
595 emd->getClassDef()==nullptr && // not a class member
596 emd->getFileDef()!=m_fileScope) // defined in a different file
597 {
598 // skip lookup for static members that are not in the current file scope
599 return true;
600 }
601 }
602 return false;
603 };
604
605 for (Definition *d : range)
606 {
607 if (isCodeSymbol(d->definitionType()) &&
608 (!onlyLinkable ||
609 d->isLinkable() ||
610 d->isLinkableInProject() ||
611 (d->definitionType()==Definition::TypeFile &&
612 (toFileDef(d))->generateSourceFile()
613 ) // undocumented file that has source code we can link to
614 )
615 )
616 {
617 if (skipDefinition(d)) continue;
618 getResolvedSymbol(visitedKeys,scope,d,args,checkCV,insideCode,explicitScopePart,strippedTemplateParams,false,
619 minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType);
620 }
621 if (minDistance==0) break; // we can stop reaching if we already reached distance 0
622 }
623
624 // in case we are looking for e.g. func() and the real function is func(int x) we also
625 // accept func(), see example 036 in the test set.
626 if (bestMatch==nullptr && args=="()")
627 {
628 for (Definition *d : range)
629 {
630 if (isCodeSymbol(d->definitionType()))
631 {
632 if (skipDefinition(d)) continue;
633 getResolvedSymbol(visitedKeys,scope,d,DString(),false,insideCode,explicitScopePart,strippedTemplateParams,true,
634 minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType);
635 }
636 if (minDistance==0) break; // we can stop reaching if we already reached distance 0
637 }
638 }
639
640 if (pTypeDef)
641 {
642 *pTypeDef = bestTypedef;
643 }
644 if (pTemplSpec)
645 {
646 *pTemplSpec = bestTemplSpec;
647 }
648 if (pResolvedType)
649 {
650 *pResolvedType = bestResolvedType;
651 }
652
653 cache.insert(key,LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
654 visitedKeys.erase(std::remove(visitedKeys.begin(),visitedKeys.end(),key),visitedKeys.end());
655
656 AUTO_TRACE_EXIT("found name={} templSpec={} typeDef={} resolvedTypedef={}",
657 bestMatch?bestMatch->name():DString(),
658 bestTemplSpec,
659 bestTypedef?bestTypedef->name():DString(),
660 bestResolvedType);
661 }
662 return bestMatch;
663}
664
666 LookupCache &cache, // inout
667 VisitedKeys &visitedKeys, // in
668 const Definition *scope, // in
669 const Definition *d, // in
670 const DString &explicitScopePart, // in
671 const ArgumentList *actTemplParams, // in
672 int &minDistance, // inout
673 const ClassDef *&bestMatch, // out
674 const MemberDef *&bestTypedef, // out
675 DString &bestTemplSpec, // out
676 DString &bestResolvedType // out
677 )
678{
679 AUTO_TRACE("scope={} sym={} explicitScope={}",scope->name(),d->qualifiedName(),explicitScopePart);
680 // only look at classes and members that are enums or typedefs
683 ((toMemberDef(d))->isTypedef() ||
684 (toMemberDef(d))->isEnumerate())
685 )
686 )
687 {
688 VisitedNamespaces visitedNamespaces;
689 AccessStack accessStack;
690 // test accessibility of definition within scope.
691 int distance = isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,
692 accessStack,scope,d,explicitScopePart);
693 AUTO_TRACE_ADD("distance={}",distance);
694 if (distance!=-1) // definition is accessible
695 {
696 // see if we are dealing with a class or a typedef
697 if (d->definitionType()==Definition::TypeClass) // d is a class
698 {
699 const ClassDef *cd = toClassDef(d);
700 //printf("cd=%s\n",qPrint(cd->name()));
701 if (!cd->isTemplateArgument()) // skip classes that
702 // are only there to
703 // represent a template
704 // argument
705 {
706 //printf("is not a templ arg\n");
707 if (distance<minDistance) // found a definition that is "closer"
708 {
709 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",cd->name(),distance,minDistance);
710 minDistance=distance;
711 bestMatch = cd;
712 bestTypedef = nullptr;
713 bestTemplSpec.clear();
714 bestResolvedType = cd->qualifiedName();
715 }
716 else if (distance==minDistance &&
717 m_fileScope && bestMatch &&
718 !m_fileScope->getUsedNamespaces().empty() &&
721 )
722 {
723 // in case the distance is equal it could be that a class X
724 // is defined in a namespace and in the global scope. When searched
725 // in the global scope the distance is 0 in both cases. We have
726 // to choose one of the definitions: we choose the one in the
727 // namespace if the fileScope imports namespaces and the definition
728 // found was in a namespace while the best match so far isn't.
729 // Just a non-perfect heuristic but it could help in some situations
730 // (kdecore code is an example).
731 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",cd->name(),distance,minDistance);
732 minDistance=distance;
733 bestMatch = cd;
734 bestTypedef = nullptr;
735 bestTemplSpec.clear();
736 bestResolvedType = cd->qualifiedName();
737 }
738 }
739 else
740 {
741 //printf(" is a template argument!\n");
742 }
743 }
745 {
746 const MemberDef *md = toMemberDef(d);
747 AUTO_TRACE_ADD("member={} isTypeDef={}",md->name(),md->isTypedef());
748 if (md->isTypedef()) // d is a typedef
749 {
750 DString args=md->argsString();
751 if (args.empty()) // do not expand "typedef t a[4];"
752 {
753 // we found a symbol at this distance, but if it didn't
754 // resolve to a class, we still have to make sure that
755 // something at a greater distance does not match, since
756 // that symbol is hidden by this one.
757 if (distance<minDistance)
758 {
759 DString spec;
760 DString type;
761 minDistance=distance;
762 const MemberDef *enumType = nullptr;
763 const ClassDef *cd = newResolveTypedef(cache,visitedKeys,scope,md,&enumType,&spec,&type,actTemplParams);
764 if (cd) // type resolves to a class
765 {
766 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",cd->name(),distance,minDistance);
767 bestMatch = cd;
768 bestTypedef = md;
769 bestTemplSpec = spec;
770 bestResolvedType = type;
771 }
772 else if (enumType) // type resolves to a member type
773 {
774 AUTO_TRACE_ADD("found enum");
775 bestMatch = nullptr;
776 bestTypedef = enumType;
777 bestTemplSpec = "";
778 bestResolvedType = enumType->qualifiedName();
779 }
780 else if (md->isReference()) // external reference
781 {
782 AUTO_TRACE_ADD("found external reference");
783 bestMatch = nullptr;
784 bestTypedef = md;
785 bestTemplSpec = spec;
786 bestResolvedType = type;
787 }
788 else
789 {
790 AUTO_TRACE_ADD("no match");
791 bestMatch = nullptr;
792 bestTypedef = md;
793 bestTemplSpec.clear();
794 bestResolvedType.clear();
795 }
796 }
797 else
798 {
799 //printf(" not the best match %d min=%d\n",distance,minDistance);
800 }
801 }
802 else
803 {
804 AUTO_TRACE_ADD("skipping complex typedef");
805 }
806 }
807 else if (md->isEnumerate())
808 {
809 if (distance<minDistance)
810 {
811 AUTO_TRACE_ADD("found enum={} at distance={} minDistance={}",md->name(),distance,minDistance);
812 minDistance=distance;
813 bestMatch = nullptr;
814 bestTypedef = md;
815 bestTemplSpec = "";
816 bestResolvedType = md->qualifiedName();
817 }
818 }
819 }
820 } // if definition accessible
821 else
822 {
823 AUTO_TRACE_ADD("not accessible");
824 }
825 } // if definition is a class or member
826 AUTO_TRACE_EXIT("bestMatch sym={} type={}",
827 bestMatch?bestMatch->name():DString("<none>"),bestResolvedType);
828}
829
830
832 VisitedKeys &visitedKeys, // in
833 const Definition *scope, // in
834 const Definition *d, // in
835 const DString &args, // in
836 bool checkCV, // in
837 bool insideCode, // in
838 const DString &explicitScopePart, // in
839 const DString &strippedTemplateParams, // in
840 bool forceCallable, // in
841 int &minDistance, // inout
842 const Definition *&bestMatch, // out
843 const MemberDef *&bestTypedef, // out
844 DString &bestTemplSpec, // out
845 DString &bestResolvedType // out
846 )
847{
848 AUTO_TRACE("scope={} sym={}",scope->name(),d->qualifiedName());
849 // only look at classes and members that are enums or typedefs
850 VisitedNamespaces visitedNamespaces;
851 AccessStack accessStack;
852 // test accessibility of definition within scope.
853 int distance = isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope,d,explicitScopePart+strippedTemplateParams);
854 if (distance==-1 && !strippedTemplateParams.empty())
855 {
856 distance = isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope,d,explicitScopePart);
857 }
858 AUTO_TRACE_ADD("distance={}",distance);
859 if (distance!=-1) // definition is accessible
860 {
861 // see if we are dealing with a class or a typedef
862 if (args.empty() && !forceCallable && d->definitionType()==Definition::TypeClass) // d is a class
863 {
864 const ClassDef *cd = toClassDef(d);
865 if (!cd->isTemplateArgument()) // skip classes that
866 // are only there to
867 // represent a template
868 // argument
869 {
870 if (distance<minDistance) // found a definition that is "closer"
871 {
872 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",d->name(),distance,minDistance);
873 minDistance=distance;
874 bestMatch = d;
875 bestTypedef = nullptr;
876 bestTemplSpec.clear();
877 bestResolvedType = cd->qualifiedName();
878 }
879 else if (distance==minDistance &&
880 m_fileScope && bestMatch &&
881 !m_fileScope->getUsedNamespaces().empty() &&
884 )
885 {
886 // in case the distance is equal it could be that a class X
887 // is defined in a namespace and in the global scope. When searched
888 // in the global scope the distance is 0 in both cases. We have
889 // to choose one of the definitions: we choose the one in the
890 // namespace if the fileScope imports namespaces and the definition
891 // found was in a namespace while the best match so far isn't.
892 // Just a non-perfect heuristic but it could help in some situations
893 // (kdecore code is an example).
894 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",d->name(),distance,minDistance);
895 minDistance=distance;
896 bestMatch = d;
897 bestTypedef = nullptr;
898 bestTemplSpec.clear();
899 bestResolvedType = cd->qualifiedName();
900 }
901 }
902 else
903 {
904 AUTO_TRACE_ADD("class with template arguments");
905 }
906 }
908 {
909 const MemberDef *md = toMemberDef(d);
910
911 bool match = true;
912 AUTO_TRACE_ADD("member={} args={} isCallable()={}",md->name(),argListToString(md->argumentList()),md->isCallable());
913 if (md->isCallable() && !args.empty())
914 {
915 DString actArgs;
916 if (md->isArtificial() && md->formalTemplateArguments()) // for members of an instantiated template we need to replace
917 // the formal arguments by the actual ones before matching
918 // See issue #10640
919 {
921 }
922 else
923 {
924 actArgs = args;
925 }
926 std::unique_ptr<ArgumentList> argList = stringToArgumentList(md->getLanguage(),actArgs);
927 const ArgumentList &mdAl = md->argumentList();
928 match = matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
929 scope, md->getFileDef(),md->typeString(),argList.get(),
930 checkCV,md->getLanguage());
931 AUTO_TRACE_ADD("match={}",match);
932 }
933
934 if (match && distance<minDistance)
935 {
936 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",md->name(),distance,minDistance);
937 minDistance=distance;
938 bestMatch = md;
939 bestTypedef = md;
940 bestTemplSpec = "";
941 bestResolvedType = md->qualifiedName();
942 }
943 }
947 {
948 if (distance<minDistance) // found a definition that is "closer"
949 {
950 AUTO_TRACE_ADD("found symbol={} at distance={} minDistance={}",d->name(),distance,minDistance);
951 minDistance=distance;
952 bestMatch = d;
953 bestTypedef = nullptr;
954 bestTemplSpec.clear();
955 bestResolvedType.clear();
956 }
957 }
958 } // if definition accessible
959 else
960 {
961 AUTO_TRACE_ADD("not accessible");
962 }
963 AUTO_TRACE_EXIT("bestMatch sym={} distance={}",
964 bestMatch?bestMatch->name():DString("<none>"),bestResolvedType);
965}
966
967
969 LookupCache &cache, // inout
970 VisitedKeys &visitedKeys, // in
971 const Definition * /* scope */, // in
972 const MemberDef *md, // in
973 const MemberDef **pMemType, // out
974 DString *pTemplSpec, // out
975 DString *pResolvedType, // out
976 const ArgumentList *actTemplParams) // in
977{
978 AUTO_TRACE("md={}",md->qualifiedName());
979 std::lock_guard lock(g_cacheTypedefMutex);
980 bool isCached = md->isTypedefValCached(); // value already cached
981 if (isCached)
982 {
983 AUTO_TRACE_EXIT("cached typedef={} resolvedTypedef={} templSpec={}",
987
988 if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
989 if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
990 return md->getCachedTypedefVal();
991 }
992
993 DString qname = md->qualifiedName();
994 if (m_resolvedTypedefs.find(qname.str())!=m_resolvedTypedefs.end())
995 {
996 AUTO_TRACE_EXIT("already being processed");
997 return nullptr; // typedef already done
998 }
999
1000 auto typedef_it = m_resolvedTypedefs.emplace(qname.str(),md).first; // put on the trace list
1001
1002 const ClassDef *typeClass = md->getClassDef();
1003 DString type = md->typeString(); // get the "value" of the typedef
1004 if (typeClass && typeClass->isTemplate() &&
1005 actTemplParams && !actTemplParams->empty())
1006 {
1008 typeClass->templateArguments(),actTemplParams);
1009 }
1010 DString typedefValue = type;
1011 int tl=static_cast<int>(type.length());
1012 int ip=tl-1; // remove * and & at the end
1013 while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
1014 {
1015 ip--;
1016 }
1017 type=type.left(ip+1);
1018 type.stripPrefix("const "); // strip leading "const"
1019 type.stripPrefix("volatile "); // strip leading "volatile"
1020 type.stripPrefix("struct "); // strip leading "struct"
1021 type.stripPrefix("union "); // strip leading "union"
1022 int sp=0;
1023 tl=static_cast<int>(type.length()); // length may have been changed
1024 while (sp<tl && type.at(sp)==' ') sp++;
1025 const MemberDef *memTypeDef = nullptr;
1026 const ClassDef *result = getResolvedTypeRec(cache,visitedKeys,md->getOuterScope(),type,
1027 &memTypeDef,nullptr,pResolvedType);
1028 // if type is a typedef then return what it resolves to.
1029 if (memTypeDef && memTypeDef->isTypedef())
1030 {
1031 AUTO_TRACE_ADD("resolving typedef");
1032 result=newResolveTypedef(cache,visitedKeys,m_fileScope,memTypeDef,pMemType,pTemplSpec,nullptr);
1033 goto done;
1034 }
1035 else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
1036 {
1037 *pMemType = memTypeDef;
1038 }
1039
1040 if (result==nullptr)
1041 {
1042 // try unspecialized version if type is template
1043 size_t si = type.rfind("::");
1044 size_t i = type.find('<');
1045 if (si==DString::npos && i!=DString::npos) // typedef of a template => try the unspecialized version
1046 {
1047 if (pTemplSpec) *pTemplSpec = type.mid(i);
1048 result = getResolvedTypeRec(cache,visitedKeys,md->getOuterScope(),type.left(i),nullptr,nullptr,pResolvedType);
1049 }
1050 else if (si!=DString::npos) // A::B
1051 {
1052 i=type.find('<',si);
1053 if (i==DString::npos) // Something like A<T>::B => lookup A::B
1054 {
1055 i=type.length();
1056 }
1057 else // Something like A<T>::B<S> => lookup A::B, spec=<S>
1058 {
1059 if (pTemplSpec) *pTemplSpec = type.mid(i);
1060 }
1061 result = getResolvedTypeRec(cache,visitedKeys,md->getOuterScope(),
1062 stripTemplateSpecifiersFromScope(type.left(i),false),nullptr,nullptr,pResolvedType);
1063 }
1064 }
1065
1066done:
1067 if (pResolvedType)
1068 {
1069 if (result && result->definitionType()==Definition::TypeClass)
1070 {
1071 *pResolvedType = result->qualifiedName();
1072 if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
1073 if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
1074 }
1075 else
1076 {
1077 *pResolvedType = typedefValue;
1078 }
1079 }
1080
1081 // remember computed value for next time
1082 if (result && result->getDefFileName()!="<code>")
1083 // this check is needed to prevent that temporary classes that are
1084 // introduced while parsing code fragments are being cached here.
1085 {
1086 AUTO_TRACE_ADD("caching typedef relation {}->{}",md->name(),result->name());
1087 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
1088 if (mdm)
1089 {
1090 mdm->cacheTypedefVal(result,
1091 pTemplSpec ? *pTemplSpec : DString(),
1092 pResolvedType ? *pResolvedType : DString()
1093 );
1094 }
1095 }
1096
1097 m_resolvedTypedefs.erase(typedef_it); // remove from the trace list
1098
1099 AUTO_TRACE_EXIT("result={} pTemplSpec={} pResolvedType={}",
1100 result ? result->name() : DString(),
1101 pTemplSpec ? *pTemplSpec : "<nullptr>",
1102 pResolvedType ? *pResolvedType : "<nullptr>"
1103 );
1104 return result;
1105}
1106
1108 VisitedKeys &visitedKeys,
1109 VisitedNamespaces &visitedNamespaces,
1110 AccessStack &accessStack,
1111 const Definition *scope,
1112 const Definition *item,
1113 const DString &explicitScopePart)
1114{
1115 int result=0; // assume we found it
1116 AUTO_TRACE("scope={} item={} explictScopePart={}",
1117 scope?scope->name():DString(), item?item->name():DString(), explicitScopePart);
1118 if (explicitScopePart.empty())
1119 {
1120 // handle degenerate case where there is no explicit scope.
1121 result = isAccessibleFrom(visitedKeys,accessStack,scope,item);
1122 AUTO_TRACE_EXIT("result={}",result);
1123 return result;
1124 }
1125
1126 if (accessStack.find(scope,m_fileScope,item,explicitScopePart))
1127 {
1128 AUTO_TRACE_EXIT("already found");
1129 return -1;
1130 }
1131 accessStack.push(scope,m_fileScope,item,explicitScopePart);
1132
1133 const Definition *newScope = followPath(visitedKeys,scope,explicitScopePart);
1134 if (newScope) // explicitScope is inside scope => newScope is the result
1135 {
1136 Definition *itemScope = item->getOuterScope();
1137
1138 AUTO_TRACE_ADD("scope traversal successful newScope={}",newScope->name());
1139
1140 bool nestedClassInsideBaseClass =
1141 itemScope &&
1142 itemScope->definitionType()==Definition::TypeClass &&
1144 (toClassDef(newScope))->isBaseClass(toClassDef(itemScope),true);
1145
1146 bool enumValueWithinEnum =
1148 toMemberDef(item)->isEnumValue() &&
1149 toMemberDef(item)->getEnumScope()==newScope;
1150
1151 if (itemScope==newScope) // exact match of scopes => distance==0
1152 {
1153 AUTO_TRACE_ADD("found scope match");
1154 }
1155 else if (nestedClassInsideBaseClass)
1156 {
1157 // inheritance is also ok. Example: looking for B::I, where
1158 // class A { public: class I {} };
1159 // class B : public A {}
1160 // but looking for B::I, where
1161 // class A { public: class I {} };
1162 // class B { public: class I {} };
1163 // will find A::I, so we still prefer a direct match and give this one a distance of 1
1164 result=1;
1165
1166 AUTO_TRACE_ADD("{} is a bass class of {}",scope->name(),newScope->name());
1167 }
1168 else if (enumValueWithinEnum)
1169 {
1170 AUTO_TRACE_ADD("found enum value inside enum");
1171 result=1;
1172 }
1173 else
1174 {
1175 int i=-1;
1177 {
1178 visitedNamespaces.emplace(newScope->name().str(),newScope);
1179 // this part deals with the case where item is a class
1180 // A::B::C but is explicit referenced as A::C, where B is imported
1181 // in A via a using directive.
1182 //printf("newScope is a namespace: %s!\n",qPrint(newScope->name()));
1183 const NamespaceDef *nscope = toNamespaceDef(newScope);
1184 for (const auto &ud : nscope->getUsedDefinitions())
1185 {
1186 if (ud==item)
1187 {
1188 AUTO_TRACE_ADD("found in used definition {}",ud->name());
1189 goto done;
1190 }
1191 }
1192 for (const auto &nd : nscope->getUsedNamespaces())
1193 {
1194 if (visitedNamespaces.find(nd->name().str())==visitedNamespaces.end())
1195 {
1196 i = isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope,item,nd->name());
1197 if (i!=-1)
1198 {
1199 AUTO_TRACE_ADD("found in used namespace {}",nd->name());
1200 goto done;
1201 }
1202 }
1203 }
1204 }
1205#if 0 // this caused problems resolving A::f() in the docs when there was a A::f(int) but also a
1206 // global function f() that exactly matched the argument list.
1207 else if (isParentScope(scope,newScope) && newScope->definitionType()==Definition::TypeClass)
1208 {
1209 // if we a look for a type B and have explicit scope A, then it is also fine if B
1210 // is found at the global scope.
1211 result = 1;
1212 goto done;
1213 }
1214#endif
1215 // repeat for the parent scope
1216 if (scope!=Doxygen::globalScope)
1217 {
1218 i = isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope->getOuterScope(),item,explicitScopePart);
1219 }
1220 result = (i==-1) ? -1 : i+2;
1221 }
1222 }
1223 else // failed to resolve explicitScope
1224 {
1225 AUTO_TRACE_ADD("failed to resolve explicitScope");
1227 {
1228 const NamespaceDef *nscope = toNamespaceDef(scope);
1229 VisitedNamespaceKeys locVisitedNamespaceKeys;
1230 if (accessibleViaUsingNamespace(visitedKeys,locVisitedNamespaceKeys,nscope->getUsedNamespaces(),item,explicitScopePart))
1231 {
1232 AUTO_TRACE_ADD("found in used class");
1233 goto done;
1234 }
1235 }
1236 if (scope==Doxygen::globalScope)
1237 {
1238 if (m_fileScope)
1239 {
1240 VisitedNamespaceKeys locVisitedNamespaceKeys;
1241 if (accessibleViaUsingNamespace(visitedKeys,locVisitedNamespaceKeys,m_fileScope->getUsedNamespaces(),item,explicitScopePart))
1242 {
1243 AUTO_TRACE_ADD("found in used namespace");
1244 goto done;
1245 }
1246 }
1247 AUTO_TRACE_ADD("not found in this scope");
1248 result=-1;
1249 }
1250 else // continue by looking into the parent scope
1251 {
1252 int i=isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope->getOuterScope(),item,explicitScopePart);
1253 result= (i==-1) ? -1 : i+2;
1254 }
1255 }
1256
1257done:
1258 AUTO_TRACE_EXIT("result={}",result);
1259 accessStack.pop();
1260 return result;
1261}
1262
1264 const Definition *start,const DString &path)
1265{
1266 AUTO_TRACE("start={},path={}",start?start->name():DString(), path);
1267 int is=0,ps=0,l=0;
1268
1269 const Definition *current=start;
1270 // for each part of the explicit scope
1271 while ((is=getScopeFragment(path,ps,&l))!=-1)
1272 {
1273 // try to resolve the part if it is a typedef
1274 const MemberDef *memTypeDef=nullptr;
1275 DString qualScopePart = substTypedef(visitedKeys,current,path.mid(is,l),&memTypeDef);
1276 AUTO_TRACE_ADD("qualScopePart={} memTypeDef={}",qualScopePart,memTypeDef?memTypeDef->name():"");
1277 const Definition *next = nullptr;
1278 if (memTypeDef)
1279 {
1280 const ClassDef *type = newResolveTypedef(getTypeLookupCache(),visitedKeys,m_fileScope,memTypeDef,nullptr,nullptr,nullptr);
1281 if (type)
1282 {
1283 AUTO_TRACE_EXIT("type={}",type->name());
1284 return type;
1285 }
1286 }
1287 else if (m_fileScope)
1288 {
1289 next = endOfPathIsUsedClass(m_fileScope->getUsedDefinitions(),qualScopePart);
1290 }
1291 if (next==nullptr)
1292 {
1293 next = current->findInnerCompound(qualScopePart);
1294 }
1295 AUTO_TRACE_ADD("Looking for {} inside {} result={}",
1296 qualScopePart, current->name(), next?next->name():DString());
1297 if (next==nullptr)
1298 {
1299 next = current->findInnerCompound(qualScopePart+"-p");
1300 }
1301 if (current->definitionType()==Definition::TypeClass)
1302 {
1303 const MemberDef *classMember = toClassDef(current)->getMemberByName(qualScopePart);
1304 if (classMember && classMember->isEnumerate())
1305 {
1306 next = classMember;
1307 }
1308 }
1309 else if (current!=Doxygen::globalScope && current->definitionType()==Definition::TypeNamespace)
1310 {
1311 const MemberDef *namespaceMember = toNamespaceDef(current)->getMemberByName(qualScopePart);
1312 if (namespaceMember && namespaceMember->isEnumerate())
1313 {
1314 next = namespaceMember;
1315 }
1316 }
1317 else if (current==Doxygen::globalScope || current->definitionType()==Definition::TypeFile)
1318 {
1319 auto &range = Doxygen::symbolMap->find(qualScopePart);
1320 for (Definition *def : range)
1321 {
1322 const Definition *outerScope = def->getOuterScope();
1323 if (
1324 (outerScope==Doxygen::globalScope || // global scope or
1325 (outerScope && // anonymous namespace in the global scope
1326 outerScope->name().startsWith("anonymous_namespace{") &&
1327 outerScope->getOuterScope()==Doxygen::globalScope
1328 )
1329 ) &&
1330 (def->definitionType()==Definition::TypeClass ||
1331 def->definitionType()==Definition::TypeMember ||
1332 def->definitionType()==Definition::TypeNamespace
1333 )
1334 )
1335 {
1336 next=def;
1337 break;
1338 }
1339 }
1340 }
1341 if (next==nullptr) // failed to follow the path
1342 {
1344 {
1345 next = endOfPathIsUsedClass(
1346 (toNamespaceDef(current))->getUsedDefinitions(),qualScopePart);
1347 }
1348 else if (current->definitionType()==Definition::TypeFile)
1349 {
1350 next = endOfPathIsUsedClass(
1351 (toFileDef(current))->getUsedDefinitions(),qualScopePart);
1352 }
1353 current = next;
1354 if (current==nullptr) break;
1355 }
1356 else // continue to follow scope
1357 {
1358 current = next;
1359 AUTO_TRACE_ADD("current={}",current->name());
1360 }
1361 ps=is+l;
1362 }
1363
1364 AUTO_TRACE_EXIT("result={}",current?current->name():DString());
1365 return current; // path could be followed
1366}
1367
1369{
1370 for (const auto &d : dl)
1371 {
1372 if (d->localName()==localName)
1373 {
1374 return d;
1375 }
1376 }
1377 return nullptr;
1378}
1379
1381 VisitedKeys &visitedKeys,
1382 VisitedNamespaceKeys &visitedNamespaces,
1384 const Definition *item,
1385 const DString &explicitScopePart,
1386 int level)
1387{
1388 AUTO_TRACE("item={} explicitScopePart={} level={}",item?item->name():DString(), explicitScopePart, level);
1389 for (const auto &und : nl) // check used namespaces for the class
1390 {
1391 AUTO_TRACE_ADD("trying via used namespace '{}'",und->name());
1392 const Definition *sc = explicitScopePart.empty() ? und : followPath(visitedKeys,und,explicitScopePart);
1393 if (sc && item->getOuterScope()==sc)
1394 {
1395 AUTO_TRACE_EXIT("true");
1396 return true;
1397 }
1398 if (item->getLanguage()==SrcLangExt::Cpp)
1399 {
1400 DString key=und->qualifiedName();
1401 if (!und->getUsedNamespaces().empty() && std::find(visitedNamespaces.begin(),visitedNamespaces.end(),key.str())==std::end(visitedNamespaces))
1402 {
1403 visitedNamespaces.push_back(key.str());
1404 if (accessibleViaUsingNamespace(visitedKeys,visitedNamespaces,und->getUsedNamespaces(),item,explicitScopePart,level+1))
1405 {
1406 AUTO_TRACE_EXIT("true");
1407 return true;
1408 }
1409
1410 }
1411 }
1412 }
1413 AUTO_TRACE_EXIT("false");
1414 return false;
1415}
1416
1417
1420 const Definition *item,
1421 const DString &explicitScopePart)
1422{
1423 AUTO_TRACE("item={} explicitScopePart={}",item?item->name():DString(), explicitScopePart);
1424 for (const auto &ud : dl)
1425 {
1426 AUTO_TRACE_ADD("trying via used definition '{}'",ud->name());
1427 const Definition *sc = explicitScopePart.empty() ? ud : followPath(visitedKeys,ud,explicitScopePart);
1428 if (sc && sc==item)
1429 {
1430 AUTO_TRACE_EXIT("true");
1431 return true;
1432 }
1433 }
1434 AUTO_TRACE_EXIT("false");
1435 return false;
1436}
1437
1439 AccessStack &accessStack,
1440 const Definition *scope,
1441 const Definition *item)
1442{
1443 AUTO_TRACE("scope={} item={} item.definitionType={}",
1444 scope?scope->name():DString(), item?item->name():DString(),
1445 item?(int)item->definitionType():-1);
1446
1447 if (accessStack.find(scope,m_fileScope,item))
1448 {
1449 AUTO_TRACE_EXIT("already processed!");
1450 return -1;
1451 }
1452 accessStack.push(scope,m_fileScope,item);
1453
1454 int result=0; // assume we found it
1455 int i=0;
1456
1457 const Definition *itemScope=item->getOuterScope();
1458 bool itemIsMember = item->definitionType()==Definition::TypeMember;
1459 bool itemIsClass = item->definitionType()==Definition::TypeClass;
1460
1461 // if item is a global member and scope points to a specific file
1462 // we adjust the scope so the file gets preference over members with the same name in
1463 // other files.
1464 if ((itemIsMember || itemIsClass) &&
1465 (itemScope==Doxygen::globalScope || // global
1466 (itemScope && itemScope->name().startsWith("anonymous_namespace{")) // member of an anonymous namespace
1467 ) &&
1469 {
1470 if (itemIsMember)
1471 {
1472 itemScope = toMemberDef(item)->getFileDef();
1473 }
1474 else if (itemIsClass)
1475 {
1476 itemScope = toClassDef(item)->getFileDef();
1477 }
1478 AUTO_TRACE_ADD("adjusting scope to {}",itemScope?itemScope->name():DString());
1479 }
1480
1481 bool memberAccessibleFromScope =
1482 (itemIsMember && // a member
1483 itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
1484 scope->definitionType()==Definition::TypeClass && // accessible
1485 (toClassDef(scope))->isAccessibleMember(toMemberDef(item)) // from scope
1486 );
1487 bool nestedClassInsideBaseClass =
1488 (itemIsClass && // a nested class
1489 itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
1490 scope->definitionType()==Definition::TypeClass && // class of scope
1491 (toClassDef(scope))->isBaseClass(toClassDef(itemScope),true)
1492 );
1493 bool enumValueOfStrongEnum =
1494 (itemIsMember &&
1495 toMemberDef(item)->isStrongEnumValue() &&
1497 toMemberDef(scope)->isEnumerate() &&
1498 scope==toMemberDef(item)->getEnumScope()
1499 );
1500
1501 if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass || enumValueOfStrongEnum)
1502 {
1503 AUTO_TRACE_ADD("memberAccessibleFromScope={} nestedClassInsideBaseClass={} enumValueOfStrongEnum={}",
1504 memberAccessibleFromScope, nestedClassInsideBaseClass, enumValueOfStrongEnum);
1505 int distanceToBase=0;
1506 if (nestedClassInsideBaseClass)
1507 {
1508 result++; // penalty for base class to prevent
1509 // this is preferred over nested class in this class
1510 // see bug 686956
1511 }
1512 else if (memberAccessibleFromScope &&
1513 itemScope &&
1514 itemScope->definitionType()==Definition::TypeClass &&
1516 (distanceToBase=toClassDef(scope)->isBaseClass(toClassDef(itemScope),true))>0
1517 )
1518 {
1519 result+=distanceToBase; // penalty if member is accessible via a base class
1520 }
1521 }
1522 else if (scope==Doxygen::globalScope)
1523 {
1524 if (itemScope &&
1526 toNamespaceDef(itemScope)->isAnonymous() &&
1527 itemScope->getOuterScope()==Doxygen::globalScope)
1528 { // item is in an anonymous namespace in the global scope and we are
1529 // looking in the global scope
1530 AUTO_TRACE_ADD("found in anonymous namespace");
1531 result++;
1532 goto done;
1533 }
1534 if (m_fileScope)
1535 {
1536 if (accessibleViaUsingDefinition(visitedKeys,m_fileScope->getUsedDefinitions(),item))
1537 {
1538 AUTO_TRACE_ADD("found via used class");
1539 goto done;
1540 }
1541 VisitedNamespaceKeys visitedNamespaceKeys;
1542 if (accessibleViaUsingNamespace(visitedKeys,visitedNamespaceKeys,m_fileScope->getUsedNamespaces(),item))
1543 {
1544 AUTO_TRACE_ADD("found via used namespace");
1545 goto done;
1546 }
1547 }
1548 AUTO_TRACE_ADD("reached global scope");
1549 result=-1; // not found in path to globalScope
1550 }
1551 else // keep searching
1552 {
1553 // check if scope is a namespace, which is using other classes and namespaces
1555 {
1556 const NamespaceDef *nscope = toNamespaceDef(scope);
1557 if (accessibleViaUsingDefinition(visitedKeys,nscope->getUsedDefinitions(),item))
1558 {
1559 AUTO_TRACE_ADD("found via used class");
1560 goto done;
1561 }
1562 VisitedNamespaceKeys visitedNamespaceKeys;
1563 if (accessibleViaUsingNamespace(visitedKeys,visitedNamespaceKeys,nscope->getUsedNamespaces(),item,DString()))
1564 {
1565 AUTO_TRACE_ADD("found via used namespace");
1566 goto done;
1567 }
1568 }
1569 else if (scope->definitionType()==Definition::TypeFile)
1570 {
1571 const FileDef *nfile = toFileDef(scope);
1572 if (accessibleViaUsingDefinition(visitedKeys,nfile->getUsedDefinitions(),item))
1573 {
1574 AUTO_TRACE_ADD("found via used class");
1575 goto done;
1576 }
1577 VisitedNamespaceKeys visitedNamespaceKeys;
1578 if (accessibleViaUsingNamespace(visitedKeys,visitedNamespaceKeys,nfile->getUsedNamespaces(),item,DString()))
1579 {
1580 AUTO_TRACE_ADD("found via used namespace");
1581 goto done;
1582 }
1583 }
1584 // repeat for the parent scope
1585 const Definition *parentScope = scope->getOuterScope();
1586 if (parentScope==Doxygen::globalScope)
1587 {
1589 {
1590 const FileDef *fd = toClassDef(scope)->getFileDef();
1591 if (fd)
1592 {
1593 parentScope = fd;
1594 }
1595 }
1596 }
1597 i=isAccessibleFrom(visitedKeys,accessStack,parentScope,item);
1598 result= (i==-1) ? -1 : i+2;
1599 }
1600done:
1601 AUTO_TRACE_EXIT("result={}",result);
1602 accessStack.pop();
1603 return result;
1604}
1605
1607 VisitedKeys &visitedKeys,
1608 const Definition *scope,const DString &name,
1609 const MemberDef **pTypeDef)
1610{
1611 AUTO_TRACE("scope={} name={}",scope?scope->name():DString(), name);
1612 DString result=name;
1613 if (name.empty()) return result;
1614
1615 auto &range = Doxygen::symbolMap->find(name);
1616 if (range.empty())
1617 return result; // no matches
1618
1619 MemberDef *bestMatch=nullptr;
1620 int minDistance=10000; // init at "infinite"
1621
1622 std::string key;
1623 const int maxAddrSize = 20;
1624 char ptr_str[maxAddrSize];
1625 int num = snprintf(ptr_str,maxAddrSize,"%p:",(void *)scope);
1626 assert(num>0);
1627 key.reserve(num+name.length()+1);
1628 key+=ptr_str;
1629 key+=name.str();
1630 {
1631 auto it = g_substMap.find(key);
1632 if (it!=g_substMap.end())
1633 {
1634 if (pTypeDef) *pTypeDef = it->second.second;
1635 return it->second.first;
1636 }
1637 }
1638
1639 for (Definition *d : range)
1640 {
1641 // only look at members
1642 if (d->definitionType()==Definition::TypeMember)
1643 {
1644 // that are also typedefs
1645 MemberDef *md = toMemberDef(d);
1646 if (md->isTypedef()) // d is a typedef
1647 {
1648 VisitedNamespaces visitedNamespaces;
1649 AccessStack accessStack;
1650 // test accessibility of typedef within scope.
1651 int distance = isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope,d,"");
1652 if (distance!=-1 && distance<minDistance)
1653 // definition is accessible and a better match
1654 {
1655 minDistance=distance;
1656 bestMatch = md;
1657 }
1658 }
1659 }
1660 }
1661
1662 if (bestMatch)
1663 {
1664 result = bestMatch->typeString();
1665 if (pTypeDef) *pTypeDef=bestMatch;
1666 }
1667
1668 // cache the result of the computation to give a faster answers next time, especially relevant
1669 // if `range` has many arguments (i.e. there are many symbols with the same name in different contexts)
1670 {
1671 g_substMap.emplace(key,std::make_pair(result,bestMatch));
1672 }
1673
1674 AUTO_TRACE_EXIT("result={}",result);
1675 return result;
1676}
1677
1678//----------------------------------------------------------------------------------------------
1679
1680
1682 : p(std::make_unique<Private>(fileScope))
1683{
1684}
1685
1689
1690
1692 const DString &name,
1693 bool mayBeUnlinkable,
1694 bool mayBeHidden)
1695{
1696 AUTO_TRACE("scope={} name={} mayBeUnlinkable={} mayBeHidden={}",
1697 scope?scope->name():DString(), name, mayBeUnlinkable, mayBeHidden);
1698 p->reset();
1699
1700 auto lang = scope ? scope->getLanguage() :
1701 p->fileScope() ? p->fileScope()->getLanguage() :
1702 SrcLangExt::Cpp; // fallback to C++
1703
1704 if (scope==nullptr ||
1707 ) ||
1708 (name.stripWhiteSpace().startsWith("::")) ||
1709 ((lang==SrcLangExt::Java || lang==SrcLangExt::CSharp) && DString(name).find("::")!=DString::npos)
1710 )
1711 {
1713 }
1714 const ClassDef *result=nullptr;
1715 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
1716 {
1717 result = getClass(name);
1718 }
1719 else
1720 {
1721 VisitedKeys visitedKeys;
1722 DString lookupName = lang==SrcLangExt::CSharp ? mangleCSharpGenericName(name) : name;
1723 AUTO_TRACE_ADD("lookup={}",lookupName);
1724 result = p->getResolvedTypeRec(getTypeLookupCache(),visitedKeys,scope,lookupName,&p->typeDef,&p->templateSpec,&p->resolvedType);
1725 if (result==nullptr) // for nested classes imported via tag files, the scope may not
1726 // present, so we check the class name directly as well.
1727 // See also bug701314
1728 {
1729 result = getClass(lookupName);
1730 }
1731 }
1732 if (!mayBeUnlinkable && result && !result->isLinkable())
1733 {
1734 if (!mayBeHidden || !result->isHidden())
1735 {
1736 AUTO_TRACE_ADD("hiding symbol {}",result->name());
1737 result=nullptr; // don't link to artificial/hidden classes unless explicitly allowed
1738 }
1739 }
1740 AUTO_TRACE_EXIT("result={}",result?result->name():DString());
1741 return result;
1742}
1743
1745 const DString &name,
1746 const DString &args,
1747 bool checkCV,
1748 bool insideCode,
1749 bool onlyLinkable)
1750{
1751 AUTO_TRACE("scope={} name={} args={} checkCV={} insideCode={}",
1752 scope?scope->name():DString(), name, args, checkCV, insideCode);
1753 p->reset();
1754 if (scope==nullptr) scope=Doxygen::globalScope;
1755 VisitedKeys visitedKeys;
1756 const Definition *result = p->getResolvedSymbolRec(getSymbolLookupCache(),visitedKeys,scope,name,args,checkCV,insideCode,onlyLinkable,&p->typeDef,&p->templateSpec,&p->resolvedType);
1757 AUTO_TRACE_EXIT("result={}{}", qPrint(result?result->qualifiedName():DString()),
1758 qPrint(result && result->definitionType()==Definition::TypeMember ? toMemberDef(result)->argsString() : DString()));
1759 return result;
1760}
1761
1763{
1764 AUTO_TRACE("scope={} item={}",
1765 scope?scope->name():DString(), item?item->name():DString());
1766 p->reset();
1767 VisitedKeys visitedKeys;
1768 AccessStack accessStack;
1769 int result = p->isAccessibleFrom(visitedKeys,accessStack,scope,item);
1770 AUTO_TRACE_EXIT("result={}",result);
1771 return result;
1772}
1773
1775 const DString &explicitScopePart)
1776{
1777 AUTO_TRACE("scope={} item={} explicitScopePart={}",
1778 scope?scope->name():DString(), item?item->name():DString(), explicitScopePart);
1779 p->reset();
1780 VisitedKeys visitedKeys;
1781 VisitedNamespaces visitedNamespaces;
1782 AccessStack accessStack;
1783 int result = p->isAccessibleFromWithExpScope(visitedKeys,visitedNamespaces,accessStack,scope,item,explicitScopePart);
1784 AUTO_TRACE_EXIT("result={}",result);
1785 return result;
1786}
1787
1789{
1790 p->setFileScope(fileScope);
1791}
1792
1794{
1795 return p->typeDef;
1796}
1797
1799{
1800 return p->templateSpec;
1801}
1802
1804{
1805 return p->resolvedType;
1806}
1807
1809{
1810 auto &cache = getTypeLookupCache();
1811 switch (scope)
1812 {
1813 case ClearScope::All:
1814 cache.clear();
1815 break;
1817 {
1818 StringVector elementsToRemove;
1819 for (const auto &ci : cache)
1820 {
1821 const LookupInfo &li = ci.second;
1822 if (li.definition==nullptr && li.typeDef==nullptr)
1823 {
1824 elementsToRemove.push_back(ci.first);
1825 }
1826 }
1827 for (const auto &k : elementsToRemove)
1828 {
1829 cache.remove(k);
1830 }
1831 }
1832 break;
1834 {
1835 StringVector elementsToRemove;
1836 for (const auto &ci : cache)
1837 {
1838 const LookupInfo &li = ci.second;
1839 if (li.definition)
1840 {
1841 elementsToRemove.push_back(ci.first);
1842 }
1843 }
1844 for (const auto &k : elementsToRemove)
1845 {
1846 cache.remove(k);
1847 }
1848 }
1849 break;
1850 }
1851}
1852
1853static int computeIdealCacheParam(size_t v)
1854{
1855 //printf("computeIdealCacheParam(v=%u)\n",v);
1856
1857 int r=0;
1858 while (v!=0)
1859 {
1860 v >>= 1;
1861 r++;
1862 }
1863 // r = log2(v)
1864
1865 // convert to a valid cache size value
1866 return std::max(0,std::min(r-16,9));
1867}
1868
1870{
1871 // merge the stats of the main thread
1874
1875 msg("type lookup cache used {}/{} hits={} misses={}\n",
1880 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
1885 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(g_typeCacheStatistics.misses*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
1886 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(g_symbolCacheStatistics.misses*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
1887 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
1888 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
1889 {
1890 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
1891 }
1892}
1893
Helper class representing the stack of items considered while resolving the scope.
void push(const Definition *scope, const FileDef *fileScope, const Definition *item, const DString &expScope)
bool find(const Definition *scope, const FileDef *fileScope, const Definition *item, const DString &expScope)
std::vector< AccessElem > m_elements
bool find(const Definition *scope, const FileDef *fileScope, const Definition *item)
void push(const Definition *scope, const FileDef *fileScope, const Definition *item)
This class represents an function or template argument list.
Definition arguments.h:65
bool empty() const
Definition arguments.h:99
Definition cache.h:32
V * insert(const K &key, V &&value)
Inserts value under key in the cache.
Definition cache.h:44
V * find(const K &key)
Definition cache.h:105
size_t capacity() const
Returns the maximum number of values that can be stored in the cache.
Definition cache.h:132
size_t size() const
Returns the number of values stored in the cache.
Definition cache.h:126
uint64_t misses() const
Returns how many of the find() calls did not found a value in the cache.
Definition cache.h:144
uint64_t hits() const
Returns how many of the find() calls did find a value in the cache.
Definition cache.h:138
A abstract class representing of a compound symbol.
Definition classdef.h:104
virtual const MemberDef * getMemberByName(const DString &) const =0
Returns the member with the given name.
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual bool isTemplate() const =0
Returns true if this class is a template.
virtual bool isTemplateArgument() const =0
virtual FileDef * getFileDef() const =0
Returns the namespace this compound is in, or 0 if it has a global scope.
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
void clear()
Definition dstring.h:219
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
char * rawData()
Returns a writable pointer to the data.
Definition dstring.h:171
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:183
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:675
DString & append(char c)
Definition dstring.h:478
DString right(size_t len) const
Definition dstring.h:316
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:159
DString & prepend(const char *s)
Definition dstring.h:504
size_t find(char c, size_t pos=0) const
Definition dstring.h:244
@ ExplicitSize
Definition dstring.h:136
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:342
DString left(size_t len) const
Definition dstring.h:311
const std::string & str() const
Definition dstring.h:634
bool stripPrefix(const DString &prefix)
Definition dstring.h:295
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:162
bool startsWith(const char *s) const
Definition dstring.h:589
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:156
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual DString getDefFileName() const =0
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual DString qualifiedName() const =0
virtual bool isHidden() const =0
virtual bool isArtificial() const =0
virtual Definition * getOuterScope() const =0
virtual bool isReference() const =0
virtual const Definition * findInnerCompound(const DString &name) const =0
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:125
A model of a file symbol.
Definition filedef.h:99
virtual DString absFilePath() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual const LinkedRefMap< const Definition > & getUsedDefinitions() const =0
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
bool empty() const
Definition linkedmap.h:374
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual DString argsString() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual const ClassDef * getClassDef() const =0
virtual DString getCachedResolvedTypedef() const =0
virtual bool isTypedef() const =0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual bool isStatic() const =0
virtual bool isTypedefValCached() const =0
virtual std::optional< ArgumentList > formalTemplateArguments() const =0
virtual DString getCachedTypedefTemplSpec() const =0
virtual bool isEnumerate() const =0
virtual bool isStrong() const =0
virtual DString typeString() const =0
virtual bool isCallable() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual void cacheTypedefVal(const ClassDef *val, const DString &templSpec, const DString &resolvedType)=0
An abstract interface of a namespace symbol.
virtual const MemberDef * getMemberByName(const DString &) const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual const LinkedRefMap< const Definition > & getUsedDefinitions() const =0
const VectorPtr & find(const DString &name)
Definition symbolmap.h:75
static void showCacheUsage()
Show usage of the type lookup cache.
ClearScope
Clear the type lookup cache for the current thread.
int isAccessibleFrom(const Definition *scope, const Definition *item)
Checks if symbol item is accessible from within scope.
static void clearTypeLookupCache(ClearScope scope)
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getResolvedType() const
In case a call to resolveClass() points to a typedef or using declaration.
std::unique_ptr< Private > p
int isAccessibleFromWithExpScope(const Definition *scope, const Definition *item, const DString &explicitScopePart)
Check if symbol item is accessible from within scope, where it has to match the explicitScopePart.
SymbolResolver(const FileDef *fileScope=nullptr)
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
void setFileScope(const FileDef *fd)
Sets or updates the file scope using when resolving symbols.
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
ClassDef * getClass(const DString &n)
ClassDef * toClassDef(Definition *d)
#define Config_getInt(name)
Definition config.h:34
#define Config_getBool(name)
Definition config.h:33
std::vector< std::string > StringVector
Definition containers.h:33
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:828
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:49
#define AUTO_TRACE(...)
Definition docnode.cpp:48
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:50
#define THREAD_LOCAL
Definition doxygen.h:30
char * dstrcpy(char *dst, const char *src)
Definition dstring.h:47
const char * qPrint(const char *s)
Definition dstring.h:769
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1966
MemberDefMutable * toMemberDefMutable(Definition *d)
MemberDef * toMemberDef(Definition *d)
#define msg(fmt,...)
Definition message.h:94
void replaceNamespaceAliases(DString &name)
NamespaceDef * toNamespaceDef(Definition *d)
Definition dstring.h:879
Element in the stack.
const Definition * scope
const Definition * item
AccessElem(const Definition *d, const FileDef *f, const Definition *i)
AccessElem(const Definition *d, const FileDef *f, const Definition *i, const DString &e)
CacheStatistics & m_statistics
Cache< std::string, LookupInfo > m_cache
CacheStatsWrapper(CacheStatistics &stats, size_t capacity)
LookupCache & cache()
DString resolvedType
Definition doxygen.h:62
const Definition * definition
Definition doxygen.h:59
const MemberDef * typeDef
Definition doxygen.h:60
DString templSpec
Definition doxygen.h:61
LookupInfo()=default
const MemberDef * typeDef
const ClassDef * newResolveTypedef(LookupCache &cache, VisitedKeys &visitedKeys, const Definition *scope, const MemberDef *md, const MemberDef **pMemType, DString *pTemplSpec, DString *pResolvedType, const ArgumentList *actTemplParams=nullptr)
int isAccessibleFrom(VisitedKeys &visitedKeys, AccessStack &accessStack, const Definition *scope, const Definition *item)
Private(const FileDef *f)
const ClassDef * getResolvedTypeRec(LookupCache &cache, VisitedKeys &visitedKeys, const Definition *scope, const DString &n, const MemberDef **pTypeDef, DString *pTemplSpec, DString *pResolvedType)
bool accessibleViaUsingNamespace(VisitedKeys &visitedKeys, VisitedNamespaceKeys &visitedNamespaces, const LinkedRefMap< NamespaceDef > &nl, const Definition *item, const DString &explicitScopePart="", int level=0)
bool accessibleViaUsingDefinition(VisitedKeys &visitedKeys, const LinkedRefMap< const Definition > &dl, const Definition *item, const DString &explicitScopePart="")
const FileDef * fileScope() const
void setFileScope(const FileDef *fileScope)
const Definition * endOfPathIsUsedClass(const LinkedRefMap< const Definition > &dl, const DString &localName)
void getResolvedType(LookupCache &cache, VisitedKeys &visitedKeys, const Definition *scope, const Definition *d, const DString &explicitScopePart, const ArgumentList *actTemplParams, int &minDistance, const ClassDef *&bestMatch, const MemberDef *&bestTypedef, DString &bestTemplSpec, DString &bestResolvedType)
const Definition * followPath(VisitedKeys &visitedKeys, const Definition *start, const DString &path)
void getResolvedSymbol(VisitedKeys &visitedKeys, const Definition *scope, const Definition *d, const DString &args, bool checkCV, bool insideCode, const DString &explicitScopePart, const DString &strippedTemplateParams, bool forceCallable, int &minDistance, const Definition *&bestMatch, const MemberDef *&bestTypedef, DString &bestTemplSpec, DString &bestResolvedType)
int isAccessibleFromWithExpScope(VisitedKeys &visitedKeys, VisitedNamespaces &visitedNamespaces, AccessStack &accessStack, const Definition *scope, const Definition *item, const DString &explicitScopePart)
DString substTypedef(VisitedKeys &visitedKeys, const Definition *scope, const DString &name, const MemberDef **pTypeDef=nullptr)
std::unordered_map< std::string, const MemberDef * > m_resolvedTypedefs
const Definition * getResolvedSymbolRec(LookupCache &cache, VisitedKeys &visitedKeys, const Definition *scope, const DString &n, const DString &args, bool checkCV, bool insideCode, bool onlyLinkable, const MemberDef **pTypeDef, DString *pTemplSpec, DString *pResolvedType)
StringVector VisitedNamespaceKeys
static bool isCodeSymbol(Definition::DefType defType)
static std::recursive_mutex g_cacheTypedefMutex
static void mergeStatistics(CacheStatistics &stats, LookupCache &cache)
std::unordered_map< std::string, const Definition * > VisitedNamespaces
static LookupCache & getTypeLookupCache()
StringVector VisitedKeys
static int computeIdealCacheParam(size_t v)
static LookupCache & getSymbolLookupCache()
static size_t getCacheSize()
THREAD_LOCAL std::unordered_map< std::string, std::pair< DString, const MemberDef * > > g_substMap
static CacheStatistics g_symbolCacheStatistics
static CacheStatistics g_typeCacheStatistics
Cache< std::string, LookupInfo > LookupCache
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4407
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6862
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:2027
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:4569
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1253
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:6951
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:4681
A bunch of utility functions.