summaryrefslogtreecommitdiffstats
path: root/src/qdoc/qdocindexfiles.cpp
diff options
context:
space:
mode:
authorMartin Smith <martin.smith@qt.io>2019-01-22 12:11:13 +0100
committerPaul Wicking <paul.wicking@qt.io>2019-02-06 13:19:48 +0000
commitbf8ee4c99d56c2bf770343b7db07f68a2c6a7617 (patch)
tree73095c02dbfbce7d0830f890aabaa58d2c0e9224 /src/qdoc/qdocindexfiles.cpp
parentb3c9f6cb9eb104bf4fe86b377aee7875473f97b4 (diff)
qdoc: Major clean-up of FunctionNode and parameter processingv5.13.0-alpha1
This update was motivated by the need to correct two known issues in qdoc. First, linking to overloaded functions failed in some cases because the overloads were not numbered consistently, causing links to go to the wrong overload or to nowhere at all. Second, the mechanism for handling the \relates command didn't support using it to relate a function to more than one class. For example, there are many global qHash() functions spread around QtBase. Each is meant to compute a hash index for an object of some class type, so the object can be inserted into a QHash map for that class type. It is then desired to relate qHash(type Xxx, int seed) to both QHash<type> and class Xxx, so that the documentation for that qHash() function appears as a related non-member function of both QHash<type> and class Xxx. The example above also illustrates the overload numbering problem, because all these qHash() functions are overloads of the name qHash. To make matters worse, they are not all in the same module. Most of them are in QtCore, but a few are in QtNetwork, and this distribution problem will become worse over time as more qHash() functions are added. Prior to this update, qdoc was unable to relate a function to something in a different module, or it didn't always work. While designing a fix for these issues, it became clear that the processing of the FunctionNode and the function parameters would have to be rewritten. That's what this update does. These are the main points: 1. A new subclass of Node is added to act as a proxy for a class in another module. This ProxyNode acts as a place holder for the functions (and possibly other elements) that are related to a class in another module. This is used for the qHash() functions in QtNetwork that are related to QHash in QtCore. qdoc generates an html file named qtnetwork/qhash-proxy.html that contains the documentation for these functions. But these functions are listed as related non-members on the QHash class reference page in the qtcore output directory. They are listed there in the summary, but they link to the qhash-proxy.html page in qtnetwork. 2. A new, Parameters class is added to qdoc (parameters.h and parameters.cpp), and the class Parameter is moved there from node.h. class Parameters replaces the old QVector<Parameter> wherever it was used. This encapsulates all the parameter processing and matching in the Parameters class and simplifies the code at all the places where QVector<Parameter> had been used. 3. The assignment of overload numbers is now done in the normalizeOverloads() function, which is called after all the headers and sources have been processed but before the generate phase begins. This assignment is a simple renumbering now because all the overloads of a function are linked to each other via a nextOverload_ link in the FunctionNode. The first function named qHash() is inserted into the Aggregate node's function map, but subsequent qHash() FunctionNodes are not entered into the function map but are linked to the first qHash() via its nextOverload_ link. 4. The \relates command can now be used multiple times in a single qdoc comment. There remains some work to be done here because this currently only works for global entities, but there are several cases where \relates has been used in the qdoc comment of a member of a class. This will be fixed soon, I believe. When qdoc sees the first \relates Xxx command, for example for qHash(Yyy, seed), that qHash() is a child of the global namespace. qdoc allows it to remain as a child of the global namespace but it tells class Xxx to "adopt" that child (see Node::adoptChild()). This function makes this instance of qHash() be a child of class Xxx (in this case QHash<type>), so that the parent of this qHash() becomes Xxx. After this "adoption," qHash() is a child of both the global namespace and class Xxx, but qHash() only knows it is a child of Xxx, i.e. its parent pointer is Xxx. If this is the first qHash() to become a child of Xxx, it is inserted into the function map of Xxx, but its nextOverload_ link is not changed. This is because all the global qHash() functions have already been linked into the nextOverload_ linked list, and this list must not be changed. Hence, when qdoc searches for qHash(something) to make a link to it, it will find it as a child of the global namespace, but it will correctly link to it using its actual parent pointer. When qdoc sees the second \relates Yyy for this qHash() function, qdoc sees that this FunctionNode has already been made a related non-member of Xxx, so it can't let Yyy "adopt" it. Instead, it tells Yyy to clone this qHash(), which creates a shallow copy of it but resets its nextOverload_ pointer to nullptr. I believe this clone of qHash() won't be found in a search for a function named qHash(), because the global one (the adopted one) will be found first. Or, if it is found, a link to the clone will be generated, but that's ok because the documentation is identical. Note that the existence of qHash in two child lists is taken into account at destruction time. The only place where a Node is destroyed is in the destructor of Tree, which destroys the Node tree from the root down to the leaves. Each aggregate node is responsible for deleting each of its child nodes, but it only deletes a child node if it is the parent of that child node. All of the above revealed that some of the findFunctionNode() functions were either no longer needed or weren't being called in the first place, so they were deleted. This change is now ready for testing. Change-Id: I6da3e2e9e71d39a29d90e073ed614309a49e3d4c Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Diffstat (limited to 'src/qdoc/qdocindexfiles.cpp')
-rw-r--r--src/qdoc/qdocindexfiles.cpp534
1 files changed, 265 insertions, 269 deletions
diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp
index cdba1b548..2b957a439 100644
--- a/src/qdoc/qdocindexfiles.cpp
+++ b/src/qdoc/qdocindexfiles.cpp
@@ -105,7 +105,6 @@ void QDocIndexFiles::destroyQDocIndexFiles()
*/
void QDocIndexFiles::readIndexes(const QStringList& indexFiles)
{
- relatedList_.clear();
foreach (const QString& indexFile, indexFiles) {
QString msg = "Loading index file: " + indexFile;
Location::logToStdErr(msg);
@@ -209,8 +208,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
ns->setDocumented();
}
- }
- else if (elementName == QLatin1String("class")) {
+ } else if (elementName == QLatin1String("class")) {
node = new ClassNode(parent, name);
if (attributes.hasAttribute(QLatin1String("bases"))) {
QString bases = attributes.value(QLatin1String("bases")).toString();
@@ -225,8 +223,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
abstract = true;
node->setAbstract(abstract);
- }
- else if (elementName == QLatin1String("header")) {
+ } else if (elementName == QLatin1String("header")) {
node = new HeaderNode(parent, name);
if (attributes.hasAttribute(QLatin1String("location")))
@@ -236,8 +233,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
location = Location(indexUrl + QLatin1Char('/') + name);
else if (!indexUrl.isNull())
location = Location(name);
- }
- else if (elementName == QLatin1String("qmlclass")) {
+ } else if (elementName == QLatin1String("qmlclass")) {
QmlTypeNode* qcn = new QmlTypeNode(parent, name);
qcn->setTitle(attributes.value(QLatin1String("title")).toString());
QString logicalModuleName = attributes.value(QLatin1String("qml-module-name")).toString();
@@ -258,8 +254,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(name);
node = qcn;
- }
- else if (elementName == QLatin1String("jstype")) {
+ } else if (elementName == QLatin1String("jstype")) {
QmlTypeNode* qcn = new QmlTypeNode(parent, name);
qcn->setGenus(Node::JS);
qcn->setTitle(attributes.value(QLatin1String("title")).toString());
@@ -281,8 +276,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(name);
node = qcn;
- }
- else if (elementName == QLatin1String("qmlbasictype")) {
+ } else if (elementName == QLatin1String("qmlbasictype")) {
QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
if (attributes.hasAttribute(QLatin1String("location")))
@@ -292,8 +286,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(name);
node = qbtn;
- }
- else if (elementName == QLatin1String("jsbasictype")) {
+ } else if (elementName == QLatin1String("jsbasictype")) {
QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
qbtn->setGenus(Node::JS);
qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
@@ -304,8 +297,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(name);
node = qbtn;
- }
- else if (elementName == QLatin1String("qmlpropertygroup")) {
+ } else if (elementName == QLatin1String("qmlpropertygroup")) {
QmlTypeNode* qcn = static_cast<QmlTypeNode*>(parent);
QmlPropertyGroupNode* qpgn = new QmlPropertyGroupNode(qcn, name);
if (attributes.hasAttribute(QLatin1String("location")))
@@ -315,8 +307,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(name);
node = qpgn;
- }
- else if (elementName == QLatin1String("jspropertygroup")) {
+ } else if (elementName == QLatin1String("jspropertygroup")) {
QmlTypeNode* qcn = static_cast<QmlTypeNode*>(parent);
QmlPropertyGroupNode* qpgn = new QmlPropertyGroupNode(qcn, name);
qpgn->setGenus(Node::JS);
@@ -327,8 +318,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(name);
node = qpgn;
- }
- else if (elementName == QLatin1String("qmlproperty")) {
+ } else if (elementName == QLatin1String("qmlproperty")) {
QString type = attributes.value(QLatin1String("type")).toString();
bool attached = false;
if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
@@ -337,10 +327,9 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
readonly = true;
QmlPropertyNode* qpn = new QmlPropertyNode(parent, name, type, attached);
- qpn->setReadOnly(readonly);
+ qpn->markReadOnly(readonly);
node = qpn;
- }
- else if (elementName == QLatin1String("jsproperty")) {
+ } else if (elementName == QLatin1String("jsproperty")) {
QString type = attributes.value(QLatin1String("type")).toString();
bool attached = false;
if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
@@ -350,26 +339,23 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
readonly = true;
QmlPropertyNode* qpn = new QmlPropertyNode(parent, name, type, attached);
qpn->setGenus(Node::JS);
- qpn->setReadOnly(readonly);
+ qpn->markReadOnly(readonly);
node = qpn;
- }
- else if (elementName == QLatin1String("group")) {
+ } else if (elementName == QLatin1String("group")) {
CollectionNode* cn = qdb_->addGroup(name);
cn->setTitle(attributes.value(QLatin1String("title")).toString());
cn->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
cn->markSeen();
node = cn;
- }
- else if (elementName == QLatin1String("module")) {
+ } else if (elementName == QLatin1String("module")) {
CollectionNode* cn = qdb_->addModule(name);
cn->setTitle(attributes.value(QLatin1String("title")).toString());
cn->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
cn->markSeen();
node = cn;
- }
- else if (elementName == QLatin1String("qmlmodule")) {
+ } else if (elementName == QLatin1String("qmlmodule")) {
QString t = attributes.value(QLatin1String("qml-module-name")).toString();
CollectionNode* cn = qdb_->addQmlModule(t);
QStringList info;
@@ -380,8 +366,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
cn->markSeen();
node = cn;
- }
- else if (elementName == QLatin1String("jsmodule")) {
+ } else if (elementName == QLatin1String("jsmodule")) {
QString t = attributes.value(QLatin1String("js-module-name")).toString();
CollectionNode* cn = qdb_->addJsModule(t);
QStringList info;
@@ -392,36 +377,29 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
cn->markSeen();
node = cn;
- }
- else if (elementName == QLatin1String("page")) {
+ } else if (elementName == QLatin1String("page")) {
QDocAttr subtype = QDocAttrNone;
Node::PageType ptype = Node::NoPageType;
QString attr = attributes.value(QLatin1String("subtype")).toString();
if (attr == QLatin1String("attribution")) {
subtype = QDocAttrDocument;
ptype = Node::AttributionPage;
- }
- else if (attr == QLatin1String("example")) {
+ } else if (attr == QLatin1String("example")) {
subtype = QDocAttrExample;
ptype = Node::ExamplePage;
- }
- else if (attr == QLatin1String("file")) {
+ } else if (attr == QLatin1String("file")) {
subtype = QDocAttrFile;
ptype = Node::NoPageType;
- }
- else if (attr == QLatin1String("image")) {
+ } else if (attr == QLatin1String("image")) {
subtype = QDocAttrImage;
ptype = Node::NoPageType;
- }
- else if (attr == QLatin1String("page")) {
+ } else if (attr == QLatin1String("page")) {
subtype = QDocAttrDocument;
ptype = Node::ArticlePage;
- }
- else if (attr == QLatin1String("externalpage")) {
+ } else if (attr == QLatin1String("externalpage")) {
subtype = QDocAttrExternalPage;
ptype = Node::ArticlePage;
- }
- else
+ } else
goto done;
if (current && current->isExample()) {
@@ -454,8 +432,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
node = pn;
- }
- else if (elementName == QLatin1String("enum")) {
+ } else if (elementName == QLatin1String("enum")) {
EnumNode* enumNode = new EnumNode(parent, name);
if (!indexUrl.isEmpty())
@@ -480,8 +457,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
node = enumNode;
hasReadChildren = true;
- }
- else if (elementName == QLatin1String("typedef")) {
+ } else if (elementName == QLatin1String("typedef")) {
node = new TypedefNode(parent, name);
if (!indexUrl.isEmpty())
@@ -489,8 +465,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
else if (!indexUrl.isNull())
location = Location(parent->name().toLower() + ".html");
- }
- else if (elementName == QLatin1String("property")) {
+ } else if (elementName == QLatin1String("property")) {
node = new PropertyNode(parent, name);
if (!indexUrl.isEmpty())
@@ -508,12 +483,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
attached = true;
FunctionNode* fn = new FunctionNode(metaness, parent, name, attached);
if (fn->isCppNode()) {
- fn->setReturnType(attributes.value(QLatin1String("return")).toString());
+ fn->setReturnType(attributes.value(QLatin1String("type")).toString());
fn->setVirtualness(attributes.value(QLatin1String("virtual")).toString());
fn->setConst(attributes.value(QLatin1String("const")) == QLatin1String("true"));
fn->setStatic(attributes.value(QLatin1String("static")) == QLatin1String("true"));
- fn->setIsDeleted(attributes.value(QLatin1String("delete")) == QLatin1String("true"));
- fn->setIsDefaulted(attributes.value(QLatin1String("default")) == QLatin1String("true"));
fn->setFinal(attributes.value(QLatin1String("final")) == QLatin1String("true"));
fn->setOverride(attributes.value(QLatin1String("override")) == QLatin1String("true"));
int refness = attributes.value(QLatin1String("refness")).toUInt();
@@ -521,32 +494,31 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
fn->setRef(true);
else if (refness == 2)
fn->setRefRef(true);
- if (attributes.value(QLatin1String("overload")) == QLatin1String("true")) {
- fn->setOverloadFlag(true);
+ /*
+ Theoretically, this should ensure that each function
+ node receives the same overload number and overload
+ flag it was written with, and it should be unnecessary
+ to call normalizeOverloads() for index nodes.
+ */
+ if (attributes.value(QLatin1String("overload")) == QLatin1String("true"))
fn->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
- }
- else {
- fn->setOverloadFlag(false);
+ else
fn->setOverloadNumber(0);
- }
- if (attributes.hasAttribute(QLatin1String("relates"))) {
- QString relatesName = attributes.value(QLatin1String("relates")).toString();
- if (relatesName != parent->name())
- relatedList_.append(QPair<FunctionNode*, QString>(fn, relatesName));
- }
/*
Note: The "signature" attribute was written to the
- index file, but it is not read back in. Is that ok?
+ index file, but it is not read back in. That is ok
+ because we reconstruct the parameter list and the
+ return type, from which the signature was built in
+ the first place and from which it can be rebuilt.
*/
while (reader.readNextStartElement()) {
QXmlStreamAttributes childAttributes = reader.attributes();
if (reader.name() == QLatin1String("parameter")) {
// Do not use the default value for the parameter; it is not
// required, and has been known to cause problems.
- Parameter parameter(childAttributes.value(QLatin1String("type")).toString(),
- childAttributes.value(QLatin1String("name")).toString(),
- QString()); // childAttributes.value(QLatin1String("default"))
- fn->addParameter(parameter);
+ QString type = childAttributes.value(QLatin1String("type")).toString();
+ QString name = childAttributes.value(QLatin1String("name")).toString();
+ fn->parameters().append(type, name);
} else if (reader.name() == QLatin1String("keyword")) {
insertTarget(TargetRec::Keyword, childAttributes, fn);
} else if (reader.name() == QLatin1String("target")) {
@@ -563,28 +535,30 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
location = Location(parent->name().toLower() + ".html");
hasReadChildren = true;
- }
- else if (elementName == QLatin1String("variable")) {
+ } else if (elementName == QLatin1String("variable")) {
node = new VariableNode(parent, name);
if (!indexUrl.isEmpty())
location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
else if (!indexUrl.isNull())
location = Location(parent->name().toLower() + ".html");
- }
- else if (elementName == QLatin1String("keyword")) {
+ } else if (elementName == QLatin1String("keyword")) {
insertTarget(TargetRec::Keyword, attributes, current);
goto done;
- }
- else if (elementName == QLatin1String("target")) {
+ } else if (elementName == QLatin1String("target")) {
insertTarget(TargetRec::Target, attributes, current);
goto done;
- }
- else if (elementName == QLatin1String("contents")) {
+ } else if (elementName == QLatin1String("contents")) {
insertTarget(TargetRec::Contents, attributes, current);
goto done;
- }
- else
+ } else if (elementName == QLatin1String("proxy")) {
+ node = new ProxyNode(parent, name);
+ if (!indexUrl.isEmpty())
+ location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
+ else if (!indexUrl.isNull())
+ location = Location(name.toLower() + ".html");
+ } else {
goto done;
+ }
{
QString access = attributes.value(QLatin1String("access")).toString();
@@ -596,12 +570,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
node->setAccess(Node::Private);
else
node->setAccess(Node::Public);
+ if (attributes.hasAttribute(QLatin1String("related")))
+ node->setRelatedNonmember(attributes.value(QLatin1String("related")) == QLatin1String("true"));
- if ((elementName != QLatin1String("page")) &&
- (elementName != QLatin1String("qmlclass")) &&
- (elementName != QLatin1String("qmlbasictype")) &&
- (elementName != QLatin1String("jstype")) &&
- (elementName != QLatin1String("jsbasictype"))) {
+ if (attributes.hasAttribute(QLatin1String("threadsafety"))) {
QString threadSafety = attributes.value(QLatin1String("threadsafety")).toString();
if (threadSafety == QLatin1String("non-reentrant"))
node->setThreadSafeness(Node::NonReentrant);
@@ -662,7 +634,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
}
Doc doc(location, location, QString(), emptySet, emptySet); // placeholder
node->setDoc(doc);
- node->setIndexNodeFlag();
+ node->setIndexNodeFlag(); // Important: This node came from an index file.
node->setOutputSubdirectory(project_.toLower());
QString briefAttr = attributes.value(QLatin1String("brief")).toString();
if (!briefAttr.isEmpty()) {
@@ -743,34 +715,54 @@ void QDocIndexFiles::resolveIndex()
basesList_.clear();
}
-/*
- Goes though the list of nodes that are related to other aggregates
- that were read from all index files, and tries to find the aggregate
- nodes from the database. Calls the node's setRelates() for each
- aggregate that is found in the local module (primary tree).
-
- This function is meant to be called before starting the doc generation,
- after all the index files are read.
- */
-void QDocIndexFiles::resolveRelates()
+static const QString getAccessString(Node::Access t)
{
- if (relatedList_.isEmpty())
- return;
- // Restrict searching only to the local (primary) tree
- QVector<Tree*> searchOrder = qdb_->searchOrder();
- qdb_->setLocalSearch();
+ switch (t) {
+ case Node::Public:
+ return QLatin1String("public");
+ case Node::Protected:
+ return QLatin1String("protected");
+ case Node::Private:
+ return QLatin1String("private");
+ default:
+ break;
+ }
+ return QLatin1String("public");
+}
- QPair<FunctionNode*,QString> relatedPair;
- foreach (relatedPair, relatedList_) {
- QStringList path = relatedPair.second.split("::");
- Node* n = qdb_->findRelatesNode(path);
- if (n)
- relatedPair.first->setRelates(static_cast<PageNode*>(n));
+static const QString getStatusString(Node::Status t)
+{
+ switch (t) {
+ case Node::Obsolete:
+ case Node::Deprecated:
+ return QLatin1String("obsolete");
+ case Node::Preliminary:
+ return QLatin1String("preliminary");
+ case Node::Active:
+ return QLatin1String("active");
+ case Node::Internal:
+ return QLatin1String("internal");
+ default:
+ break;
}
- // Restore original search order
- qdb_->setSearchOrder(searchOrder);
- relatedList_.clear();
+ return QLatin1String("active");
+}
+
+static const QString getThreadSafenessString(Node::ThreadSafeness t)
+{
+ switch (t) {
+ case Node::NonReentrant:
+ return QLatin1String("non-reentrant");
+ case Node::Reentrant:
+ return QLatin1String("reentrant");
+ case Node::ThreadSafe:
+ return QLatin1String("thread safe");
+ case Node::UnspecifiedSafeness:
+ default:
+ break;
+ }
+ return QLatin1String("unspecified");
}
/*!
@@ -778,12 +770,9 @@ void QDocIndexFiles::resolveRelates()
specified, returning true if an element was written, and returning
false if an element is not written.
- \node Currently \a generateInternalNodes is always \c true. If it is
- passed as \c false, then nodes marked internal or private are skipped.
+ \note Function nodes are processed in generateFunctionSection()
*/
-bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
- Node* node,
- bool generateInternalNodes)
+bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node)
{
if (!gen_)
gen_ = Generator::currentGenerator();
@@ -794,8 +783,6 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
*/
if (node->isIndexNode())
return false;
- if (!generateInternalNodes && (node->isInternal() || node->isPrivate()))
- return false;
QString nodeName;
QString logicalModuleName;
@@ -873,9 +860,6 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
case Node::Property:
nodeName = "property";
break;
- case Node::Function:
- nodeName = "function";
- break;
case Node::Variable:
nodeName = "variable";
break;
@@ -891,27 +875,10 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
case Node::JsPropertyGroup:
nodeName = "jspropertygroup";
break;
- default:
- return false;
- }
-
- QString access;
- switch (node->access()) {
- case Node::Public:
- access = "public";
- break;
- case Node::Protected:
- access = "protected";
- break;
- case Node::Private:
- /*
- Should we test generateInternalNodes here and return
- false immediately if it is set? As it is now, we are
- always writing all internal and private nodes to the
- index file.
- */
- access = "private";
+ case Node::Proxy:
+ nodeName = "proxy";
break;
+ case Node::Function: // Now processed in generateFunctionSection()
default:
return false;
}
@@ -923,46 +890,9 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
writer.writeStartElement(nodeName);
- QXmlStreamAttributes attributes;
-
if (!node->isTextPageNode() && !node->isCollectionNode() && !node->isHeader()) {
- QString threadSafety;
- switch (node->threadSafeness()) {
- case Node::NonReentrant:
- threadSafety = "non-reentrant";
- break;
- case Node::Reentrant:
- threadSafety = "reentrant";
- break;
- case Node::ThreadSafe:
- threadSafety = "thread safe";
- break;
- case Node::UnspecifiedSafeness:
- default:
- threadSafety = "unspecified";
- break;
- }
- writer.writeAttribute("threadsafety", threadSafety);
- }
-
- QString status;
- switch (node->status()) {
- case Node::Obsolete:
- case Node::Deprecated:
- status = "obsolete";
- break;
- case Node::Preliminary:
- status = "preliminary";
- break;
- case Node::Active:
- status = "active";
- break;
- case Node::Internal:
- status = "internal";
- break;
- default:
- status = "main";
- break;
+ if (node->threadSafeness() != Node::UnspecifiedSafeness)
+ writer.writeAttribute("threadsafety", getThreadSafenessString(node->threadSafeness()));
}
writer.writeAttribute("name", objName);
@@ -1000,9 +930,9 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
if (!href.isEmpty())
writer.writeAttribute("href", href);
- writer.writeAttribute("status", status);
+ writer.writeAttribute("status", getStatusString(node->status()));
if (!node->isTextPageNode() && !node->isCollectionNode() && !node->isHeader()) {
- writer.writeAttribute("access", access);
+ writer.writeAttribute("access", getAccessString(node->access()));
if (node->isAbstract())
writer.writeAttribute("abstract", "true");
}
@@ -1014,9 +944,11 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
}
- if (!node->since().isEmpty()) {
+ if (node->isRelatedNonmember())
+ writer.writeAttribute("related", "true");
+
+ if (!node->since().isEmpty())
writer.writeAttribute("since", node->since());
- }
QString brief = node->doc().trimmedBriefText(node->name()).toString();
switch (node->nodeType()) {
@@ -1202,68 +1134,6 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
writer.writeAttribute("brief", brief);
}
break;
- case Node::Function:
- {
- const FunctionNode* fn = static_cast<const FunctionNode*>(node);
- writer.writeAttribute("meta", fn->metanessString());
- if (fn->isCppNode()) {
- writer.writeAttribute("virtual", fn->virtualness());
- writer.writeAttribute("const", fn->isConst() ? "true":"false");
- writer.writeAttribute("static", fn->isStatic() ? "true":"false");
- writer.writeAttribute("overload", fn->isOverload() ? "true":"false");
- writer.writeAttribute("delete", fn->isDeleted() ? "true" : "false");
- writer.writeAttribute("default", fn->isDefaulted() ? "true" : "false");
- writer.writeAttribute("final", fn->isFinal() ? "true" : "false");
- writer.writeAttribute("override", fn->isOverride() ? "true" : "false");
- if (fn->isRef())
- writer.writeAttribute("refness", QString::number(1));
- else if (fn->isRefRef())
- writer.writeAttribute("refness", QString::number(2));
- if (fn->isOverload())
- writer.writeAttribute("overload-number", QString::number(fn->overloadNumber()));
- if (fn->relates())
- writer.writeAttribute("relates", fn->relates()->name());
- if (fn->hasAssociatedProperties()) {
- QStringList associatedProperties;
- foreach (PropertyNode* pn, fn->associatedProperties()) {
- associatedProperties << pn->name();
- }
- associatedProperties.sort();
- writer.writeAttribute("associated-property", associatedProperties.join(QLatin1Char(',')));
- }
- writer.writeAttribute("type", fn->returnType());
- if (!brief.isEmpty())
- writer.writeAttribute("brief", brief);
- /*
- Note: The "signature" attribute is written to the
- index file, but it is not read back in by qdoc. However,
- we need it for the webxml generator.
- */
- QString signature = fn->signature(false);
- // 'const' is already part of FunctionNode::signature()
- if (fn->isFinal())
- signature += " final";
- if (fn->isOverride())
- signature += " override";
- if (fn->isPureVirtual())
- signature += " = 0";
- else if (fn->isDeleted())
- signature += " = delete";
- else if (fn->isDefaulted())
- signature += " = default";
- writer.writeAttribute("signature", signature);
-
- for (int i = 0; i < fn->parameters().size(); ++i) {
- Parameter parameter = fn->parameters()[i];
- writer.writeStartElement("parameter");
- writer.writeAttribute("type", parameter.dataType());
- writer.writeAttribute("name", parameter.name());
- writer.writeAttribute("default", parameter.defaultValue());
- writer.writeEndElement(); // parameter
- }
- }
- }
- break;
case Node::JsProperty:
case Node::QmlProperty:
{
@@ -1351,6 +1221,7 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
}
break;
+ case Node::Function: // Now processed in generateFunctionSection()
default:
break;
}
@@ -1448,32 +1319,154 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
}
/*!
+ This function writes a <function> element for \a fn to the
+ index file using \a writer.
+ */
+void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionNode *fn)
+{
+ QString objName = fn->name();
+ writer.writeStartElement("function");
+ writer.writeAttribute("name", objName);
+
+ QString fullName = fn->fullDocumentName();
+ if (fullName != objName)
+ writer.writeAttribute("fullname", fullName);
+ QString href = gen_->fullDocumentLocation(fn);
+ if (!href.isEmpty())
+ writer.writeAttribute("href", href);
+ if (fn->threadSafeness() != Node::UnspecifiedSafeness)
+ writer.writeAttribute("threadsafety", getThreadSafenessString(fn->threadSafeness()));
+ writer.writeAttribute("status", getStatusString(fn->status()));
+ writer.writeAttribute("access", getAccessString(fn->access()));
+
+ const Location &declLocation = fn->declLocation();
+ if (!declLocation.fileName().isEmpty())
+ writer.writeAttribute("location", declLocation.fileName());
+ if (!declLocation.filePath().isEmpty()) {
+ writer.writeAttribute("filepath", declLocation.filePath());
+ writer.writeAttribute("lineno", QString("%1").arg(declLocation.lineNo()));
+ }
+
+ if (fn->isRelatedNonmember())
+ writer.writeAttribute("related", "true");
+ if (!fn->since().isEmpty())
+ writer.writeAttribute("since", fn->since());
+
+ QString brief = fn->doc().trimmedBriefText(fn->name()).toString();
+ writer.writeAttribute("meta", fn->metanessString());
+ if (fn->isCppNode()) {
+ writer.writeAttribute("virtual", fn->virtualness());
+ writer.writeAttribute("const", fn->isConst() ? "true" : "false");
+ writer.writeAttribute("static", fn->isStatic() ? "true" : "false");
+ writer.writeAttribute("final", fn->isFinal() ? "true" : "false");
+ writer.writeAttribute("override", fn->isOverride() ? "true" : "false");
+ /*
+ This ensures that for functions that have overloads,
+ the first function written is the one that is not an
+ overload, and the overloads follow it immediately in
+ the index file numbered from 1 to n.
+ */
+ if (fn->isOverload() && (fn->overloadNumber() > 0)) {
+ writer.writeAttribute("overload", "true");
+ writer.writeAttribute("overload-number", QString::number(fn->overloadNumber()));
+ }
+ if (fn->isRef())
+ writer.writeAttribute("refness", QString::number(1));
+ else if (fn->isRefRef())
+ writer.writeAttribute("refness", QString::number(2));
+ if (fn->hasAssociatedProperties()) {
+ QStringList associatedProperties;
+ foreach (Node *n, fn->associatedProperties()) {
+ associatedProperties << n->name();
+ }
+ associatedProperties.sort();
+ writer.writeAttribute("associated-property", associatedProperties.join(QLatin1Char(',')));
+ }
+ writer.writeAttribute("type", fn->returnType());
+ if (!brief.isEmpty())
+ writer.writeAttribute("brief", brief);
+ /*
+ Note: The "signature" attribute is written to the
+ index file, but it is not read back in by qdoc. However,
+ we need it for the webxml generator.
+ */
+ QString signature = fn->signature(false, false);
+ // 'const' is already part of FunctionNode::signature()
+ if (fn->isFinal())
+ signature += " final";
+ if (fn->isOverride())
+ signature += " override";
+ if (fn->isPureVirtual())
+ signature += " = 0";
+ writer.writeAttribute("signature", signature);
+
+ for (int i = 0; i < fn->parameters().count(); ++i) {
+ const Parameter &parameter = fn->parameters().at(i);
+ writer.writeStartElement("parameter");
+ writer.writeAttribute("type", parameter.type());
+ writer.writeAttribute("name", parameter.name());
+ writer.writeAttribute("default", parameter.defaultValue());
+ writer.writeEndElement(); // parameter
+ }
+ }
+ writer.writeEndElement();
+}
+
+/*!
+ This function outputs a <function> element to the index file
+ for each FunctionNode in \a aggregate using the \a writer.
+ The \a aggregate has a function map that contains all the
+ function nodes indexed by function name. But the map is not
+ used as a multimap, so if the \a aggregate contains multiple
+ functions with the same name, only one of those functions is
+ in the function map index. The others are linked to that
+ function using the next overload pointer.
+
+ So this function generates a <function> element for a function
+ followed by a function element for each of its overloads. If a
+ <function> element represents an overload, it has an \c overload
+ attribute set to \c true and an \c {overload-number} attribute
+ set to the function's overload number. If the <function>
+ element does not represent an overload, the <function> element
+ has neither of these attributes.
+ */
+void QDocIndexFiles::generateFunctionSections(QXmlStreamWriter &writer, Aggregate *aggregate)
+{
+ FunctionMap &functionMap = aggregate->functionMap();
+ if (!functionMap.isEmpty()) {
+ FunctionMap::iterator i = functionMap.begin();
+ while (i != functionMap.end()) {
+ FunctionNode *fn = i.value();
+ while (fn != nullptr) {
+ generateFunctionSection(writer, fn);
+ fn = fn->nextOverload();
+ }
+ i++;
+ }
+ }
+}
+
+/*!
Generate index sections for the child nodes of the given \a node
- using the \a writer specified. If \a generateInternalNodes is true,
- nodes marked as internal will be included in the index; otherwise,
- they will be omitted.
+ using the \a writer specified.
*/
-void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
- Node* node,
- bool generateInternalNodes)
+void QDocIndexFiles::generateIndexSections(QXmlStreamWriter &writer, Node *node)
{
/*
Note that groups, modules, and QML modules are written
after all the other nodes.
*/
- if (node->isGroup() || node->isModule() || node->isQmlModule() || node->isJsModule())
+ if (node->isCollectionNode() ||
+ node->isGroup() || node->isModule() || node->isQmlModule() || node->isJsModule())
return;
- if (generateIndexSection(writer, node, generateInternalNodes)) {
+ if (generateIndexSection(writer, node)) {
if (node->isAggregate()) {
- const Aggregate* aggregate = static_cast<const Aggregate*>(node);
-
- NodeList cnodes = aggregate->childNodes();
- std::sort(cnodes.begin(), cnodes.end(), Node::nodeNameLessThan);
-
- foreach (Node* child, cnodes) {
- generateIndexSections(writer, child, generateInternalNodes);
- }
+ Aggregate *aggregate = static_cast<Aggregate *>(node);
+ // First write the function children, then write the nonfunction children.
+ generateFunctionSections(writer, aggregate);
+ foreach (Node *n, aggregate->nonfunctionList())
+ generateIndexSections(writer, n);
}
if (node == root_) {
@@ -1489,7 +1482,7 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
if (!groups.isEmpty()) {
CNMap::ConstIterator g = groups.constBegin();
while (g != groups.constEnd()) {
- if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ if (generateIndexSection(writer, g.value()))
writer.writeEndElement();
++g;
}
@@ -1499,7 +1492,7 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
if (!modules.isEmpty()) {
CNMap::ConstIterator g = modules.constBegin();
while (g != modules.constEnd()) {
- if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ if (generateIndexSection(writer, g.value()))
writer.writeEndElement();
++g;
}
@@ -1509,7 +1502,7 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
if (!qmlModules.isEmpty()) {
CNMap::ConstIterator g = qmlModules.constBegin();
while (g != qmlModules.constEnd()) {
- if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ if (generateIndexSection(writer, g.value()))
writer.writeEndElement();
++g;
}
@@ -1519,7 +1512,7 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
if (!jsModules.isEmpty()) {
CNMap::ConstIterator g = jsModules.constBegin();
while (g != jsModules.constEnd()) {
- if (generateIndexSection(writer, g.value(), generateInternalNodes))
+ if (generateIndexSection(writer, g.value()))
writer.writeEndElement();
++g;
}
@@ -1531,13 +1524,16 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
}
/*!
- Outputs an index file.
+ Writes aqdoc module index in XML to a file named \afilerName.
+ \a url becaomes the \c url attribute of the <INDEX> element.
+ \a title becomes the \c title attribute of the <INDEX> element.
+ \a g is used to get the Config object that contains the variables
+ from the module's .qdocconf file.
*/
void QDocIndexFiles::generateIndex(const QString& fileName,
const QString& url,
const QString& title,
- Generator* g,
- bool generateInternalNodes)
+ Generator *g)
{
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text))
@@ -1562,7 +1558,7 @@ void QDocIndexFiles::generateIndex(const QString& fileName,
if (!root_->tree()->indexTitle().isEmpty())
writer.writeAttribute("indexTitle", root_->tree()->indexTitle());
- generateIndexSections(writer, root_, generateInternalNodes);
+ generateIndexSections(writer, root_);
writer.writeEndElement(); // INDEX
writer.writeEndElement(); // QDOCINDEX