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