summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/classdef.cpp4
-rw-r--r--src/code.l40
-rw-r--r--src/doxygen.cpp66
-rw-r--r--src/pycode.l12
-rw-r--r--src/symbolresolver.cpp1149
-rw-r--r--src/symbolresolver.h93
-rw-r--r--src/util.cpp1045
-rw-r--r--src/util.h84
9 files changed, 1331 insertions, 1163 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c3d6f13..68de622 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -296,6 +296,7 @@ add_library(doxymain STATIC
searchindex.cpp
sqlite3gen.cpp
stlsupport.cpp
+ symbolresolver.cpp
tagreader.cpp
template.cpp
textdocvisitor.cpp
diff --git a/src/classdef.cpp b/src/classdef.cpp
index 91730a0..ffbbbb4 100644
--- a/src/classdef.cpp
+++ b/src/classdef.cpp
@@ -50,6 +50,7 @@
#include "namespacedef.h"
#include "membergroup.h"
#include "definitionimpl.h"
+#include "symbolresolver.h"
//-----------------------------------------------------------------------------
@@ -3231,7 +3232,8 @@ void ClassDefImpl::addTypeConstraint(const QCString &typeConstraint,const QCStri
//printf("addTypeConstraint(%s,%s)\n",type.data(),typeConstraint.data());
static bool hideUndocRelation = Config_getBool(HIDE_UNDOC_RELATIONS);
if (typeConstraint.isEmpty() || type.isEmpty()) return;
- ClassDef *cd = const_cast<ClassDef*>(getResolvedClass(this,getFileDef(),typeConstraint));
+ SymbolResolver resolver(getFileDef());
+ ClassDef *cd = const_cast<ClassDef*>(resolver.resolveClass(this,typeConstraint));
if (cd==0 && !hideUndocRelation)
{
cd = new ClassDefImpl(getDefFileName(),getDefLine(),getDefColumn(),typeConstraint,ClassDef::Class);
diff --git a/src/code.l b/src/code.l
index 6308dff..4ebd526 100644
--- a/src/code.l
+++ b/src/code.l
@@ -58,6 +58,7 @@
#include "namespacedef.h"
#include "tooltip.h"
#include "scopedtypevariant.h"
+#include "symbolresolver.h"
// Toggle for some debugging info
//#define DBG_CTX(x) fprintf x
@@ -176,6 +177,7 @@ struct codeYY_state
VariableContext theVarContext;
CallContext theCallContext;
TooltipManager tooltipManager;
+ SymbolResolver symbolResolver;
};
static bool isCastKeyword(const QCString &s);
@@ -757,7 +759,7 @@ NUMBER {INTEGER_NUMBER}|{FLOAT_NUMBER}
yyextra->scopeStack.push(CLASSBLOCK);
pushScope(yyscanner,yyextra->curClassName);
DBG_CTX((stderr,"***** yyextra->curClassName=%s\n",yyextra->curClassName.data()));
- if (getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,yyextra->curClassName)==0)
+ if (yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,yyextra->curClassName)==0)
{
DBG_CTX((stderr,"Adding new class %s\n",yyextra->curClassName.data()));
ScopedTypeVariant var(yyextra->curClassName);
@@ -771,7 +773,7 @@ NUMBER {INTEGER_NUMBER}|{FLOAT_NUMBER}
{
bcd = dynamic_cast<const ClassDef*>(it->second.globalDef());
}
- if (bcd==0) bcd=getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,s);
+ if (bcd==0) bcd=yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,s);
if (bcd && bcd->name()!=yyextra->curClassName)
{
var.localDef()->insertBaseClass(bcd->name());
@@ -1699,7 +1701,7 @@ NUMBER {INTEGER_NUMBER}|{FLOAT_NUMBER}
{
QCString scope = yyextra->name.left((uint)index);
if (!yyextra->classScope.isEmpty()) scope.prepend(yyextra->classScope+"::");
- const ClassDef *cd=getResolvedClass(Doxygen::globalScope,yyextra->sourceFileDef,scope);
+ const ClassDef *cd=yyextra->symbolResolver.resolveClass(Doxygen::globalScope,scope);
if (cd)
{
setClassScope(yyscanner,cd->name());
@@ -2222,7 +2224,7 @@ static void addVariable(yyscan_t yyscanner,QCString type,QCString name)
}
else
{
- const ClassDef *varDef = getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,ltype);
+ const ClassDef *varDef = yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,ltype);
int i=0;
if (varDef)
{
@@ -2570,11 +2572,11 @@ static const ClassDef *stripClassName(yyscan_t yyscanner,const char *s,const Def
const ClassDef *cd=0;
if (!yyextra->classScope.isEmpty())
{
- cd=getResolvedClass(d,yyextra->sourceFileDef,yyextra->classScope+"::"+clName);
+ cd=yyextra->symbolResolver.resolveClass(d,yyextra->classScope+"::"+clName);
}
if (cd==0)
{
- cd=getResolvedClass(d,yyextra->sourceFileDef,clName);
+ cd=yyextra->symbolResolver.resolveClass(d,clName);
}
//printf("stripClass trying '%s' = %p\n",clName.data(),cd);
if (cd)
@@ -2632,7 +2634,7 @@ static const MemberDef *setCallContextForVar(yyscan_t yyscanner,const QCString &
DBG_CTX((stderr,"local variable?\n"));
if (mcv->type()!=ScopedTypeVariant::Dummy) // locally found variable
{
- DBG_CTX((stderr,"local var '%s' mcd=%s\n",name.data(),mcv.name().data()));
+ DBG_CTX((stderr,"local var '%s' mcd=%s\n",name.data(),mcv->name().data()));
yyextra->theCallContext.setScope(*mcv);
}
}
@@ -2843,7 +2845,8 @@ static void generateClassOrGlobalLink(yyscan_t yyscanner,
{
const Definition *d = yyextra->currentDefinition;
//printf("d=%s yyextra->sourceFileDef=%s\n",d?d->name().data():"<none>",yyextra->sourceFileDef?yyextra->sourceFileDef->name().data():"<none>");
- cd = getResolvedClass(d,yyextra->sourceFileDef,className,&md);
+ cd = yyextra->symbolResolver.resolveClass(d,className);
+ md = yyextra->symbolResolver.getTypedef();
DBG_CTX((stderr,"non-local variable name=%s cd=%s md=%s!\n",
className.data(),cd?cd->name().data():"<none>",
md?md->name().data():"<none>"));
@@ -2853,7 +2856,8 @@ static void generateClassOrGlobalLink(yyscan_t yyscanner,
DBG_CTX((stderr,"bareName=%s\n",bareName.data()));
if (bareName!=className)
{
- cd=getResolvedClass(d,yyextra->sourceFileDef,bareName,&md); // try unspecialized version
+ cd = yyextra->symbolResolver.resolveClass(d,bareName); // try unspecialized version
+ md = yyextra->symbolResolver.getTypedef();
}
}
const NamespaceDef *nd = getResolvedNamespace(className);
@@ -2935,19 +2939,18 @@ static void generateClassOrGlobalLink(yyscan_t yyscanner,
{
if (md==0) // not found as a typedef
{
- AccessStack accessStack;
md = setCallContextForVar(yyscanner,clName);
//printf("setCallContextForVar(%s) md=%p yyextra->currentDefinition=%p\n",clName,md,yyextra->currentDefinition);
if (md && yyextra->currentDefinition)
{
DBG_CTX((stderr,"%s accessible from %s? %d md->getOuterScope=%s\n",
md->name().data(),yyextra->currentDefinition->name().data(),
- isAccessibleFrom(accessStack,yyextra->currentDefinition,yyextra->sourceFileDef,md),
+ yyextra->symbolResolver.isAccessibleFrom(yyextra->currentDefinition,md),
md->getOuterScope()->name().data()));
}
if (md && yyextra->currentDefinition &&
- isAccessibleFrom(accessStack,yyextra->currentDefinition,yyextra->sourceFileDef,md)==-1)
+ yyextra->symbolResolver.isAccessibleFrom(yyextra->currentDefinition,md)==-1)
{
md=0; // variable not accessible
}
@@ -3134,7 +3137,7 @@ static void generateMemberLink(yyscan_t yyscanner,
}
else // variable not in current context, maybe it is in a parent context
{
- const ClassDef *vcd = getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,yyextra->classScope);
+ const ClassDef *vcd = yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,yyextra->classScope);
if (vcd && vcd->isLinkable())
{
//printf("Found class %s for variable '%s'\n",yyextra->classScope.data(),varName.data());
@@ -3381,11 +3384,8 @@ static void writeObjCMethodCall(yyscan_t yyscanner,ObjCCallCtx *ctx)
}
else
{
- ctx->objectType = getResolvedClass(
- yyextra->currentDefinition,
- yyextra->sourceFileDef,
- ctx->objectTypeOrName,
- &ctx->method);
+ ctx->objectType = yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,ctx->objectTypeOrName);
+ ctx->method = yyextra->symbolResolver.getTypedef();
}
//printf(" object is class? %p\n",ctx->objectType);
if (ctx->objectType) // found class
@@ -3547,8 +3547,7 @@ static void writeObjCMethodCall(yyscan_t yyscanner,ObjCCallCtx *ctx)
}
else // object still needs to be resolved
{
- const ClassDef *cd = getResolvedClass(yyextra->currentDefinition,
- yyextra->sourceFileDef, object);
+ const ClassDef *cd = yyextra->symbolResolver.resolveClass(yyextra->currentDefinition, object);
if (cd && cd->isLinkable())
{
if (ctx->objectType==0) ctx->objectType=cd;
@@ -3836,6 +3835,7 @@ void CCodeParser::parseCode(CodeOutputInterface &od,const char *className,const
yyextra->searchCtx = searchCtx;
yyextra->collectXRefs = collectXRefs;
yyextra->inFunctionTryBlock = FALSE;
+ yyextra->symbolResolver.setFileScope(fd);
if (startLine!=-1)
yyextra->yyLineNr = startLine;
diff --git a/src/doxygen.cpp b/src/doxygen.cpp
index 988b308..b459a4b 100644
--- a/src/doxygen.cpp
+++ b/src/doxygen.cpp
@@ -106,6 +106,7 @@
#include "stlsupport.h"
#include "threadpool.h"
#include "clangparser.h"
+#include "symbolresolver.h"
// provided by the generated file resources.cpp
extern void initResources();
@@ -1813,7 +1814,8 @@ static void findUsingDeclarations(const Entry *root)
// vector -> std::vector
if (usingCd==0)
{
- usingCd = const_cast<ClassDef*>(getResolvedClass(nd,fd,name)); // try via resolving (see also bug757509)
+ SymbolResolver resolver(fd);
+ usingCd = const_cast<ClassDef*>(resolver.resolveClass(nd,name)); // try via resolving (see also bug757509)
}
if (usingCd==0)
{
@@ -1879,7 +1881,8 @@ static void findUsingDeclImports(const Entry *root)
{
QCString scope=root->name.left(i);
QCString memName=root->name.right(root->name.length()-i-2);
- const ClassDef *bcd = getResolvedClass(cd,0,scope); // todo: file in fileScope parameter
+ SymbolResolver resolver;
+ const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
if (bcd && bcd!=cd)
{
//printf("found class %s memName=%s\n",bcd->name().data(),memName.data());
@@ -2433,8 +2436,9 @@ static bool isVarWithConstructor(const Entry *root)
bool typePtrType = false;
QCString type;
Definition *ctx = 0;
- FileDef *fd = 0;
+ FileDef *fd = root->fileDef();
int ti;
+ SymbolResolver resolver(fd);
//printf("isVarWithConstructor(%s)\n",rootNav->name().data());
if (root->parent()->section & Entry::COMPOUND_MASK)
@@ -2442,9 +2446,7 @@ static bool isVarWithConstructor(const Entry *root)
result=FALSE;
goto done;
}
- else if ((fd = root->fileDef()) &&
- (fd->name().right(2)==".c" || fd->name().right(2)==".h")
- )
+ else if (fd->name().right(2)==".c" || fd->name().right(2)==".h")
{ // inside a .c file
result=FALSE;
goto done;
@@ -2467,10 +2469,10 @@ static bool isVarWithConstructor(const Entry *root)
//if (type.left(6)=="const ") type=type.right(type.length()-6);
if (!typePtrType)
{
- typeIsClass = getResolvedClass(ctx,fd,type)!=0;
+ typeIsClass = resolver.resolveClass(ctx,type)!=0;
if (!typeIsClass && (ti=type.find('<'))!=-1)
{
- typeIsClass=getResolvedClass(ctx,fd,type.left(ti))!=0;
+ typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=0;
}
}
if (typeIsClass) // now we still have to check if the arguments are
@@ -2505,7 +2507,7 @@ static bool isVarWithConstructor(const Entry *root)
result=FALSE;
goto done;
}
- if (a.type.isEmpty() || getResolvedClass(ctx,fd,a.type)!=0)
+ if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=0)
{
result=FALSE; // arg type is a known type
goto done;
@@ -3810,13 +3812,14 @@ static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,co
return result;
}
FileDef *fd=cd->getFileDef();
+ SymbolResolver resolver(fd);
if (context && cd!=context)
{
- result = const_cast<ClassDef*>(getResolvedClass(context,0,name,0,0,TRUE,TRUE));
+ result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
}
if (result==0)
{
- result = const_cast<ClassDef*>(getResolvedClass(cd,fd,name,0,0,TRUE,TRUE));
+ result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
}
if (result==0) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
{
@@ -3875,12 +3878,8 @@ static void findUsedClassesForClass(const Entry *root,
while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
{
// find the type (if any) that matches usedClassName
- const ClassDef *typeCd = getResolvedClass(masterCd,
- masterCd->getFileDef(),
- usedClassName,
- 0,0,
- FALSE,TRUE
- );
+ SymbolResolver resolver(masterCd->getFileDef());
+ const ClassDef *typeCd = resolver.resolveClass(masterCd,usedClassName,false,true);
//printf("====> usedClassName=%s -> typeCd=%s\n",
// usedClassName.data(),typeCd?typeCd->name().data():"<none>");
if (typeCd)
@@ -4259,17 +4258,15 @@ static bool findClassRelation(
//baseClassName=stripTemplateSpecifiersFromScope
// (removeRedundantWhiteSpace(baseClassName),TRUE,
// &stripped);
- const MemberDef *baseClassTypeDef=0;
- QCString templSpec;
+ SymbolResolver resolver(cd->getFileDef());
ClassDef *baseClass=const_cast<ClassDef*>(
- getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context,
- cd->getFileDef(),
+ resolver.resolveClass(explicitGlobalScope ? Doxygen::globalScope : context,
baseClassName,
- &baseClassTypeDef,
- &templSpec,
mode==Undocumented,
- TRUE
+ true
));
+ const MemberDef *baseClassTypeDef = resolver.getTypedef();
+ QCString templSpec = resolver.getTemplateSpec();
//printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
// baseClassName.data(),baseClass,cd,explicitGlobalScope);
//printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
@@ -4321,14 +4318,12 @@ static bool findClassRelation(
templSpec=removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
baseClassName=baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
baseClass=const_cast<ClassDef*>(
- getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context,
- cd->getFileDef(),
+ resolver.resolveClass(explicitGlobalScope ? Doxygen::globalScope : context,
baseClassName,
- &baseClassTypeDef,
- 0, //&templSpec,
mode==Undocumented,
- TRUE
+ true
));
+ baseClassTypeDef = resolver.getTypedef();
//printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
// baseClass,baseClassName.data(),templSpec.data());
}
@@ -4354,18 +4349,16 @@ static bool findClassRelation(
//printf("1. found=%d\n",found);
if (!found && si!=-1)
{
- QCString tmpTemplSpec;
// replace any namespace aliases
replaceNamespaceAliases(baseClassName,si);
baseClass=const_cast<ClassDef*>(
- getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context,
- cd->getFileDef(),
+ resolver.resolveClass(explicitGlobalScope ? Doxygen::globalScope : context,
baseClassName,
- &baseClassTypeDef,
- &tmpTemplSpec,
mode==Undocumented,
- TRUE
+ true
));
+ baseClassTypeDef = resolver.getTypedef();
+ QCString tmpTemplSpec = resolver.getTemplateSpec();
found=baseClass!=0 && baseClass!=cd;
if (found) templSpec = tmpTemplSpec;
}
@@ -5025,7 +5018,8 @@ static void addMemberDocs(const Entry *root,
static const ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd,
const char *scopeName)
{
- const ClassDef *tcd = getResolvedClass(nd,fd,scopeName,0,0,TRUE,TRUE);
+ SymbolResolver resolver(fd);
+ const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
return tcd;
}
diff --git a/src/pycode.l b/src/pycode.l
index 853e5cd..58f3daa 100644
--- a/src/pycode.l
+++ b/src/pycode.l
@@ -53,6 +53,7 @@
#include "namespacedef.h"
#include "tooltip.h"
#include "scopedtypevariant.h"
+#include "symbolresolver.h"
// Toggle for some debugging info
//#define DBG_CTX(x) fprintf x
@@ -107,6 +108,7 @@ struct pycodeYY_state
VariableContext theVarContext;
CallContext theCallContext;
TooltipManager tooltipManager;
+ SymbolResolver symbolResolver;
};
@@ -407,7 +409,7 @@ TARGET ({IDENTIFIER}|"("{TARGET_LIST}")"|"["{TARGET_LIST}"]"|{ATTRIBU
// Try to find class in global scope
if (baseDefToAdd==0)
{
- baseDefToAdd=getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,s);
+ baseDefToAdd=yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,s);
}
if (baseDefToAdd && baseDefToAdd->name()!=yyextra->curClassName)
@@ -966,11 +968,11 @@ static const ClassDef *stripClassName(yyscan_t yyscanner,const char *s,Definitio
const ClassDef *cd=0;
if (!yyextra->classScope.isEmpty())
{
- cd=getResolvedClass(d,yyextra->sourceFileDef,yyextra->classScope+"::"+clName);
+ cd=yyextra->symbolResolver.resolveClass(d,yyextra->classScope+"::"+clName);
}
if (cd==0)
{
- cd=getResolvedClass(d,yyextra->sourceFileDef,clName);
+ cd=yyextra->symbolResolver.resolveClass(d,clName);
}
if (cd)
{
@@ -1280,7 +1282,8 @@ static void generateClassOrGlobalLink(yyscan_t yyscanner,
Definition *d = yyextra->currentDefinition;
QCString scope = substitute(className,".","::");
- cd = getResolvedClass(d,yyextra->sourceFileDef,substitute(className,".","::"),&md);
+ cd = yyextra->symbolResolver.resolveClass(d,substitute(className,".","::"));
+ md = yyextra->symbolResolver.getTypedef();
DBG_CTX((stderr,"d=%s yyextra->sourceFileDef=%s\n",
d?d->displayName().data():"<null>",
@@ -1578,6 +1581,7 @@ void PythonCodeParser::parseCode(CodeOutputInterface &codeOutIntf,
yyextra->exampleBlock = isExampleBlock;
yyextra->exampleName = exampleName;
yyextra->sourceFileDef = fileDef;
+ yyextra->symbolResolver.setFileScope(fileDef);
bool cleanupSourceDef = FALSE;
if (yyextra->exampleBlock && fileDef==0)
diff --git a/src/symbolresolver.cpp b/src/symbolresolver.cpp
new file mode 100644
index 0000000..1c6baf4
--- /dev/null
+++ b/src/symbolresolver.cpp
@@ -0,0 +1,1149 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1997-2020 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#include <unordered_map>
+#include <string>
+#include <vector>
+
+#include "symbolresolver.h"
+#include "util.h"
+#include "doxygen.h"
+#include "namespacedef.h"
+#include "config.h"
+#include "defargs.h"
+
+static std::mutex g_cacheMutex;
+
+//--------------------------------------------------------------------------------------
+
+/** Helper class representing the stack of items considered while resolving
+ * the scope.
+ */
+class AccessStack
+{
+ /** Element in the stack. */
+ struct AccessElem
+ {
+ AccessElem(const Definition *d,const FileDef *f,const Definition *i,QCString e = QCString()) : scope(d), fileScope(f), item(i), expScope(e) {}
+ const Definition *scope;
+ const FileDef *fileScope;
+ const Definition *item;
+ QCString expScope;
+ };
+ public:
+ void push(const Definition *scope,const FileDef *fileScope,const Definition *item)
+ {
+ m_elements.push_back(AccessElem(scope,fileScope,item));
+ }
+ void push(const Definition *scope,const FileDef *fileScope,const Definition *item,const QCString &expScope)
+ {
+ m_elements.push_back(AccessElem(scope,fileScope,item,expScope));
+ }
+ void pop()
+ {
+ if (!m_elements.empty()) m_elements.pop_back();
+ }
+ bool find(const Definition *scope,const FileDef *fileScope, const Definition *item)
+ {
+ auto it = std::find_if(m_elements.begin(),m_elements.end(),
+ [&](const AccessElem &e) { return e.scope==scope && e.fileScope==fileScope && e.item==item; });
+ return it!=m_elements.end();
+ }
+ bool find(const Definition *scope,const FileDef *fileScope, const Definition *item,const QCString &expScope)
+ {
+ auto it = std::find_if(m_elements.begin(),m_elements.end(),
+ [&](const AccessElem &e) { return e.scope==scope && e.fileScope==fileScope && e.item==item && e.expScope==expScope; });
+ return it!=m_elements.end();
+ }
+ void clear()
+ {
+ m_elements.clear();
+ }
+
+ private:
+ std::vector<AccessElem> m_elements;
+};
+
+//--------------------------------------------------------------------------------------
+
+using VisitedNamespaces = std::unordered_map<std::string,const Definition *>;
+
+//--------------------------------------------------------------------------------------
+
+struct SymbolResolver::Private
+{
+ public:
+ Private(const FileDef *f) : m_fileScope(f) {}
+ void reset()
+ {
+ m_resolvedTypedefs.clear();
+ resolvedType.resize(0);
+ typeDef = 0;
+ templateSpec.resize(0);
+ }
+ void setFileScope(const FileDef *fileScope)
+ {
+ m_fileScope = fileScope;
+ }
+
+ QCString resolvedType;
+ const MemberDef *typeDef = 0;
+ QCString templateSpec;
+
+ const ClassDef *getResolvedClassRec(
+ const Definition *scope, // in
+ const char *n, // in
+ const MemberDef **pTypeDef, // out
+ QCString *pTemplSpec, // out
+ QCString *pResolvedType); // out
+
+ int isAccessibleFrom( AccessStack &accessStack,
+ const Definition *scope,
+ const Definition *item);
+
+ int isAccessibleFromWithExpScope(
+ VisitedNamespaces &visitedNamespaces,
+ AccessStack &accessStack,
+ const Definition *scope,
+ const Definition *item,
+ const QCString &explicitScopePart);
+
+ private:
+ void getResolvedSymbol(const Definition *scope, // in
+ const Definition *d, // in
+ const QCString &explicitScopePart, // in
+ const std::unique_ptr<ArgumentList> &actTemplParams, // in
+ int &minDistance, // input
+ const ClassDef *&bestMatch, // out
+ const MemberDef *&bestTypedef, // out
+ QCString &bestTemplSpec, // out
+ QCString &bestResolvedType // out
+ );
+
+ const ClassDef *newResolveTypedef(
+ const Definition *scope, // in
+ const MemberDef *md, // in
+ const MemberDef **pMemType, // out
+ QCString *pTemplSpec, // out
+ QCString *pResolvedType, // out
+ const std::unique_ptr<ArgumentList> &actTemplParams = std::unique_ptr<ArgumentList>()
+ );
+
+ const Definition *followPath(const Definition *start,const QCString &path);
+
+ const Definition *endOfPathIsUsedClass(const SDict<Definition> *cl,const QCString &localName);
+
+ bool accessibleViaUsingNamespace(StringUnorderedSet &visited,
+ const NamespaceSDict *nl,
+ const Definition *item,
+ const QCString &explicitScopePart="");
+ bool accessibleViaUsingClass(const SDict<Definition> *cl,
+ const Definition *item,
+ const QCString &explicitScopePart=""
+ );
+ QCString substTypedef(const Definition *scope,const QCString &name,
+ const MemberDef **pTypeDef=0);
+
+ const FileDef *m_fileScope;
+ std::unordered_map<std::string,const MemberDef*> m_resolvedTypedefs;
+};
+
+
+
+const ClassDef *SymbolResolver::Private::getResolvedClassRec(
+ const Definition *scope,
+ const char *n,
+ const MemberDef **pTypeDef,
+ QCString *pTemplSpec,
+ QCString *pResolvedType)
+{
+ if (n==0 || *n=='\0') return 0;
+ //static int level=0;
+ //fprintf(stderr,"%d [getResolvedClassRec(%s,%s)\n",level++,scope?scope->name().data():"<global>",n);
+ QCString name;
+ QCString explicitScopePart;
+ QCString strippedTemplateParams;
+ name=stripTemplateSpecifiersFromScope
+ (removeRedundantWhiteSpace(n),TRUE,
+ &strippedTemplateParams);
+ std::unique_ptr<ArgumentList> actTemplParams;
+ if (!strippedTemplateParams.isEmpty()) // template part that was stripped
+ {
+ actTemplParams = stringToArgumentList(scope->getLanguage(),strippedTemplateParams);
+ }
+
+ int qualifierIndex = computeQualifiedIndex(name);
+ //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
+ if (qualifierIndex!=-1) // qualified name
+ {
+ // split off the explicit scope part
+ explicitScopePart=name.left(qualifierIndex);
+ // todo: improve namespace alias substitution
+ replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
+ name=name.mid(qualifierIndex+2);
+ }
+
+ if (name.isEmpty())
+ {
+ //fprintf(stderr,"%d ] empty name\n",--level);
+ return 0; // empty name
+ }
+
+ //printf("Looking for symbol %s\n",name.data());
+ auto range = Doxygen::symbolMap.find(name);
+ // the -g (for C# generics) and -p (for ObjC protocols) are now already
+ // stripped from the key used in the symbolMap, so that is not needed here.
+ if (range.first==range.second)
+ {
+ range = Doxygen::symbolMap.find(name+"-p");
+ if (range.first==range.second)
+ {
+ //fprintf(stderr,"%d ] no such symbol!\n",--level);
+ return 0;
+ }
+ }
+ //printf("found symbol!\n");
+
+ bool hasUsingStatements =
+ (m_fileScope && ((m_fileScope->getUsedNamespaces() &&
+ m_fileScope->getUsedNamespaces()->count()>0) ||
+ (m_fileScope->getUsedClasses() &&
+ m_fileScope->getUsedClasses()->count()>0))
+ );
+ //printf("hasUsingStatements=%d\n",hasUsingStatements);
+ // Since it is often the case that the same name is searched in the same
+ // scope over an over again (especially for the linked source code generation)
+ // we use a cache to collect previous results. This is possible since the
+ // result of a lookup is deterministic. As the key we use the concatenated
+ // scope, the name to search for and the explicit scope prefix. The speedup
+ // achieved by this simple cache can be enormous.
+ int scopeNameLen = scope->name().length()+1;
+ int nameLen = name.length()+1;
+ int explicitPartLen = explicitScopePart.length();
+ int fileScopeLen = hasUsingStatements ? 1+m_fileScope->absFilePath().length() : 0;
+
+ // below is a more efficient coding of
+ // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
+ QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
+ char *pk=key.rawData();
+ qstrcpy(pk,scope->name()); *(pk+scopeNameLen-1)='+';
+ pk+=scopeNameLen;
+ qstrcpy(pk,name); *(pk+nameLen-1)='+';
+ pk+=nameLen;
+ qstrcpy(pk,explicitScopePart);
+ pk+=explicitPartLen;
+
+ // if a file scope is given and it contains using statements we should
+ // also use the file part in the key (as a class name can be in
+ // two different namespaces and a using statement in a file can select
+ // one of them).
+ if (hasUsingStatements)
+ {
+ // below is a more efficient coding of
+ // key+="+"+m_fileScope->name();
+ *pk++='+';
+ qstrcpy(pk,m_fileScope->absFilePath());
+ pk+=fileScopeLen-1;
+ }
+ *pk='\0';
+
+ LookupInfo *pval = 0;
+ {
+ std::lock_guard<std::mutex> lock(g_cacheMutex);
+ pval=Doxygen::lookupCache->find(key.str());
+ //printf("Searching for %s result=%p\n",key.data(),pval);
+ if (pval)
+ {
+ //printf("LookupInfo %p %p '%s' %p\n",
+ // pval->classDef, pval->typeDef, pval->templSpec.data(),
+ // pval->resolvedType.data());
+ if (pTemplSpec) *pTemplSpec=pval->templSpec;
+ if (pTypeDef) *pTypeDef=pval->typeDef;
+ if (pResolvedType) *pResolvedType=pval->resolvedType;
+ //fprintf(stderr,"%d ] cachedMatch=%s\n",--level,
+ // pval->classDef?pval->classDef->name().data():"<none>");
+ //if (pTemplSpec)
+ // printf("templSpec=%s\n",pTemplSpec->data());
+ return pval->classDef;
+ }
+ else // not found yet; we already add a 0 to avoid the possibility of
+ // endless recursion.
+ {
+ pval = Doxygen::lookupCache->insert(key.str(),LookupInfo());
+ }
+ }
+
+ const ClassDef *bestMatch=0;
+ const MemberDef *bestTypedef=0;
+ QCString bestTemplSpec;
+ QCString bestResolvedType;
+ int minDistance=10000; // init at "infinite"
+
+ for (auto it=range.first ; it!=range.second; ++it)
+ {
+ Definition *d = it->second;
+ getResolvedSymbol(scope,d,explicitScopePart,actTemplParams,
+ minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType);
+ }
+
+ if (pTypeDef)
+ {
+ *pTypeDef = bestTypedef;
+ }
+ if (pTemplSpec)
+ {
+ *pTemplSpec = bestTemplSpec;
+ }
+ if (pResolvedType)
+ {
+ *pResolvedType = bestResolvedType;
+ }
+
+ //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
+ // bestMatch,bestResolvedType.data());
+
+ if (pval)
+ {
+ std::lock_guard<std::mutex> lock(g_cacheMutex);
+ pval->classDef = bestMatch;
+ pval->typeDef = bestTypedef;
+ pval->templSpec = bestTemplSpec;
+ pval->resolvedType = bestResolvedType;
+ }
+ //fprintf(stderr,"%d ] bestMatch=%s distance=%d\n",--level,
+ // bestMatch?bestMatch->name().data():"<none>",minDistance);
+ //if (pTemplSpec)
+ // printf("templSpec=%s\n",pTemplSpec->data());
+ return bestMatch;
+}
+
+void SymbolResolver::Private::getResolvedSymbol(
+ const Definition *scope, // in
+ const Definition *d, // in
+ const QCString &explicitScopePart, // in
+ const std::unique_ptr<ArgumentList> &actTemplParams, // in
+ int &minDistance, // inout
+ const ClassDef *&bestMatch, // out
+ const MemberDef *&bestTypedef, // out
+ QCString &bestTemplSpec, // out
+ QCString &bestResolvedType // out
+ )
+{
+ //fprintf(stderr,"getResolvedSymbol(%s,%s)\n",scope->name().data(),d->qualifiedName().data());
+ // only look at classes and members that are enums or typedefs
+ if (d->definitionType()==Definition::TypeClass ||
+ (d->definitionType()==Definition::TypeMember &&
+ ((dynamic_cast<const MemberDef*>(d))->isTypedef() ||
+ (dynamic_cast<const MemberDef*>(d))->isEnumerate())
+ )
+ )
+ {
+ VisitedNamespaces visitedNamespaces;
+ AccessStack accessStack;
+ // test accessibility of definition within scope.
+ int distance = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,d,explicitScopePart);
+ //fprintf(stderr," %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
+ if (distance!=-1) // definition is accessible
+ {
+ // see if we are dealing with a class or a typedef
+ if (d->definitionType()==Definition::TypeClass) // d is a class
+ {
+ const ClassDef *cd = dynamic_cast<const ClassDef *>(d);
+ //printf("cd=%s\n",cd->name().data());
+ if (!cd->isTemplateArgument()) // skip classes that
+ // are only there to
+ // represent a template
+ // argument
+ {
+ //printf("is not a templ arg\n");
+ if (distance<minDistance) // found a definition that is "closer"
+ {
+ minDistance=distance;
+ bestMatch = cd;
+ bestTypedef = 0;
+ bestTemplSpec.resize(0);
+ bestResolvedType = cd->qualifiedName();
+ }
+ else if (distance==minDistance &&
+ m_fileScope && bestMatch &&
+ m_fileScope->getUsedNamespaces() &&
+ d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
+ bestMatch->getOuterScope()==Doxygen::globalScope
+ )
+ {
+ // in case the distance is equal it could be that a class X
+ // is defined in a namespace and in the global scope. When searched
+ // in the global scope the distance is 0 in both cases. We have
+ // to choose one of the definitions: we choose the one in the
+ // namespace if the fileScope imports namespaces and the definition
+ // found was in a namespace while the best match so far isn't.
+ // Just a non-perfect heuristic but it could help in some situations
+ // (kdecore code is an example).
+ minDistance=distance;
+ bestMatch = cd;
+ bestTypedef = 0;
+ bestTemplSpec.resize(0);
+ bestResolvedType = cd->qualifiedName();
+ }
+ }
+ else
+ {
+ //printf(" is a template argument!\n");
+ }
+ }
+ else if (d->definitionType()==Definition::TypeMember)
+ {
+ const MemberDef *md = dynamic_cast<const MemberDef *>(d);
+ //fprintf(stderr," member isTypedef()=%d\n",md->isTypedef());
+ if (md->isTypedef()) // d is a typedef
+ {
+ QCString args=md->argsString();
+ if (args.isEmpty()) // do not expand "typedef t a[4];"
+ {
+ //printf(" found typedef!\n");
+
+ // we found a symbol at this distance, but if it didn't
+ // resolve to a class, we still have to make sure that
+ // something at a greater distance does not match, since
+ // that symbol is hidden by this one.
+ if (distance<minDistance)
+ {
+ QCString spec;
+ QCString type;
+ minDistance=distance;
+ const MemberDef *enumType = 0;
+ const ClassDef *cd = newResolveTypedef(scope,md,&enumType,&spec,&type,actTemplParams);
+ if (cd) // type resolves to a class
+ {
+ //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
+ bestMatch = cd;
+ bestTypedef = md;
+ bestTemplSpec = spec;
+ bestResolvedType = type;
+ }
+ else if (enumType) // type resolves to a member type
+ {
+ //printf(" is enum\n");
+ bestMatch = 0;
+ bestTypedef = enumType;
+ bestTemplSpec = "";
+ bestResolvedType = enumType->qualifiedName();
+ }
+ else if (md->isReference()) // external reference
+ {
+ bestMatch = 0;
+ bestTypedef = md;
+ bestTemplSpec = spec;
+ bestResolvedType = type;
+ }
+ else
+ {
+ bestMatch = 0;
+ bestTypedef = md;
+ bestTemplSpec.resize(0);
+ bestResolvedType.resize(0);
+ //printf(" no match\n");
+ }
+ }
+ else
+ {
+ //printf(" not the best match %d min=%d\n",distance,minDistance);
+ }
+ }
+ else
+ {
+ //printf(" not a simple typedef\n")
+ }
+ }
+ else if (md->isEnumerate())
+ {
+ if (distance<minDistance)
+ {
+ minDistance=distance;
+ bestMatch = 0;
+ bestTypedef = md;
+ bestTemplSpec = "";
+ bestResolvedType = md->qualifiedName();
+ }
+ }
+ }
+ } // if definition accessible
+ else
+ {
+ //printf(" Not accessible!\n");
+ }
+ } // if definition is a class or member
+ //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
+}
+
+const ClassDef *SymbolResolver::Private::newResolveTypedef(
+ const Definition *scope, // in
+ const MemberDef *md, // in
+ const MemberDef **pMemType, // out
+ QCString *pTemplSpec, // out
+ QCString *pResolvedType, // out
+ const std::unique_ptr<ArgumentList> &actTemplParams) // in
+{
+ //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
+ bool isCached = md->isTypedefValCached(); // value already cached
+ if (isCached)
+ {
+ //printf("Already cached %s->%s [%s]\n",
+ // md->name().data(),
+ // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
+ // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
+
+ if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
+ if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
+ return md->getCachedTypedefVal();
+ }
+ //printf("new typedef\n");
+ QCString qname = md->qualifiedName();
+ if (m_resolvedTypedefs.find(qname.str())!=m_resolvedTypedefs.end())
+ {
+ return 0; // typedef already done
+ }
+
+ auto typedef_it = m_resolvedTypedefs.insert({qname.str(),md}).first; // put on the trace list
+
+ const ClassDef *typeClass = md->getClassDef();
+ QCString type = md->typeString(); // get the "value" of the typedef
+ if (typeClass && typeClass->isTemplate() &&
+ actTemplParams && !actTemplParams->empty())
+ {
+ type = substituteTemplateArgumentsInString(type,
+ typeClass->templateArguments(),actTemplParams);
+ }
+ QCString typedefValue = type;
+ int tl=type.length();
+ int ip=tl-1; // remove * and & at the end
+ while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
+ {
+ ip--;
+ }
+ type=type.left(ip+1);
+ type.stripPrefix("const "); // strip leading "const"
+ type.stripPrefix("struct "); // strip leading "struct"
+ type.stripPrefix("union "); // strip leading "union"
+ int sp=0;
+ tl=type.length(); // length may have been changed
+ while (sp<tl && type.at(sp)==' ') sp++;
+ const MemberDef *memTypeDef = 0;
+ const ClassDef *result = getResolvedClassRec(md->getOuterScope(),type,
+ &memTypeDef,0,pResolvedType);
+ // if type is a typedef then return what it resolves to.
+ if (memTypeDef && memTypeDef->isTypedef())
+ {
+ result=newResolveTypedef(m_fileScope,memTypeDef,pMemType,pTemplSpec,0);
+ goto done;
+ }
+ else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
+ {
+ *pMemType = memTypeDef;
+ }
+
+ //printf("type=%s result=%p\n",type.data(),result);
+ if (result==0)
+ {
+ // try unspecialized version if type is template
+ int si=type.findRev("::");
+ int i=type.find('<');
+ if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
+ {
+ if (pTemplSpec) *pTemplSpec = type.mid(i);
+ result = getResolvedClassRec(md->getOuterScope(),type.left(i),0,0,pResolvedType);
+ //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
+ // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
+ }
+ else if (si!=-1) // A::B
+ {
+ i=type.find('<',si);
+ if (i==-1) // Something like A<T>::B => lookup A::B
+ {
+ i=type.length();
+ }
+ else // Something like A<T>::B<S> => lookup A::B, spec=<S>
+ {
+ if (pTemplSpec) *pTemplSpec = type.mid(i);
+ }
+ result = getResolvedClassRec(md->getOuterScope(),
+ stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,pResolvedType);
+ }
+
+ //if (result) ip=si+sp+1;
+ }
+
+done:
+ if (pResolvedType)
+ {
+ if (result)
+ {
+ *pResolvedType = result->qualifiedName();
+ //printf("*pResolvedType=%s\n",pResolvedType->data());
+ if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
+ if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
+ }
+ else
+ {
+ *pResolvedType = typedefValue;
+ }
+ }
+
+ // remember computed value for next time
+ if (result && result->getDefFileName()!="<code>")
+ // this check is needed to prevent that temporary classes that are
+ // introduced while parsing code fragments are being cached here.
+ {
+ //printf("setting cached typedef %p in result %p\n",md,result);
+ //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
+ //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
+ const_cast<MemberDef*>(md)->cacheTypedefVal(result,
+ pTemplSpec ? *pTemplSpec : QCString(),
+ pResolvedType ? *pResolvedType : QCString()
+ );
+ }
+
+ m_resolvedTypedefs.erase(typedef_it); // remove from the trace list
+
+ return result;
+}
+
+int SymbolResolver::Private::isAccessibleFromWithExpScope(
+ VisitedNamespaces &visitedNamespaces,
+ AccessStack &accessStack,
+ const Definition *scope,
+ const Definition *item,
+ const QCString &explicitScopePart)
+{
+ if (explicitScopePart.isEmpty())
+ {
+ // handle degenerate case where there is no explicit scope.
+ return isAccessibleFrom(accessStack,scope,item);
+ }
+
+ if (accessStack.find(scope,m_fileScope,item,explicitScopePart))
+ {
+ return -1;
+ }
+ accessStack.push(scope,m_fileScope,item,explicitScopePart);
+
+
+ //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
+ // item?item->name().data():"<none>",
+ // explicitScopePart.data());
+ int result=0; // assume we found it
+ const Definition *newScope = followPath(scope,explicitScopePart);
+ if (newScope) // explicitScope is inside scope => newScope is the result
+ {
+ Definition *itemScope = item->getOuterScope();
+ //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
+ //if (newScope && newScope->definitionType()==Definition::TypeClass)
+ //{
+ // ClassDef *cd = (ClassDef *)newScope;
+ // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
+ //}
+ if (itemScope==newScope) // exact match of scopes => distance==0
+ {
+ //printf("> found it\n");
+ }
+ else if (itemScope && newScope &&
+ itemScope->definitionType()==Definition::TypeClass &&
+ newScope->definitionType()==Definition::TypeClass &&
+ (dynamic_cast<const ClassDef*>(newScope))->isBaseClass(dynamic_cast<const ClassDef*>(itemScope),TRUE,0)
+ )
+ {
+ // inheritance is also ok. Example: looking for B::I, where
+ // class A { public: class I {} };
+ // class B : public A {}
+ // but looking for B::I, where
+ // class A { public: class I {} };
+ // class B { public: class I {} };
+ // will find A::I, so we still prefer a direct match and give this one a distance of 1
+ result=1;
+
+ //printf("scope(%s) is base class of newScope(%s)\n",
+ // scope->name().data(),newScope->name().data());
+ }
+ else
+ {
+ int i=-1;
+ if (newScope->definitionType()==Definition::TypeNamespace)
+ {
+ visitedNamespaces.insert({newScope->name().str(),newScope});
+ // this part deals with the case where item is a class
+ // A::B::C but is explicit referenced as A::C, where B is imported
+ // in A via a using directive.
+ //printf("newScope is a namespace: %s!\n",newScope->name().data());
+ const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(newScope);
+ const SDict<Definition> *cl = nscope->getUsedClasses();
+ if (cl)
+ {
+ SDict<Definition>::Iterator cli(*cl);
+ const Definition *cd;
+ for (cli.toFirst();(cd=cli.current());++cli)
+ {
+ //printf("Trying for class %s\n",cd->name().data());
+ if (cd==item)
+ {
+ //printf("> class is used in this scope\n");
+ goto done;
+ }
+ }
+ }
+ const NamespaceSDict *nl = nscope->getUsedNamespaces();
+ if (nl)
+ {
+ NamespaceSDict::Iterator nli(*nl);
+ const NamespaceDef *nd;
+ for (nli.toFirst();(nd=nli.current());++nli)
+ {
+ if (visitedNamespaces.find(nd->name().str())==visitedNamespaces.end())
+ {
+ //printf("Trying for namespace %s\n",nd->name().data());
+ i = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,item,nd->name());
+ if (i!=-1)
+ {
+ //printf("> found via explicit scope of used namespace\n");
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ // repeat for the parent scope
+ if (scope!=Doxygen::globalScope)
+ {
+ i = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope->getOuterScope(),item,explicitScopePart);
+ }
+ //printf(" | result=%d\n",i);
+ result = (i==-1) ? -1 : i+2;
+ }
+ }
+ else // failed to resolve explicitScope
+ {
+ //printf(" failed to resolve: scope=%s\n",scope->name().data());
+ if (scope->definitionType()==Definition::TypeNamespace)
+ {
+ const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope);
+ const NamespaceSDict *nl = nscope->getUsedNamespaces();
+ StringUnorderedSet visited;
+ if (accessibleViaUsingNamespace(visited,nl,item,explicitScopePart))
+ {
+ //printf("> found in used namespace\n");
+ goto done;
+ }
+ }
+ if (scope==Doxygen::globalScope)
+ {
+ if (m_fileScope)
+ {
+ const NamespaceSDict *nl = m_fileScope->getUsedNamespaces();
+ StringUnorderedSet visited;
+ if (accessibleViaUsingNamespace(visited,nl,item,explicitScopePart))
+ {
+ //printf("> found in used namespace\n");
+ goto done;
+ }
+ }
+ //printf("> not found\n");
+ result=-1;
+ }
+ else // continue by looking into the parent scope
+ {
+ int i=isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope->getOuterScope(),item,explicitScopePart);
+ //printf("> result=%d\n",i);
+ result= (i==-1) ? -1 : i+2;
+ }
+ }
+
+done:
+ //printf(" > result=%d\n",result);
+ accessStack.pop();
+ return result;
+}
+
+const Definition *SymbolResolver::Private::followPath(const Definition *start,const QCString &path)
+{
+ int is,ps;
+ int l;
+ const Definition *current=start;
+ ps=0;
+ //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
+ // for each part of the explicit scope
+ while ((is=getScopeFragment(path,ps,&l))!=-1)
+ {
+ // try to resolve the part if it is a typedef
+ const MemberDef *memTypeDef=0;
+ QCString qualScopePart = substTypedef(current,path.mid(is,l),&memTypeDef);
+ //printf(" qualScopePart=%s\n",qualScopePart.data());
+ if (memTypeDef)
+ {
+ const ClassDef *type = newResolveTypedef(m_fileScope,memTypeDef,0,0,0);
+ if (type)
+ {
+ //printf("Found type %s\n",type->name().data());
+ return type;
+ }
+ }
+ const Definition *next = current->findInnerCompound(qualScopePart);
+ //printf("++ Looking for %s inside %s result %s\n",
+ // qualScopePart.data(),
+ // current->name().data(),
+ // next?next->name().data():"<null>");
+ if (next==0) // failed to follow the path
+ {
+ //printf("==> next==0!\n");
+ if (current->definitionType()==Definition::TypeNamespace)
+ {
+ next = endOfPathIsUsedClass(
+ (dynamic_cast<const NamespaceDef *>(current))->getUsedClasses(),qualScopePart);
+ }
+ else if (current->definitionType()==Definition::TypeFile)
+ {
+ next = endOfPathIsUsedClass(
+ (dynamic_cast<const FileDef *>(current))->getUsedClasses(),qualScopePart);
+ }
+ current = next;
+ if (current==0) break;
+ }
+ else // continue to follow scope
+ {
+ current = next;
+ //printf("==> current = %p\n",current);
+ }
+ ps=is+l;
+ }
+ //printf("followPath(start=%s,path=%s) result=%s\n",
+ // start->name().data(),path.data(),current?current->name().data():"<null>");
+ return current; // path could be followed
+}
+
+const Definition *SymbolResolver::Private::endOfPathIsUsedClass(const SDict<Definition> *cl,const QCString &localName)
+{
+ if (cl)
+ {
+ SDict<Definition>::Iterator cli(*cl);
+ Definition *cd;
+ for (cli.toFirst();(cd=cli.current());++cli)
+ {
+ if (cd->localName()==localName)
+ {
+ return cd;
+ }
+ }
+ }
+ return 0;
+}
+
+bool SymbolResolver::Private::accessibleViaUsingNamespace(StringUnorderedSet &visited,
+ const NamespaceSDict *nl,
+ const Definition *item,
+ const QCString &explicitScopePart)
+{
+ if (nl) // check used namespaces for the class
+ {
+ NamespaceSDict::Iterator nli(*nl);
+ NamespaceDef *und;
+ int count=0;
+ for (nli.toFirst();(und=nli.current());++nli,count++)
+ {
+ //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
+ // count,nl->count());
+ const Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,explicitScopePart);
+ if (sc && item->getOuterScope()==sc)
+ {
+ //printf("] found it\n");
+ return true;
+ }
+ if (item->getLanguage()==SrcLangExt_Cpp)
+ {
+ QCString key=und->name();
+ if (und->getUsedNamespaces() && visited.find(key.str())==visited.end())
+ {
+ visited.insert(key.str());
+
+ if (accessibleViaUsingNamespace(visited,und->getUsedNamespaces(),item,explicitScopePart))
+ {
+ //printf("] found it via recursion\n");
+ return true;
+ }
+
+ visited.erase(key.str());
+ }
+ }
+ //printf("] Try via used namespace done\n");
+ }
+ }
+ return false;
+}
+
+
+bool SymbolResolver::Private::accessibleViaUsingClass(const SDict<Definition> *cl,
+ const Definition *item,
+ const QCString &explicitScopePart)
+{
+ //printf("accessibleViaUsingClass(%p)\n",cl);
+ if (cl) // see if the class was imported via a using statement
+ {
+ SDict<Definition>::Iterator cli(*cl);
+ Definition *ucd;
+ bool explicitScopePartEmpty = explicitScopePart.isEmpty();
+ for (cli.toFirst();(ucd=cli.current());++cli)
+ {
+ //printf("Trying via used class %s\n",ucd->name().data());
+ const Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,explicitScopePart);
+ if (sc && sc==item) return TRUE;
+ //printf("Try via used class done\n");
+ }
+ }
+ return FALSE;
+}
+
+int SymbolResolver::Private::isAccessibleFrom(AccessStack &accessStack,
+ const Definition *scope,
+ const Definition *item)
+{
+ //printf("<isAccessibleFrom(scope=%s,item=%s itemScope=%s)\n",
+ // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
+
+ if (accessStack.find(scope,m_fileScope,item))
+ {
+ return -1;
+ }
+ accessStack.push(scope,m_fileScope,item);
+
+ int result=0; // assume we found it
+ int i;
+
+ Definition *itemScope=item->getOuterScope();
+ bool memberAccessibleFromScope =
+ (item->definitionType()==Definition::TypeMember && // a member
+ itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
+ scope->definitionType()==Definition::TypeClass && // accessible
+ (dynamic_cast<const ClassDef*>(scope))->isAccessibleMember(dynamic_cast<const MemberDef *>(item)) // from scope
+ );
+ bool nestedClassInsideBaseClass =
+ (item->definitionType()==Definition::TypeClass && // a nested class
+ itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
+ scope->definitionType()==Definition::TypeClass && // class of scope
+ (dynamic_cast<const ClassDef*>(scope))->isBaseClass(dynamic_cast<ClassDef*>(itemScope),TRUE)
+ );
+
+ if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass)
+ {
+ //printf("> found it\n");
+ if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent
+ // this is preferred over nested class in this class
+ // see bug 686956
+ }
+ else if (scope==Doxygen::globalScope)
+ {
+ if (m_fileScope)
+ {
+ SDict<Definition> *cl = m_fileScope->getUsedClasses();
+ if (accessibleViaUsingClass(cl,item))
+ {
+ //printf("> found via used class\n");
+ goto done;
+ }
+ NamespaceSDict *nl = m_fileScope->getUsedNamespaces();
+ StringUnorderedSet visited;
+ if (accessibleViaUsingNamespace(visited,nl,item))
+ {
+ //printf("> found via used namespace\n");
+ goto done;
+ }
+ }
+ //printf("> reached global scope\n");
+ result=-1; // not found in path to globalScope
+ }
+ else // keep searching
+ {
+ // check if scope is a namespace, which is using other classes and namespaces
+ if (scope->definitionType()==Definition::TypeNamespace)
+ {
+ const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope);
+ //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
+ const SDict<Definition> *cl = nscope->getUsedClasses();
+ if (accessibleViaUsingClass(cl,item))
+ {
+ //printf("> found via used class\n");
+ goto done;
+ }
+ const NamespaceSDict *nl = nscope->getUsedNamespaces();
+ StringUnorderedSet visited;
+ if (accessibleViaUsingNamespace(visited,nl,item))
+ {
+ //printf("> found via used namespace\n");
+ goto done;
+ }
+ }
+ // repeat for the parent scope
+ i=isAccessibleFrom(accessStack,scope->getOuterScope(),item);
+ //printf("> result=%d\n",i);
+ result= (i==-1) ? -1 : i+2;
+ }
+done:
+ accessStack.pop();
+ return result;
+}
+
+QCString SymbolResolver::Private::substTypedef(
+ const Definition *scope,const QCString &name,
+ const MemberDef **pTypeDef)
+{
+ QCString result=name;
+ if (name.isEmpty()) return result;
+
+ auto range = Doxygen::symbolMap.find(name);
+ if (range.first==range.second)
+ return result; // no matches
+
+ MemberDef *bestMatch=0;
+ int minDistance=10000; // init at "infinite"
+
+ for (auto it = range.first; it!=range.second; ++it)
+ {
+ Definition *d = it->second;
+ // only look at members
+ if (d->definitionType()==Definition::TypeMember)
+ {
+ // that are also typedefs
+ MemberDef *md = dynamic_cast<MemberDef *>(d);
+ if (md->isTypedef()) // d is a typedef
+ {
+ VisitedNamespaces visitedNamespaces;
+ AccessStack accessStack;
+ // test accessibility of typedef within scope.
+ int distance = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,d,"");
+ if (distance!=-1 && distance<minDistance)
+ // definition is accessible and a better match
+ {
+ minDistance=distance;
+ bestMatch = md;
+ }
+ }
+ }
+ }
+
+ if (bestMatch)
+ {
+ result = bestMatch->typeString();
+ if (pTypeDef) *pTypeDef=bestMatch;
+ }
+
+ //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
+ // name.data(),result.data());
+ return result;
+}
+
+//----------------------------------------------------------------------------------------------
+
+
+SymbolResolver::SymbolResolver(const FileDef *fileScope)
+ : p(std::make_unique<Private>(fileScope))
+{
+}
+
+SymbolResolver::~SymbolResolver()
+{
+}
+
+
+const ClassDef *SymbolResolver::resolveClass(const Definition *scope,
+ const char *name,
+ bool mayBeUnlinkable,
+ bool mayBeHidden)
+{
+ p->reset();
+
+ if (scope==0 ||
+ (scope->definitionType()!=Definition::TypeClass &&
+ scope->definitionType()!=Definition::TypeNamespace
+ ) ||
+ (scope->getLanguage()==SrcLangExt_Java && QCString(name).find("::")!=-1)
+ )
+ {
+ scope=Doxygen::globalScope;
+ }
+ //fprintf(stderr,"------------ resolveClass(scope=%s,name=%s,mayUnlinkable=%d)\n",
+ // scope?scope->name().data():"<global>",
+ // name,
+ // mayBeUnlinkable
+ // );
+ const ClassDef *result;
+ if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
+ {
+ result = getClass(name);
+ }
+ else
+ {
+ result = p->getResolvedClassRec(scope,name,&p->typeDef,&p->templateSpec,&p->resolvedType);
+ if (result==0) // for nested classes imported via tag files, the scope may not
+ // present, so we check the class name directly as well.
+ // See also bug701314
+ {
+ result = getClass(name);
+ }
+ }
+ if (!mayBeUnlinkable && result && !result->isLinkable())
+ {
+ if (!mayBeHidden || !result->isHidden())
+ {
+ //printf("result was %s\n",result?result->name().data():"<none>");
+ result=0; // don't link to artificial/hidden classes unless explicitly allowed
+ }
+ }
+ //fprintf(stderr,"ResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
+ // name,result?result->name().data():"<none>");
+ return result;
+}
+
+int SymbolResolver::isAccessibleFrom(const Definition *scope,const Definition *item)
+{
+ p->reset();
+ AccessStack accessStack;
+ return p->isAccessibleFrom(accessStack,scope,item);
+}
+
+int SymbolResolver::isAccessibleFromWithExpScope(const Definition *scope,const Definition *item,
+ const QCString &explicitScopePart)
+{
+ p->reset();
+ VisitedNamespaces visitedNamespaces;
+ AccessStack accessStack;
+ return p->isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,item,explicitScopePart);
+}
+
+void SymbolResolver::setFileScope(const FileDef *fileScope)
+{
+ p->setFileScope(fileScope);
+}
+
+const MemberDef *SymbolResolver::getTypedef() const
+{
+ return p->typeDef;
+}
+
+QCString SymbolResolver::getTemplateSpec() const
+{
+ return p->templateSpec;
+}
+
+QCString SymbolResolver::getResolvedType() const
+{
+ return p->resolvedType;
+}
+
diff --git a/src/symbolresolver.h b/src/symbolresolver.h
new file mode 100644
index 0000000..e00569d
--- /dev/null
+++ b/src/symbolresolver.h
@@ -0,0 +1,93 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1997-2020 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#ifndef SYMBOLRESOLVER_H
+#define SYMBOLRESOLVER_H
+
+#include <memory>
+#include "qcstring.h"
+
+class Definition;
+class ClassDef;
+class FileDef;
+class MemberDef;
+
+//! Helper class to find a class definition or check if
+//! A symbol is accessible in a given scope.
+class SymbolResolver
+{
+ public:
+ explicit SymbolResolver(const FileDef *fileScope = 0);
+ ~SymbolResolver();
+
+ // actions
+
+ /** Find the class definition matching name within
+ * the scope set.
+ * @param scope The scope to search from.
+ * @param name The name of the symbol.
+ * @param maybeUnlinkable include unlinkable symbols in the search.
+ * @param myBeHidden include hidden symbols in the search.
+ * @note As a result of this call the getters getTypedef(),
+ * getTemplateSpec(), and getResolvedType() are set as well.
+ */
+ const ClassDef *resolveClass(const Definition *scope,
+ const char *name,
+ bool maybeUnlinkable=false,
+ bool myBeHidden=false);
+
+ /** Checks if symbol \a item is accessible from within \a scope.
+ * @returns -1 if \a item is not accessible or a number indicating how
+ * many scope levels up the nearest match was found.
+ */
+ int isAccessibleFrom(const Definition *scope,
+ const Definition *item);
+
+ /** Check if symbol \a item is accessible from within \a scope,
+ * where it has to match the \a explicitScopePart.
+ * @returns -1 if \a item is not accessible or a number indicating how
+ * many scope levels up the nearest match was found.
+ */
+ int isAccessibleFromWithExpScope(const Definition *scope,
+ const Definition *item,
+ const QCString &explicitScopePart
+ );
+
+ /** Sets or updates the file scope using when resolving symbols. */
+ void setFileScope(const FileDef *fd);
+
+ // getters
+
+ /** In case a call to resolveClass() resolves to a type member (e.g. an enum)
+ * this method will return it.
+ */
+ const MemberDef *getTypedef() const;
+
+ /** In case a call to resolveClass() points to a template specialization, the
+ * template part is return via this method.
+ */
+ QCString getTemplateSpec() const;
+
+ /** In case a call to resolveClass() points to a typedef or using declaration.
+ * The type name it resolved to is returned via this method.
+ */
+ QCString getResolvedType() const;
+
+ private:
+ struct Private;
+ std::unique_ptr<Private> p;
+};
+
+#endif
diff --git a/src/util.cpp b/src/util.cpp
index f518491..446fb4d 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -67,6 +67,7 @@
#include "membergroup.h"
#include "dirdef.h"
#include "htmlentity.h"
+#include "symbolresolver.h"
#define ENABLE_TRACINGSUPPORT 0
@@ -429,8 +430,8 @@ QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName,
// tmd->getOuterScope()->name().data(), mContext);
if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
{
- AccessStack accessStack;
- int dist=isAccessibleFrom(accessStack,resScope,0,tmd);
+ SymbolResolver resolver;
+ int dist=resolver.isAccessibleFrom(resScope,tmd);
if (dist!=-1 && (md==0 || dist<minDist))
{
md = tmd;
@@ -506,1011 +507,12 @@ NamespaceDef *getResolvedNamespace(const char *name)
}
}
-static QDict<MemberDef> g_resolvedTypedefs;
-
-// forward declaration
-static const ClassDef *getResolvedClassRec(const Definition *scope,
- const FileDef *fileScope,
- const char *n,
- const MemberDef **pTypeDef,
- QCString *pTemplSpec,
- QCString *pResolvedType
- );
-
-/*! Returns the class representing the value of the typedef represented by \a md
- * within file \a fileScope.
- *
- * Example: typedef A T; will return the class representing A if it is a class.
- *
- * Example: typedef int T; will return 0, since "int" is not a class.
- */
-const ClassDef *newResolveTypedef(const FileDef *fileScope,
- const MemberDef *md,
- const MemberDef **pMemType,
- QCString *pTemplSpec,
- QCString *pResolvedType,
- const std::unique_ptr<ArgumentList> &actTemplParams)
-{
- //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
- bool isCached = md->isTypedefValCached(); // value already cached
- if (isCached)
- {
- //printf("Already cached %s->%s [%s]\n",
- // md->name().data(),
- // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
- // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
-
- if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
- if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
- return md->getCachedTypedefVal();
- }
- //printf("new typedef\n");
- QCString qname = md->qualifiedName();
- if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
-
- g_resolvedTypedefs.insert(qname,md); // put on the trace list
-
- const ClassDef *typeClass = md->getClassDef();
- QCString type = md->typeString(); // get the "value" of the typedef
- if (typeClass && typeClass->isTemplate() &&
- actTemplParams && !actTemplParams->empty())
- {
- type = substituteTemplateArgumentsInString(type,
- typeClass->templateArguments(),actTemplParams);
- }
- QCString typedefValue = type;
- int tl=type.length();
- int ip=tl-1; // remove * and & at the end
- while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
- {
- ip--;
- }
- type=type.left(ip+1);
- type.stripPrefix("const "); // strip leading "const"
- type.stripPrefix("struct "); // strip leading "struct"
- type.stripPrefix("union "); // strip leading "union"
- int sp=0;
- tl=type.length(); // length may have been changed
- while (sp<tl && type.at(sp)==' ') sp++;
- const MemberDef *memTypeDef = 0;
- const ClassDef *result = getResolvedClassRec(md->getOuterScope(),
- fileScope,type,&memTypeDef,0,pResolvedType);
- // if type is a typedef then return what it resolves to.
- if (memTypeDef && memTypeDef->isTypedef())
- {
- result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
- goto done;
- }
- else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
- {
- *pMemType = memTypeDef;
- }
-
- //printf("type=%s result=%p\n",type.data(),result);
- if (result==0)
- {
- // try unspecialized version if type is template
- int si=type.findRev("::");
- int i=type.find('<');
- if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
- {
- if (pTemplSpec) *pTemplSpec = type.mid(i);
- result = getResolvedClassRec(md->getOuterScope(),fileScope,
- type.left(i),0,0,pResolvedType);
- //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
- // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
- }
- else if (si!=-1) // A::B
- {
- i=type.find('<',si);
- if (i==-1) // Something like A<T>::B => lookup A::B
- {
- i=type.length();
- }
- else // Something like A<T>::B<S> => lookup A::B, spec=<S>
- {
- if (pTemplSpec) *pTemplSpec = type.mid(i);
- }
- result = getResolvedClassRec(md->getOuterScope(),fileScope,
- stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
- pResolvedType);
- }
-
- //if (result) ip=si+sp+1;
- }
-
-done:
- if (pResolvedType)
- {
- if (result)
- {
- *pResolvedType=result->qualifiedName();
- //printf("*pResolvedType=%s\n",pResolvedType->data());
- if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
- if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
- }
- else
- {
- *pResolvedType=typedefValue;
- }
- }
-
- // remember computed value for next time
- if (result && result->getDefFileName()!="<code>")
- // this check is needed to prevent that temporary classes that are
- // introduced while parsing code fragments are being cached here.
- {
- //printf("setting cached typedef %p in result %p\n",md,result);
- //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
- //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
- const_cast<MemberDef*>(md)->cacheTypedefVal(result,
- pTemplSpec ? *pTemplSpec : QCString(),
- pResolvedType ? *pResolvedType : QCString()
- );
- }
-
- g_resolvedTypedefs.remove(qname); // remove from the trace list
-
- return result;
-}
-
-/*! Substitutes a simple unqualified \a name within \a scope. Returns the
- * value of the typedef or \a name if no typedef was found.
- */
-static QCString substTypedef(const Definition *scope,const FileDef *fileScope,const QCString &name,
- const MemberDef **pTypeDef=0)
-{
- QCString result=name;
- if (name.isEmpty()) return result;
-
- auto range = Doxygen::symbolMap.find(name);
- if (range.first==range.second)
- return result; // no matches
-
- MemberDef *bestMatch=0;
- int minDistance=10000; // init at "infinite"
-
- for (auto it = range.first; it!=range.second; ++it)
- {
- Definition *d = it->second;
- // only look at members
- if (d->definitionType()==Definition::TypeMember)
- {
- // that are also typedefs
- MemberDef *md = dynamic_cast<MemberDef *>(d);
- if (md->isTypedef()) // d is a typedef
- {
- VisitedNamespaces visitedNamespaces;
- AccessStack accessStack;
- // test accessibility of typedef within scope.
- int distance = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,fileScope,d,"");
- if (distance!=-1 && distance<minDistance)
- // definition is accessible and a better match
- {
- minDistance=distance;
- bestMatch = md;
- }
- }
- }
- }
-
- if (bestMatch)
- {
- result = bestMatch->typeString();
- if (pTypeDef) *pTypeDef=bestMatch;
- }
-
- //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
- // name.data(),result.data());
- return result;
-}
-
-static const Definition *endOfPathIsUsedClass(const SDict<Definition> *cl,const QCString &localName)
-{
- if (cl)
- {
- SDict<Definition>::Iterator cli(*cl);
- Definition *cd;
- for (cli.toFirst();(cd=cli.current());++cli)
- {
- if (cd->localName()==localName)
- {
- return cd;
- }
- }
- }
- return 0;
-}
-
-/*! Starting with scope \a start, the string \a path is interpreted as
- * a part of a qualified scope name (e.g. A::B::C), and the scope is
- * searched. If found the scope definition is returned, otherwise 0
- * is returned.
- */
-static const Definition *followPath(const Definition *start,const FileDef *fileScope,const QCString &path)
-{
- int is,ps;
- int l;
- const Definition *current=start;
- ps=0;
- //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
- // for each part of the explicit scope
- while ((is=getScopeFragment(path,ps,&l))!=-1)
- {
- // try to resolve the part if it is a typedef
- const MemberDef *typeDef=0;
- QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
- //printf(" qualScopePart=%s\n",qualScopePart.data());
- if (typeDef)
- {
- const ClassDef *type = newResolveTypedef(fileScope,typeDef);
- if (type)
- {
- //printf("Found type %s\n",type->name().data());
- return type;
- }
- }
- const Definition *next = current->findInnerCompound(qualScopePart);
- //printf("++ Looking for %s inside %s result %s\n",
- // qualScopePart.data(),
- // current->name().data(),
- // next?next->name().data():"<null>");
- if (next==0) // failed to follow the path
- {
- //printf("==> next==0!\n");
- if (current->definitionType()==Definition::TypeNamespace)
- {
- next = endOfPathIsUsedClass(
- (dynamic_cast<const NamespaceDef *>(current))->getUsedClasses(),qualScopePart);
- }
- else if (current->definitionType()==Definition::TypeFile)
- {
- next = endOfPathIsUsedClass(
- (dynamic_cast<const FileDef *>(current))->getUsedClasses(),qualScopePart);
- }
- current = next;
- if (current==0) break;
- }
- else // continue to follow scope
- {
- current = next;
- //printf("==> current = %p\n",current);
- }
- ps=is+l;
- }
- //printf("followPath(start=%s,path=%s) result=%s\n",
- // start->name().data(),path.data(),current?current->name().data():"<null>");
- return current; // path could be followed
-}
-
-bool accessibleViaUsingClass(const SDict<Definition> *cl,
- const FileDef *fileScope,
- const Definition *item,
- const QCString &explicitScopePart=""
- )
-{
- //printf("accessibleViaUsingClass(%p)\n",cl);
- if (cl) // see if the class was imported via a using statement
- {
- SDict<Definition>::Iterator cli(*cl);
- Definition *ucd;
- bool explicitScopePartEmpty = explicitScopePart.isEmpty();
- for (cli.toFirst();(ucd=cli.current());++cli)
- {
- //printf("Trying via used class %s\n",ucd->name().data());
- const Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
- if (sc && sc==item) return TRUE;
- //printf("Try via used class done\n");
- }
- }
- return FALSE;
-}
-
-bool accessibleViaUsingNamespace(StringUnorderedSet &visited,
- const NamespaceSDict *nl,
- const FileDef *fileScope,
- const Definition *item,
- const QCString &explicitScopePart="")
-{
- if (nl) // check used namespaces for the class
- {
- NamespaceSDict::Iterator nli(*nl);
- NamespaceDef *und;
- int count=0;
- for (nli.toFirst();(und=nli.current());++nli,count++)
- {
- //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
- // count,nl->count());
- const Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
- if (sc && item->getOuterScope()==sc)
- {
- //printf("] found it\n");
- return TRUE;
- }
- if (item->getLanguage()==SrcLangExt_Cpp)
- {
- QCString key=und->name();
- if (und->getUsedNamespaces() && visited.find(key.str())==visited.end())
- {
- visited.insert(key.str());
-
- if (accessibleViaUsingNamespace(visited,und->getUsedNamespaces(),fileScope,item,explicitScopePart))
- {
- //printf("] found it via recursion\n");
- return TRUE;
- }
-
- visited.erase(key.str());
- }
- }
- //printf("] Try via used namespace done\n");
- }
- }
- return FALSE;
-}
-
-
-/* Returns the "distance" (=number of levels up) from item to scope, or -1
- * if item in not inside scope.
- */
-int isAccessibleFrom(AccessStack &accessStack,const Definition *scope,const FileDef *fileScope,const Definition *item)
-{
- //printf("<isAccessibleFrom(scope=%s,item=%s itemScope=%s)\n",
- // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
-
- if (accessStack.find(scope,fileScope,item))
- {
- return -1;
- }
- accessStack.push(scope,fileScope,item);
-
- int result=0; // assume we found it
- int i;
-
- Definition *itemScope=item->getOuterScope();
- bool memberAccessibleFromScope =
- (item->definitionType()==Definition::TypeMember && // a member
- itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
- scope->definitionType()==Definition::TypeClass && // accessible
- (dynamic_cast<const ClassDef*>(scope))->isAccessibleMember(dynamic_cast<const MemberDef *>(item)) // from scope
- );
- bool nestedClassInsideBaseClass =
- (item->definitionType()==Definition::TypeClass && // a nested class
- itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
- scope->definitionType()==Definition::TypeClass && // class of scope
- (dynamic_cast<const ClassDef*>(scope))->isBaseClass(dynamic_cast<ClassDef*>(itemScope),TRUE)
- );
-
- if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass)
- {
- //printf("> found it\n");
- if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent
- // this is preferred over nested class in this class
- // see bug 686956
- }
- else if (scope==Doxygen::globalScope)
- {
- if (fileScope)
- {
- SDict<Definition> *cl = fileScope->getUsedClasses();
- if (accessibleViaUsingClass(cl,fileScope,item))
- {
- //printf("> found via used class\n");
- goto done;
- }
- NamespaceSDict *nl = fileScope->getUsedNamespaces();
- StringUnorderedSet visited;
- if (accessibleViaUsingNamespace(visited,nl,fileScope,item))
- {
- //printf("> found via used namespace\n");
- goto done;
- }
- }
- //printf("> reached global scope\n");
- result=-1; // not found in path to globalScope
- }
- else // keep searching
- {
- // check if scope is a namespace, which is using other classes and namespaces
- if (scope->definitionType()==Definition::TypeNamespace)
- {
- const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope);
- //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
- const SDict<Definition> *cl = nscope->getUsedClasses();
- if (accessibleViaUsingClass(cl,fileScope,item))
- {
- //printf("> found via used class\n");
- goto done;
- }
- const NamespaceSDict *nl = nscope->getUsedNamespaces();
- StringUnorderedSet visited;
- if (accessibleViaUsingNamespace(visited,nl,fileScope,item))
- {
- //printf("> found via used namespace\n");
- goto done;
- }
- }
- // repeat for the parent scope
- i=isAccessibleFrom(accessStack,scope->getOuterScope(),fileScope,item);
- //printf("> result=%d\n",i);
- result= (i==-1) ? -1 : i+2;
- }
-done:
- accessStack.pop();
- return result;
-}
-
-
-/* Returns the "distance" (=number of levels up) from item to scope, or -1
- * if item in not in this scope. The explicitScopePart limits the search
- * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
- * Example:
- *
- * class A { public: class I {}; };
- * class B { public: class J {}; };
- *
- * - Looking for item=='J' inside scope=='B' will return 0.
- * - Looking for item=='I' inside scope=='B' will return -1
- * (as it is not found in B nor in the global scope).
- * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
- * not found and then A::I is searched in the global scope, which matches and
- * thus the result is 1.
- */
-int isAccessibleFromWithExpScope(VisitedNamespaces &visitedNamespaces,
- AccessStack &accessStack, const Definition *scope,const FileDef *fileScope,
- const Definition *item,const QCString &explicitScopePart)
-{
- if (explicitScopePart.isEmpty())
- {
- // handle degenerate case where there is no explicit scope.
- return isAccessibleFrom(accessStack,scope,fileScope,item);
- }
-
- if (accessStack.find(scope,fileScope,item,explicitScopePart))
- {
- return -1;
- }
- accessStack.push(scope,fileScope,item,explicitScopePart);
-
-
- //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
- // item?item->name().data():"<none>",
- // explicitScopePart.data());
- int result=0; // assume we found it
- const Definition *newScope = followPath(scope,fileScope,explicitScopePart);
- if (newScope) // explicitScope is inside scope => newScope is the result
- {
- Definition *itemScope = item->getOuterScope();
- //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
- //if (newScope && newScope->definitionType()==Definition::TypeClass)
- //{
- // ClassDef *cd = (ClassDef *)newScope;
- // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
- //}
- if (itemScope==newScope) // exact match of scopes => distance==0
- {
- //printf("> found it\n");
- }
- else if (itemScope && newScope &&
- itemScope->definitionType()==Definition::TypeClass &&
- newScope->definitionType()==Definition::TypeClass &&
- (dynamic_cast<const ClassDef*>(newScope))->isBaseClass(dynamic_cast<const ClassDef*>(itemScope),TRUE,0)
- )
- {
- // inheritance is also ok. Example: looking for B::I, where
- // class A { public: class I {} };
- // class B : public A {}
- // but looking for B::I, where
- // class A { public: class I {} };
- // class B { public: class I {} };
- // will find A::I, so we still prefer a direct match and give this one a distance of 1
- result=1;
-
- //printf("scope(%s) is base class of newScope(%s)\n",
- // scope->name().data(),newScope->name().data());
- }
- else
- {
- int i=-1;
- if (newScope->definitionType()==Definition::TypeNamespace)
- {
- visitedNamespaces.insert({newScope->name().str(),newScope});
- // this part deals with the case where item is a class
- // A::B::C but is explicit referenced as A::C, where B is imported
- // in A via a using directive.
- //printf("newScope is a namespace: %s!\n",newScope->name().data());
- const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(newScope);
- const SDict<Definition> *cl = nscope->getUsedClasses();
- if (cl)
- {
- SDict<Definition>::Iterator cli(*cl);
- const Definition *cd;
- for (cli.toFirst();(cd=cli.current());++cli)
- {
- //printf("Trying for class %s\n",cd->name().data());
- if (cd==item)
- {
- //printf("> class is used in this scope\n");
- goto done;
- }
- }
- }
- const NamespaceSDict *nl = nscope->getUsedNamespaces();
- if (nl)
- {
- NamespaceSDict::Iterator nli(*nl);
- const NamespaceDef *nd;
- for (nli.toFirst();(nd=nli.current());++nli)
- {
- if (visitedNamespaces.find(nd->name().str())==visitedNamespaces.end())
- {
- //printf("Trying for namespace %s\n",nd->name().data());
- i = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,fileScope,item,nd->name());
- if (i!=-1)
- {
- //printf("> found via explicit scope of used namespace\n");
- goto done;
- }
- }
- }
- }
- }
- // repeat for the parent scope
- if (scope!=Doxygen::globalScope)
- {
- i = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope->getOuterScope(),fileScope,
- item,explicitScopePart);
- }
- //printf(" | result=%d\n",i);
- result = (i==-1) ? -1 : i+2;
- }
- }
- else // failed to resolve explicitScope
- {
- //printf(" failed to resolve: scope=%s\n",scope->name().data());
- if (scope->definitionType()==Definition::TypeNamespace)
- {
- const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope);
- const NamespaceSDict *nl = nscope->getUsedNamespaces();
- StringUnorderedSet visited;
- if (accessibleViaUsingNamespace(visited,nl,fileScope,item,explicitScopePart))
- {
- //printf("> found in used namespace\n");
- goto done;
- }
- }
- if (scope==Doxygen::globalScope)
- {
- if (fileScope)
- {
- const NamespaceSDict *nl = fileScope->getUsedNamespaces();
- StringUnorderedSet visited;
- if (accessibleViaUsingNamespace(visited,nl,fileScope,item,explicitScopePart))
- {
- //printf("> found in used namespace\n");
- goto done;
- }
- }
- //printf("> not found\n");
- result=-1;
- }
- else // continue by looking into the parent scope
- {
- int i=isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope->getOuterScope(),fileScope,
- item,explicitScopePart);
- //printf("> result=%d\n",i);
- result= (i==-1) ? -1 : i+2;
- }
- }
-
-done:
- //printf(" > result=%d\n",result);
- accessStack.pop();
- return result;
-}
-
int computeQualifiedIndex(const QCString &name)
{
int i = name.find('<');
return name.findRev("::",i==-1 ? name.length() : i);
}
-static void getResolvedSymbol(const Definition *scope,
- const FileDef *fileScope,
- const Definition *d,
- const QCString &explicitScopePart,
- const std::unique_ptr<ArgumentList> &actTemplParams,
- int &minDistance,
- const ClassDef *&bestMatch,
- const MemberDef *&bestTypedef,
- QCString &bestTemplSpec,
- QCString &bestResolvedType
- )
-{
- //printf(" => found type %x name=%s d=%p\n",
- // d->definitionType(),d->name().data(),d);
-
- // only look at classes and members that are enums or typedefs
- if (d->definitionType()==Definition::TypeClass ||
- (d->definitionType()==Definition::TypeMember &&
- ((dynamic_cast<const MemberDef*>(d))->isTypedef() || (dynamic_cast<const MemberDef*>(d))->isEnumerate())
- )
- )
- {
- VisitedNamespaces visitedNamespaces;
- AccessStack accessStack;
- // test accessibility of definition within scope.
- int distance = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,fileScope,d,explicitScopePart);
- //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
- if (distance!=-1) // definition is accessible
- {
- // see if we are dealing with a class or a typedef
- if (d->definitionType()==Definition::TypeClass) // d is a class
- {
- const ClassDef *cd = dynamic_cast<const ClassDef *>(d);
- //printf("cd=%s\n",cd->name().data());
- if (!cd->isTemplateArgument()) // skip classes that
- // are only there to
- // represent a template
- // argument
- {
- //printf("is not a templ arg\n");
- if (distance<minDistance) // found a definition that is "closer"
- {
- minDistance=distance;
- bestMatch = cd;
- bestTypedef = 0;
- bestTemplSpec.resize(0);
- bestResolvedType = cd->qualifiedName();
- }
- else if (distance==minDistance &&
- fileScope && bestMatch &&
- fileScope->getUsedNamespaces() &&
- d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
- bestMatch->getOuterScope()==Doxygen::globalScope
- )
- {
- // in case the distance is equal it could be that a class X
- // is defined in a namespace and in the global scope. When searched
- // in the global scope the distance is 0 in both cases. We have
- // to choose one of the definitions: we choose the one in the
- // namespace if the fileScope imports namespaces and the definition
- // found was in a namespace while the best match so far isn't.
- // Just a non-perfect heuristic but it could help in some situations
- // (kdecore code is an example).
- minDistance=distance;
- bestMatch = cd;
- bestTypedef = 0;
- bestTemplSpec.resize(0);
- bestResolvedType = cd->qualifiedName();
- }
- }
- else
- {
- //printf(" is a template argument!\n");
- }
- }
- else if (d->definitionType()==Definition::TypeMember)
- {
- const MemberDef *md = dynamic_cast<const MemberDef *>(d);
- //printf(" member isTypedef()=%d\n",md->isTypedef());
- if (md->isTypedef()) // d is a typedef
- {
- QCString args=md->argsString();
- if (args.isEmpty()) // do not expand "typedef t a[4];"
- {
- //printf(" found typedef!\n");
-
- // we found a symbol at this distance, but if it didn't
- // resolve to a class, we still have to make sure that
- // something at a greater distance does not match, since
- // that symbol is hidden by this one.
- if (distance<minDistance)
- {
- QCString spec;
- QCString type;
- minDistance=distance;
- const MemberDef *enumType = 0;
- const ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
- if (cd) // type resolves to a class
- {
- //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
- bestMatch = cd;
- bestTypedef = md;
- bestTemplSpec = spec;
- bestResolvedType = type;
- }
- else if (enumType) // type resolves to a enum
- {
- //printf(" is enum\n");
- bestMatch = 0;
- bestTypedef = enumType;
- bestTemplSpec = "";
- bestResolvedType = enumType->qualifiedName();
- }
- else if (md->isReference()) // external reference
- {
- bestMatch = 0;
- bestTypedef = md;
- bestTemplSpec = spec;
- bestResolvedType = type;
- }
- else
- {
- bestMatch = 0;
- bestTypedef = md;
- bestTemplSpec.resize(0);
- bestResolvedType.resize(0);
- //printf(" no match\n");
- }
- }
- else
- {
- //printf(" not the best match %d min=%d\n",distance,minDistance);
- }
- }
- else
- {
- //printf(" not a simple typedef\n")
- }
- }
- else if (md->isEnumerate())
- {
- if (distance<minDistance)
- {
- minDistance=distance;
- bestMatch = 0;
- bestTypedef = md;
- bestTemplSpec = "";
- bestResolvedType = md->qualifiedName();
- }
- }
- }
- } // if definition accessible
- else
- {
- //printf(" Not accessible!\n");
- }
- } // if definition is a class or member
- //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
-}
-
-static std::mutex g_cacheMutex;
-
-/* Find the fully qualified class name referred to by the input class
- * or typedef name against the input scope.
- * Loops through scope and each of its parent scopes looking for a
- * match against the input name. Can recursively call itself when
- * resolving typedefs.
- */
-static const ClassDef *getResolvedClassRec(const Definition *scope,
- const FileDef *fileScope,
- const char *n,
- const MemberDef **pTypeDef,
- QCString *pTemplSpec,
- QCString *pResolvedType
- )
-{
- if (n==0 || *n=='\0') return 0;
- //static int level=0;
- //printf("%d [getResolvedClassRec(%s,%s)\n",level++,scope?scope->name().data():"<global>",n);
- QCString name;
- QCString explicitScopePart;
- QCString strippedTemplateParams;
- name=stripTemplateSpecifiersFromScope
- (removeRedundantWhiteSpace(n),TRUE,
- &strippedTemplateParams);
- std::unique_ptr<ArgumentList> actTemplParams;
- if (!strippedTemplateParams.isEmpty()) // template part that was stripped
- {
- actTemplParams = stringToArgumentList(scope->getLanguage(),strippedTemplateParams);
- }
-
- int qualifierIndex = computeQualifiedIndex(name);
- //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
- if (qualifierIndex!=-1) // qualified name
- {
- // split off the explicit scope part
- explicitScopePart=name.left(qualifierIndex);
- // todo: improve namespace alias substitution
- replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
- name=name.mid(qualifierIndex+2);
- }
-
- if (name.isEmpty())
- {
- //printf("%d ] empty name\n",--level);
- return 0; // empty name
- }
-
- //printf("Looking for symbol %s\n",name.data());
- auto range = Doxygen::symbolMap.find(name);
- // the -g (for C# generics) and -p (for ObjC protocols) are now already
- // stripped from the key used in the symbolMap, so that is not needed here.
- if (range.first==range.second)
- {
- range = Doxygen::symbolMap.find(name+"-p");
- if (range.first==range.second)
- {
- //printf("%d ] no such symbol!\n",--level);
- return 0;
- }
- }
- //printf("found symbol!\n");
-
- bool hasUsingStatements =
- (fileScope && ((fileScope->getUsedNamespaces() &&
- fileScope->getUsedNamespaces()->count()>0) ||
- (fileScope->getUsedClasses() &&
- fileScope->getUsedClasses()->count()>0))
- );
- //printf("hasUsingStatements=%d\n",hasUsingStatements);
- // Since it is often the case that the same name is searched in the same
- // scope over an over again (especially for the linked source code generation)
- // we use a cache to collect previous results. This is possible since the
- // result of a lookup is deterministic. As the key we use the concatenated
- // scope, the name to search for and the explicit scope prefix. The speedup
- // achieved by this simple cache can be enormous.
- int scopeNameLen = scope->name().length()+1;
- int nameLen = name.length()+1;
- int explicitPartLen = explicitScopePart.length();
- int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
-
- // below is a more efficient coding of
- // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
- QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
- char *p=key.rawData();
- qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
- p+=scopeNameLen;
- qstrcpy(p,name); *(p+nameLen-1)='+';
- p+=nameLen;
- qstrcpy(p,explicitScopePart);
- p+=explicitPartLen;
-
- // if a file scope is given and it contains using statements we should
- // also use the file part in the key (as a class name can be in
- // two different namespaces and a using statement in a file can select
- // one of them).
- if (hasUsingStatements)
- {
- // below is a more efficient coding of
- // key+="+"+fileScope->name();
- *p++='+';
- qstrcpy(p,fileScope->absFilePath());
- p+=fileScopeLen-1;
- }
- *p='\0';
-
- LookupInfo *pval = 0;
- {
- std::lock_guard<std::mutex> lock(g_cacheMutex);
- pval=Doxygen::lookupCache->find(key.str());
- //printf("Searching for %s result=%p\n",key.data(),pval);
- if (pval)
- {
- //printf("LookupInfo %p %p '%s' %p\n",
- // pval->classDef, pval->typeDef, pval->templSpec.data(),
- // pval->resolvedType.data());
- if (pTemplSpec) *pTemplSpec=pval->templSpec;
- if (pTypeDef) *pTypeDef=pval->typeDef;
- if (pResolvedType) *pResolvedType=pval->resolvedType;
- //printf("%d ] cachedMatch=%s\n",--level,
- // pval->classDef?pval->classDef->name().data():"<none>");
- //if (pTemplSpec)
- // printf("templSpec=%s\n",pTemplSpec->data());
- return pval->classDef;
- }
- else // not found yet; we already add a 0 to avoid the possibility of
- // endless recursion.
- {
- pval = Doxygen::lookupCache->insert(key.str(),LookupInfo());
- }
- }
-
- const ClassDef *bestMatch=0;
- const MemberDef *bestTypedef=0;
- QCString bestTemplSpec;
- QCString bestResolvedType;
- int minDistance=10000; // init at "infinite"
-
- for (auto it=range.first ; it!=range.second; ++it)
- {
- Definition *d = it->second;
- getResolvedSymbol(scope,fileScope,d,explicitScopePart,actTemplParams,
- minDistance,bestMatch,bestTypedef,bestTemplSpec,
- bestResolvedType);
- }
-
- if (pTypeDef)
- {
- *pTypeDef = bestTypedef;
- }
- if (pTemplSpec)
- {
- *pTemplSpec = bestTemplSpec;
- }
- if (pResolvedType)
- {
- *pResolvedType = bestResolvedType;
- }
- //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
- // bestMatch,bestResolvedType.data());
-
- if (pval)
- {
- std::lock_guard<std::mutex> lock(g_cacheMutex);
- pval->classDef = bestMatch;
- pval->typeDef = bestTypedef;
- pval->templSpec = bestTemplSpec;
- pval->resolvedType = bestResolvedType;
- }
- //printf("%d ] bestMatch=%s distance=%d\n",--level,
- // bestMatch?bestMatch->name().data():"<none>",minDistance);
- //if (pTemplSpec)
- // printf("templSpec=%s\n",pTemplSpec->data());
- return bestMatch;
-}
-
-/* Find the fully qualified class name referred to by the input class
- * or typedef name against the input scope.
- * Loops through scope and each of its parent scopes looking for a
- * match against the input name.
- */
-const ClassDef *getResolvedClass(const Definition *scope,
- const FileDef *fileScope,
- const char *n,
- const MemberDef **pTypeDef,
- QCString *pTemplSpec,
- bool mayBeUnlinkable,
- bool mayBeHidden,
- QCString *pResolvedType
- )
-{
- static bool optimizeOutputVhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
- g_resolvedTypedefs.clear();
- if (scope==0 ||
- (scope->definitionType()!=Definition::TypeClass &&
- scope->definitionType()!=Definition::TypeNamespace
- ) ||
- (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1)
- )
- {
- scope=Doxygen::globalScope;
- }
- //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
- // scope?scope->name().data():"<global>",
- // fileScope?fileScope->name().data():"<none>",
- // n,
- // mayBeUnlinkable
- // );
- const ClassDef *result;
- if (optimizeOutputVhdl)
- {
- result = getClass(n);
- }
- else
- {
- result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
- }
- if (result==0) // for nested classes imported via tag files, the scope may not
- // present, so we check the class name directly as well.
- // See also bug701314
- {
- result = getClass(n);
- }
- if (!mayBeUnlinkable && result && !result->isLinkable())
- {
- if (!mayBeHidden || !result->isHidden())
- {
- //printf("result was %s\n",result?result->name().data():"<none>");
- result=0; // don't link to artificial/hidden classes unless explicitly allowed
- }
- }
- //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
- // n,result?result->name().data():"<none>");
- return result;
-}
-
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
@@ -1957,8 +959,9 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope,
const GroupDef *gd=0;
//printf("** Match word '%s'\n",matchWord.data());
- const MemberDef *typeDef=0;
- cd=getResolvedClass(scope,fileScope,matchWord,&typeDef);
+ SymbolResolver resolver(fileScope);
+ cd=resolver.resolveClass(scope,matchWord);
+ const MemberDef *typeDef = resolver.getTypedef();
if (typeDef) // First look at typedef then class, see bug 584184.
{
//printf("Found typedef %s\n",typeDef->name().data());
@@ -2787,18 +1790,21 @@ static QCString getCanonicalTypeForIdentifier(
// d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>",
// tSpec?tSpec->data():"<none>",templSpec.data());
- const ClassDef *cd = 0;
- const MemberDef *mType = 0;
- QCString ts;
- QCString resolvedType;
-
// lookup class / class template instance
- cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
+ SymbolResolver resolver(fs);
+ const ClassDef *cd = resolver.resolveClass(d,word+templSpec,true,true);
+ const MemberDef *mType = resolver.getTypedef();
+ QCString ts = resolver.getTemplateSpec();
+ QCString resolvedType = resolver.getResolvedType();
+
bool isTemplInst = cd && !templSpec.isEmpty();
if (!cd && !templSpec.isEmpty())
{
// class template specialization not known, look up class template
- cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
+ cd = resolver.resolveClass(d,word,true,true);
+ mType = resolver.getTypedef();
+ ts = resolver.getTemplateSpec();
+ resolvedType = resolver.getResolvedType();
}
if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
@@ -3426,12 +2432,15 @@ bool getDefs(const QCString &scName,
className=mScope;
}
- const MemberDef *tmd=0;
- const ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className,&tmd);
+ SymbolResolver resolver;
+ const ClassDef *fcd=resolver.resolveClass(Doxygen::globalScope,className);
+ const MemberDef *tmd=resolver.getTypedef();
+
if (fcd==0 && className.find('<')!=-1) // try without template specifiers as well
{
QCString nameWithoutTemplates = stripTemplateSpecifiersFromScope(className,FALSE);
- fcd=getResolvedClass(Doxygen::globalScope,0,nameWithoutTemplates,&tmd);
+ fcd=resolver.resolveClass(Doxygen::globalScope,nameWithoutTemplates);
+ tmd=resolver.getTypedef();
}
//printf("Trying class scope %s: fcd=%p tmd=%p\n",className.data(),fcd,tmd);
// todo: fill in correct fileScope!
@@ -5603,7 +4612,8 @@ QCString normalizeNonTemplateArgumentsInString(
if (!found)
{
// try to resolve the type
- const ClassDef *cd = getResolvedClass(context,0,n);
+ SymbolResolver resolver;
+ const ClassDef *cd = resolver.resolveClass(context,n);
if (cd)
{
result+=cd->name();
@@ -6700,7 +5710,7 @@ QCString getFileNameExtension(QCString fn)
//--------------------------------------------------------------------------
-MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
+static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
const char *n)
{
if (scope==0 ||
@@ -6739,9 +5749,8 @@ MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
Definition *d = it->second;
if (d->definitionType()==Definition::TypeMember)
{
- VisitedNamespaces visitedNamespaces;
- AccessStack accessStack;
- int distance = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,fileScope,d,explicitScopePart);
+ SymbolResolver resolver(fileScope);
+ int distance = resolver.isAccessibleFromWithExpScope(scope,d,explicitScopePart);
if (distance!=-1 && distance<minDistance)
{
minDistance = distance;
diff --git a/src/util.h b/src/util.h
index bc4b025..2ae24be 100644
--- a/src/util.h
+++ b/src/util.h
@@ -120,56 +120,6 @@ class LetterToIndexMap : public SIntDict<T>
//--------------------------------------------------------------------
-const int MAX_STACK_SIZE = 100;
-
-/** Helper class representing the stack of items considered while resolving
- * the scope.
- */
-class AccessStack
-{
- /** Element in the stack. */
- struct AccessElem
- {
- AccessElem(const Definition *d,const FileDef *f,const Definition *i,QCString e = QCString()) : scope(d), fileScope(f), item(i), expScope(e) {}
- const Definition *scope;
- const FileDef *fileScope;
- const Definition *item;
- QCString expScope;
- };
- public:
- void push(const Definition *scope,const FileDef *fileScope,const Definition *item)
- {
- m_elements.push_back(AccessElem(scope,fileScope,item));
- }
- void push(const Definition *scope,const FileDef *fileScope,const Definition *item,const QCString &expScope)
- {
- m_elements.push_back(AccessElem(scope,fileScope,item,expScope));
- }
- void pop()
- {
- if (!m_elements.empty()) m_elements.pop_back();
- }
- bool find(const Definition *scope,const FileDef *fileScope, const Definition *item)
- {
- auto it = std::find_if(m_elements.begin(),m_elements.end(),
- [&](const AccessElem &e) { return e.scope==scope && e.fileScope==fileScope && e.item==item; });
- return it!=m_elements.end();
- }
- bool find(const Definition *scope,const FileDef *fileScope, const Definition *item,const QCString &expScope)
- {
- auto it = std::find_if(m_elements.begin(),m_elements.end(),
- [&](const AccessElem &e) { return e.scope==scope && e.fileScope==fileScope && e.item==item && e.expScope==expScope; });
- return it!=m_elements.end();
- }
-
- private:
- std::vector<AccessElem> m_elements;
-};
-
-using VisitedNamespaces = std::unordered_map<std::string,const Definition *>;
-
-//--------------------------------------------------------------------
-
QCString langToString(SrcLangExt lang);
QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope=FALSE);
@@ -256,15 +206,6 @@ QCString resolveDefines(const char *n);
ClassDef *getClass(const char *key);
-const ClassDef *getResolvedClass(const Definition *scope,
- const FileDef *fileScope,
- const char *key,
- const MemberDef **pTypeDef=0,
- QCString *pTemplSpec=0,
- bool mayBeUnlinkable=FALSE,
- bool mayBeHidden=FALSE,
- QCString *pResolvedType=0);
-
NamespaceDef *getResolvedNamespace(const char *key);
FileDef *findFileDef(const FileNameLinkedMap *fnMap,const char *n,
@@ -363,8 +304,6 @@ QCString substituteTemplateArgumentsInString(
const ArgumentList &formalArgs,
const std::unique_ptr<ArgumentList> &actualArgs);
-//QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists);
-
QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
bool parentOnly=TRUE,
QCString *lastScopeStripped=0);
@@ -430,18 +369,6 @@ QCString stripExtension(const char *fName);
void replaceNamespaceAliases(QCString &scope,int i);
-int isAccessibleFrom(AccessStack &accessStack,
- const Definition *scope,
- const FileDef *fileScope,
- const Definition *item);
-
-int isAccessibleFromWithExpScope(VisitedNamespaces &visitedNamespaces,
- AccessStack &accessStack,
- const Definition *scope,
- const FileDef *fileScope,
- const Definition *item,
- const QCString &explicitScopePart);
-
int computeQualifiedIndex(const QCString &name);
void addDirPrefix(QCString &fileName);
@@ -464,17 +391,8 @@ QCString getFileNameExtension(QCString fn);
void initDefaultExtensionMapping();
void addCodeOnlyMappings();
-MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
- const char *n);
bool checkIfTypedef(const Definition *scope,const FileDef *fileScope,const char *n);
-const ClassDef *newResolveTypedef(const FileDef *fileScope,
- const MemberDef *md,
- const MemberDef **pMemType=0,
- QCString *pTemplSpec=0,
- QCString *pResolvedType=0,
- const std::unique_ptr<ArgumentList> &actTemplParams=std::unique_ptr<ArgumentList>());
-
QCString parseCommentAsText(const Definition *scope,const MemberDef *member,const QCString &doc,const QCString &fileName,int lineNr);
QCString transcodeCharacterStringToUTF8(const QCString &input);
@@ -485,8 +403,6 @@ QCString extractAliasArgs(const QCString &args,int pos);
int countAliasArguments(const QCString argList);
-//QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList);
-
QCString resolveAliasCmd(const QCString aliasCmd);
QCString expandAlias(const QCString &aliasName,const QCString &aliasValue);