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