Skip to content

Commit b1bbfe8

Browse files
committed
issue #11569 Doxygen might generate invalid tag file, breaking downstream project documentation (no uniquely matching class member found for)
1 parent c099e71 commit b1bbfe8

File tree

1 file changed

+95
-46
lines changed

1 file changed

+95
-46
lines changed

src/tagreader.cpp

Lines changed: 95 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,16 @@ class TagFileParser
10021002
};
10031003
private:
10041004

1005+
struct ClassNode
1006+
{
1007+
ClassNode(const std::string &n) : name(n) {}
1008+
std::string name;
1009+
const TagClassInfo *tci = nullptr;
1010+
std::unordered_map<std::string,std::unique_ptr<ClassNode>> children;
1011+
};
1012+
1013+
void buildClassEntry(const std::shared_ptr<Entry> &root, const TagClassInfo *tci);
1014+
void buildClassTree(const std::shared_ptr<Entry> &root, const ClassNode &node);
10051015
//------------------------------------
10061016

10071017
std::vector< TagCompoundVariant > m_tagFileCompounds;
@@ -1485,70 +1495,109 @@ void TagFileParser::buildMemberList(const std::shared_ptr<Entry> &ce,const std::
14851495
}
14861496
}
14871497

1498+
void TagFileParser::buildClassEntry(const std::shared_ptr<Entry> &root, const TagClassInfo *tci)
1499+
{
1500+
std::shared_ptr<Entry> ce = std::make_shared<Entry>();
1501+
ce->section = EntryType::makeClass();
1502+
switch (tci->kind)
1503+
{
1504+
case TagClassInfo::Kind::Class: break;
1505+
case TagClassInfo::Kind::Struct: ce->spec = TypeSpecifier().setStruct(true); break;
1506+
case TagClassInfo::Kind::Union: ce->spec = TypeSpecifier().setUnion(true); break;
1507+
case TagClassInfo::Kind::Interface: ce->spec = TypeSpecifier().setInterface(true); break;
1508+
case TagClassInfo::Kind::Enum: ce->spec = TypeSpecifier().setEnum(true); break;
1509+
case TagClassInfo::Kind::Exception: ce->spec = TypeSpecifier().setException(true); break;
1510+
case TagClassInfo::Kind::Protocol: ce->spec = TypeSpecifier().setProtocol(true); break;
1511+
case TagClassInfo::Kind::Category: ce->spec = TypeSpecifier().setCategory(true); break;
1512+
case TagClassInfo::Kind::Service: ce->spec = TypeSpecifier().setService(true); break;
1513+
case TagClassInfo::Kind::Singleton: ce->spec = TypeSpecifier().setSingleton(true); break;
1514+
case TagClassInfo::Kind::None: // should never happen, means not properly initialized
1515+
assert(tci->kind != TagClassInfo::Kind::None);
1516+
break;
1517+
}
1518+
ce->name = tci->name;
1519+
if (tci->kind==TagClassInfo::Kind::Protocol)
1520+
{
1521+
ce->name+="-p";
1522+
}
1523+
addDocAnchors(ce,tci->docAnchors);
1524+
ce->tagInfoData.tagName = m_tagName;
1525+
ce->tagInfoData.anchor = tci->anchor;
1526+
ce->tagInfoData.fileName = tci->filename;
1527+
ce->startLine = tci->lineNr;
1528+
ce->fileName = m_tagName;
1529+
ce->hasTagInfo = TRUE;
1530+
ce->id = tci->clangId;
1531+
ce->lang = tci->isObjC ? SrcLangExt::ObjC : SrcLangExt::Unknown;
1532+
// transfer base class list
1533+
ce->extends = tci->bases;
1534+
if (!tci->templateArguments.empty())
1535+
{
1536+
ArgumentList al;
1537+
for (const auto &argName : tci->templateArguments)
1538+
{
1539+
Argument a;
1540+
a.type = "class";
1541+
a.name = argName.c_str();
1542+
al.push_back(a);
1543+
}
1544+
ce->tArgLists.push_back(al);
1545+
}
1546+
1547+
buildMemberList(ce,tci->members);
1548+
root->moveToSubEntryAndKeep(ce);
1549+
}
1550+
1551+
void TagFileParser::buildClassTree(const std::shared_ptr<Entry> &root,const ClassNode &node)
1552+
{
1553+
if (node.tci)
1554+
{
1555+
buildClassEntry(root,node.tci);
1556+
}
1557+
for (const auto &child : node.children)
1558+
{
1559+
buildClassTree(root,*child.second);
1560+
}
1561+
}
1562+
14881563
/*! Injects the info gathered by the XML parser into the Entry tree.
14891564
* This tree contains the information extracted from the input in a
14901565
* "unrelated" form.
14911566
*/
14921567
void TagFileParser::buildLists(const std::shared_ptr<Entry> &root)
14931568
{
1494-
// build class list
1569+
// First reorganize the entries in m_tagFileCompounds such that
1570+
// outer scope is processed before the nested class scope.
1571+
// To solve issue #11569, where a class nested in a specialization is
1572+
// processed first, which later causes the wrong class to be used
1573+
ClassNode classRoot("");
14951574
for (const auto &comp : m_tagFileCompounds)
14961575
{
14971576
const TagClassInfo *tci = comp.getClassInfo();
14981577
if (tci)
14991578
{
1500-
std::shared_ptr<Entry> ce = std::make_shared<Entry>();
1501-
ce->section = EntryType::makeClass();
1502-
switch (tci->kind)
1503-
{
1504-
case TagClassInfo::Kind::Class: break;
1505-
case TagClassInfo::Kind::Struct: ce->spec = TypeSpecifier().setStruct(true); break;
1506-
case TagClassInfo::Kind::Union: ce->spec = TypeSpecifier().setUnion(true); break;
1507-
case TagClassInfo::Kind::Interface: ce->spec = TypeSpecifier().setInterface(true); break;
1508-
case TagClassInfo::Kind::Enum: ce->spec = TypeSpecifier().setEnum(true); break;
1509-
case TagClassInfo::Kind::Exception: ce->spec = TypeSpecifier().setException(true); break;
1510-
case TagClassInfo::Kind::Protocol: ce->spec = TypeSpecifier().setProtocol(true); break;
1511-
case TagClassInfo::Kind::Category: ce->spec = TypeSpecifier().setCategory(true); break;
1512-
case TagClassInfo::Kind::Service: ce->spec = TypeSpecifier().setService(true); break;
1513-
case TagClassInfo::Kind::Singleton: ce->spec = TypeSpecifier().setSingleton(true); break;
1514-
case TagClassInfo::Kind::None: // should never happen, means not properly initialized
1515-
assert(tci->kind != TagClassInfo::Kind::None);
1516-
break;
1517-
}
1518-
ce->name = tci->name;
1519-
if (tci->kind==TagClassInfo::Kind::Protocol)
1579+
ClassNode *current = &classRoot;
1580+
auto parts = split(tci->name.str(),"::");
1581+
for (size_t i=0; i<parts.size(); ++i)
15201582
{
1521-
ce->name+="-p";
1522-
}
1523-
addDocAnchors(ce,tci->docAnchors);
1524-
ce->tagInfoData.tagName = m_tagName;
1525-
ce->tagInfoData.anchor = tci->anchor;
1526-
ce->tagInfoData.fileName = tci->filename;
1527-
ce->startLine = tci->lineNr;
1528-
ce->fileName = m_tagName;
1529-
ce->hasTagInfo = TRUE;
1530-
ce->id = tci->clangId;
1531-
ce->lang = tci->isObjC ? SrcLangExt::ObjC : SrcLangExt::Unknown;
1532-
// transfer base class list
1533-
ce->extends = tci->bases;
1534-
if (!tci->templateArguments.empty())
1535-
{
1536-
ArgumentList al;
1537-
for (const auto &argName : tci->templateArguments)
1583+
const auto &part = parts[i];
1584+
if (current->children.find(part)==current->children.end()) // new child node
1585+
{
1586+
current->children[part] = std::make_unique<ClassNode>(part);
1587+
}
1588+
current = current->children[part].get();
1589+
if (i==parts.size()-1)
15381590
{
1539-
Argument a;
1540-
a.type = "class";
1541-
a.name = argName.c_str();
1542-
al.push_back(a);
1591+
current->tci = tci;
15431592
}
1544-
ce->tArgLists.push_back(al);
15451593
}
1546-
1547-
buildMemberList(ce,tci->members);
1548-
root->moveToSubEntryAndKeep(ce);
15491594
}
15501595
}
15511596

1597+
// now process the classes following the tree structure
1598+
buildClassTree(root,classRoot);
1599+
1600+
15521601
// build file list
15531602
for (const auto &comp : m_tagFileCompounds)
15541603
{

0 commit comments

Comments
 (0)