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