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