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