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