Skip to content

Commit fc0c847

Browse files
committed
issue doxygen#8803 Template class with the same name as existing class gets in the same output file
1 parent aadb457 commit fc0c847

File tree

7 files changed

+120
-34
lines changed

7 files changed

+120
-34
lines changed

src/classdef.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,9 @@ static QCString makeQualifiedNameWithTemplateParameters(const ClassDef *cd,
7979
if (!scName.isEmpty()) scName+=scopeSeparator;
8080

8181
bool isSpecialization = cd->localName().find('<')!=-1;
82-
8382
QCString clName = cd->className();
8483
scName+=clName;
85-
if (!cd->templateArguments().empty())
84+
if (lang!=SrcLangExt::CSharp && !cd->templateArguments().empty())
8685
{
8786
if (actualParams && *actualParamIndex<actualParams->size())
8887
{
@@ -802,6 +801,7 @@ ClassDefImpl::ClassDefImpl(
802801
bool isSymbol,bool isJavaEnum)
803802
: DefinitionMixin(defFileName,defLine,defColumn,removeRedundantWhiteSpace(nm),nullptr,nullptr,isSymbol)
804803
{
804+
AUTO_TRACE("name={}",name());
805805
setReference(lref);
806806
m_compType = ct;
807807
m_isJavaEnum = isJavaEnum;
@@ -853,6 +853,7 @@ ClassDefImpl::ClassDefImpl(
853853
{
854854
m_fileName = convertNameToFile(m_fileName);
855855
}
856+
AUTO_TRACE_EXIT("m_fileName='{}'",m_fileName);
856857
}
857858

858859
std::unique_ptr<ClassDef> ClassDefImpl::deepCopy(const QCString &name) const
@@ -2946,6 +2947,7 @@ void ClassDefImpl::writeDocumentation(OutputList &ol) const
29462947
hli = HighlightedItem::ClassVisible;
29472948
}
29482949

2950+
AUTO_TRACE("name='{}' getOutputFileBase='{}'",name(),getOutputFileBase());
29492951
startFile(ol,getOutputFileBase(),name(),pageTitle,hli,!generateTreeView);
29502952
if (!generateTreeView)
29512953
{
@@ -4209,6 +4211,7 @@ QCString ClassDefImpl::getOutputFileBase() const
42094211
}
42104212
}
42114213
}
4214+
AUTO_TRACE("name='{}' m_templateMaster={}",name(),(void*)m_templateMaster);
42124215
if (m_templateMaster)
42134216
{
42144217
// point to the template of which this class is an instance
@@ -4442,14 +4445,13 @@ QCString ClassDefImpl::qualifiedNameWithTemplateParameters(
44424445

44434446
QCString ClassDefImpl::className() const
44444447
{
4445-
if (m_className.isEmpty())
4446-
{
4447-
return localName();
4448-
}
4449-
else
4448+
QCString name = m_className.isEmpty() ? localName() : m_className;
4449+
auto lang = getLanguage();
4450+
if (lang==SrcLangExt::CSharp)
44504451
{
4451-
return m_className;
4452+
name = demangleCSharpGenericName(name,tempArgListToString(templateArguments(),lang));
44524453
}
4454+
return name;
44534455
}
44544456

44554457
void ClassDefImpl::setClassName(const QCString &name)

src/docnode.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -706,10 +706,11 @@ DocRef::DocRef(DocParser *parser,DocNodeVariant *parent,const QCString &target,c
706706
DocCompoundNode(parser,parent), m_refType(Unknown), m_isSubPage(FALSE)
707707
{
708708
const Definition *compound = nullptr;
709-
QCString anchor;
709+
QCString anchor;
710710
AUTO_TRACE("target='{}',context='{}'",target,context);
711711
ASSERT(!target.isEmpty());
712712
m_relPath = parser->context.relPath;
713+
auto lang = parser->context.lang;
713714
const SectionInfo *sec = SectionManager::instance().find(parser->context.prefix+target);
714715
if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
715716
{
@@ -748,13 +749,13 @@ DocRef::DocRef(DocParser *parser,DocNodeVariant *parent,const QCString &target,c
748749
AUTO_TRACE_EXIT("section");
749750
return;
750751
}
751-
else if (resolveLink(context,target,true,&compound,anchor,parser->context.prefix))
752+
else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix))
752753
{
753754
bool isFile = compound ?
754755
(compound->definitionType()==Definition::TypeFile ||
755756
compound->definitionType()==Definition::TypePage ? TRUE : FALSE) :
756757
FALSE;
757-
m_text = linkToText(parser->context.lang,target,isFile);
758+
m_text = linkToText(lang,target,isFile);
758759
m_anchor = anchor;
759760
if (compound && compound->isLinkable()) // ref to compound
760761
{
@@ -939,7 +940,9 @@ DocLink::DocLink(DocParser *parser,DocNodeVariant *parent,const QCString &target
939940
{
940941
m_refText = m_refText.right(m_refText.length()-1);
941942
}
942-
if (resolveLink(parser->context.context,stripKnownExtensions(target),parser->context.inSeeBlock,&compound,anchor,parser->context.prefix))
943+
if (resolveLink(parser->context.context,stripKnownExtensions(target),
944+
parser->context.inSeeBlock,&compound,anchor,
945+
parser->context.lang,parser->context.prefix))
943946
{
944947
m_anchor = anchor;
945948
if (compound && compound->isLinkable())

src/docparser.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,11 +813,12 @@ void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bo
813813
ClassDef *cd=nullptr;
814814
bool ambig = false;
815815
FileDef *fd = findFileDef(Doxygen::inputNameLinkedMap,context.fileName,ambig);
816+
auto lang = context.lang;
816817
//printf("handleLinkedWord(%s) context.context=%s\n",qPrint(context.token->name),qPrint(context.context));
817818
if (!context.insideHtmlLink &&
818-
(resolveRef(context.context,context.token->name,context.inSeeBlock,&compound,&member,TRUE,fd,TRUE)
819+
(resolveRef(context.context,context.token->name,context.inSeeBlock,&compound,&member,lang,TRUE,fd,TRUE)
819820
|| (!context.context.isEmpty() && // also try with global scope
820-
resolveRef(QCString(),context.token->name,context.inSeeBlock,&compound,&member,FALSE,nullptr,TRUE))
821+
resolveRef(QCString(),context.token->name,context.inSeeBlock,&compound,&member,lang,FALSE,nullptr,TRUE))
821822
)
822823
)
823824
{

src/doxygen.cpp

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,17 @@ static void addClassToContext(const Entry *root)
10331033
// a Java/C# generic class looks like a C++ specialization, so we need to split the
10341034
// name and template arguments here
10351035
tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1036-
fullName=fullName.left(i);
1036+
if (i!=-1 && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1037+
// A -> A
1038+
// A<T> -> A-1-g
1039+
// A<T,S> -> A-2-g
1040+
{
1041+
fullName=mangleCSharpGenericName(fullName);
1042+
}
1043+
else
1044+
{
1045+
fullName=fullName.left(i);
1046+
}
10371047
}
10381048
else
10391049
{
@@ -3887,7 +3897,14 @@ static void buildFunctionList(const Entry *root)
38873897
if (!rname.isEmpty() && scope.find('@')==-1)
38883898
{
38893899
// check if this function's parent is a class
3890-
scope=stripTemplateSpecifiersFromScope(scope,FALSE);
3900+
if (root->lang==SrcLangExt::CSharp)
3901+
{
3902+
scope=mangleCSharpGenericName(scope);
3903+
}
3904+
else
3905+
{
3906+
scope=stripTemplateSpecifiersFromScope(scope,FALSE);
3907+
}
38913908

38923909
FileDef *rfd=root->fileDef();
38933910

@@ -4827,7 +4844,7 @@ static bool findClassRelation(
48274844
bool isArtificial
48284845
)
48294846
{
4830-
AUTO_TRACE("name={} base={} isArtificial={}",cd->name(),bi->name,isArtificial);
4847+
AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
48314848

48324849
QCString biName=bi->name;
48334850
bool explicitGlobalScope=FALSE;
@@ -4854,10 +4871,11 @@ static bool findClassRelation(
48544871
{
48554872
baseClassName.prepend(scopeName.left(scopeOffset)+"::");
48564873
}
4857-
//QCString stripped;
4858-
//baseClassName=stripTemplateSpecifiersFromScope
4859-
// (removeRedundantWhiteSpace(baseClassName),TRUE,
4860-
// &stripped);
4874+
if (root->lang==SrcLangExt::CSharp)
4875+
{
4876+
baseClassName = mangleCSharpGenericName(baseClassName);
4877+
}
4878+
AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
48614879
SymbolResolver resolver(cd->getFileDef());
48624880
ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
48634881
baseClassName,
@@ -4933,7 +4951,7 @@ static bool findClassRelation(
49334951

49344952
//printf("cd=%p baseClass=%p\n",cd,baseClass);
49354953
bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
4936-
//printf("1. found=%d\n",found);
4954+
AUTO_TRACE_ADD("1. found={}",found);
49374955
if (!found && si!=-1)
49384956
{
49394957
// replace any namespace aliases
@@ -4947,7 +4965,7 @@ static bool findClassRelation(
49474965
found=baseClass!=nullptr && baseClass!=cd;
49484966
if (found) templSpec = resolver.getTemplateSpec();
49494967
}
4950-
//printf("2. found=%d\n",found);
4968+
AUTO_TRACE_ADD("2. found={}",found);
49514969

49524970
if (!found)
49534971
{
@@ -4957,7 +4975,7 @@ static bool findClassRelation(
49574975
found = baseClass!=nullptr && baseClass!=cd;
49584976

49594977
}
4960-
//printf("3. found=%d\n",found);
4978+
AUTO_TRACE_ADD("3. found={}",found);
49614979
if (!found)
49624980
{
49634981
// for PHP the "use A\B as C" construct map class C to A::B, so we lookup
@@ -4971,7 +4989,7 @@ static bool findClassRelation(
49714989
}
49724990
bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
49734991

4974-
//printf("4. found=%d\n",found);
4992+
AUTO_TRACE_ADD("4. found={}",found);
49754993
if (found)
49764994
{
49774995
AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
@@ -5002,7 +5020,7 @@ static bool findClassRelation(
50025020
{
50035021
//printf(" => insert base class\n");
50045022
QCString usedName;
5005-
if (baseClassTypeDef || cd->isCSharp())
5023+
if (baseClassTypeDef)
50065024
{
50075025
usedName=biName;
50085026
//printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
@@ -5011,6 +5029,7 @@ static bool findClassRelation(
50115029
if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
50125030
if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
50135031
{
5032+
AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
50145033
cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
50155034
// add this class as super class to the base class
50165035
baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
@@ -5084,6 +5103,7 @@ static bool findClassRelation(
50845103
}
50855104
if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
50865105
{
5106+
AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
50875107
// add base class to this class
50885108
cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
50895109
// add this class as super class to the base class
@@ -5197,7 +5217,14 @@ static QCString extractClassName(const Entry *root)
51975217
{
51985218
// a Java/C# generic class looks like a C++ specialization, so we need to strip the
51995219
// template part before looking for matches
5200-
bName=bName.left(i);
5220+
if (root->lang==SrcLangExt::CSharp)
5221+
{
5222+
bName = mangleCSharpGenericName(root->name);
5223+
}
5224+
else
5225+
{
5226+
bName = bName.left(i);
5227+
}
52015228
}
52025229
return bName;
52035230
}
@@ -5226,7 +5253,7 @@ static void makeTemplateInstanceRelation(const Entry *root,ClassDefMutable *cd)
52265253
{
52275254
AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
52285255
int i = root->name.find('<');
5229-
if (i!=-1)
5256+
if (i!=-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
52305257
{
52315258
ClassDefMutable *master = getClassMutable(root->name.left(i));
52325259
if (master && master!=cd && !cd->templateMaster())

src/symbolresolver.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,12 +1602,14 @@ const ClassDef *SymbolResolver::resolveClass(const Definition *scope,
16021602
scope?scope->name():QCString(), name, mayBeUnlinkable, mayBeHidden);
16031603
p->reset();
16041604

1605+
auto lang = scope ? scope->getLanguage() : SrcLangExt::Cpp;
1606+
16051607
if (scope==nullptr ||
16061608
(scope->definitionType()!=Definition::TypeClass &&
16071609
scope->definitionType()!=Definition::TypeNamespace
16081610
) ||
16091611
(name.stripWhiteSpace().startsWith("::")) ||
1610-
(scope->getLanguage()==SrcLangExt::Java && QCString(name).find("::")!=-1)
1612+
((lang==SrcLangExt::Java || lang==SrcLangExt::CSharp) && QCString(name).find("::")!=-1)
16111613
)
16121614
{
16131615
scope=Doxygen::globalScope;
@@ -1620,12 +1622,14 @@ const ClassDef *SymbolResolver::resolveClass(const Definition *scope,
16201622
else
16211623
{
16221624
VisitedKeys visitedKeys;
1623-
result = p->getResolvedTypeRec(visitedKeys,scope,name,&p->typeDef,&p->templateSpec,&p->resolvedType);
1625+
QCString lookupName = lang==SrcLangExt::CSharp ? mangleCSharpGenericName(name) : name;
1626+
AUTO_TRACE_ADD("lookup={}",lookupName);
1627+
result = p->getResolvedTypeRec(visitedKeys,scope,lookupName,&p->typeDef,&p->templateSpec,&p->resolvedType);
16241628
if (result==nullptr) // for nested classes imported via tag files, the scope may not
16251629
// present, so we check the class name directly as well.
16261630
// See also bug701314
16271631
{
1628-
result = getClass(name);
1632+
result = getClass(lookupName);
16291633
}
16301634
}
16311635
if (!mayBeUnlinkable && result && !result->isLinkable())

src/util.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2879,6 +2879,7 @@ bool resolveRef(/* in */ const QCString &scName,
28792879
/* in */ bool inSeeBlock,
28802880
/* out */ const Definition **resContext,
28812881
/* out */ const MemberDef **resMember,
2882+
/* in */ SrcLangExt lang,
28822883
bool lookForSpecialization,
28832884
const FileDef *currentFile,
28842885
bool checkScope
@@ -2898,6 +2899,17 @@ bool resolveRef(/* in */ const QCString &scName,
28982899
fullName = removeRedundantWhiteSpace(fullName);
28992900
}
29002901

2902+
int templStartPos;
2903+
if (lang==SrcLangExt::CSharp && (templStartPos=fullName.find('<'))!=-1)
2904+
{
2905+
int templEndPos = fullName.findRev('>');
2906+
if (templEndPos!=-1)
2907+
{
2908+
fullName = mangleCSharpGenericName(fullName.left(templEndPos+1))+fullName.mid(templEndPos+1);
2909+
AUTO_TRACE_ADD("C# mangled name='{}'",fullName);
2910+
}
2911+
}
2912+
29012913
int bracePos=findParameterList(fullName);
29022914
int endNamePos=bracePos!=-1 ? bracePos : static_cast<int>(fullName.length());
29032915
int scopePos=fullName.findRev("::",endNamePos);
@@ -3078,7 +3090,7 @@ bool resolveRef(/* in */ const QCString &scName,
30783090

30793091
if (tryUnspecializedVersion)
30803092
{
3081-
bool b = resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,nullptr,checkScope);
3093+
bool b = resolveRef(scName,name,inSeeBlock,resContext,resMember,lang,FALSE,nullptr,checkScope);
30823094
AUTO_TRACE_ADD("{}",b);
30833095
return b;
30843096
}
@@ -3128,12 +3140,17 @@ bool resolveLink(/* in */ const QCString &scName,
31283140
/* in */ bool /*inSeeBlock*/,
31293141
/* out */ const Definition **resContext,
31303142
/* out */ QCString &resAnchor,
3143+
/* in */ SrcLangExt lang,
31313144
/* in */ const QCString &prefix
31323145
)
31333146
{
31343147
*resContext=nullptr;
31353148

31363149
QCString linkRef=lr;
3150+
if (lang==SrcLangExt::CSharp)
3151+
{
3152+
linkRef = mangleCSharpGenericName(linkRef);
3153+
}
31373154
QCString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,FALSE);
31383155
AUTO_TRACE("scName='{}',ref='{}'",scName,lr);
31393156
const FileDef *fd = nullptr;
@@ -3199,7 +3216,8 @@ bool resolveLink(/* in */ const QCString &scName,
31993216
AUTO_TRACE_EXIT("class");
32003217
return TRUE;
32013218
}
3202-
else if ((cd=getClass(linkRefWithoutTemplates))) // C#/Java generic class link
3219+
else if (lang==SrcLangExt::Java &&
3220+
(cd=getClass(linkRefWithoutTemplates))) // Java generic class link
32033221
{
32043222
*resContext=cd;
32053223
resAnchor=cd->anchor();
@@ -3236,7 +3254,7 @@ bool resolveLink(/* in */ const QCString &scName,
32363254
else // probably a member reference
32373255
{
32383256
const MemberDef *md = nullptr;
3239-
bool res = resolveRef(scName,lr,TRUE,resContext,&md);
3257+
bool res = resolveRef(scName,lr,TRUE,resContext,&md,lang);
32403258
if (md) resAnchor=md->anchor();
32413259
AUTO_TRACE_EXIT("member? res={}",res);
32423260
return res;
@@ -7257,3 +7275,29 @@ size_t updateColumnCount(const char *s,size_t col)
72577275
return col;
72587276
}
72597277

7278+
// in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
7279+
// A -> A
7280+
// A<T> -> A-1-g
7281+
// A<T,S> -> A-2-g
7282+
QCString mangleCSharpGenericName(const QCString &name)
7283+
{
7284+
int idx = name.find('<');
7285+
if (idx!=-1)
7286+
{
7287+
return name.left(idx)+"-"+QCString().setNum(name.contains(",")+1)+"-g";
7288+
}
7289+
return name;
7290+
}
7291+
7292+
QCString demangleCSharpGenericName(const QCString &name,const QCString &templArgs)
7293+
{
7294+
QCString result=name;
7295+
if (result.endsWith("-g"))
7296+
{
7297+
int idx = result.find('-');
7298+
result = result.left(idx)+templArgs;
7299+
}
7300+
return result;
7301+
}
7302+
7303+

0 commit comments

Comments
 (0)