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