diff options
author | Martin Smith <martin.smith@qt.io> | 2019-01-22 12:11:13 +0100 |
---|---|---|
committer | Paul Wicking <paul.wicking@qt.io> | 2019-02-06 13:19:48 +0000 |
commit | bf8ee4c99d56c2bf770343b7db07f68a2c6a7617 (patch) | |
tree | 73095c02dbfbce7d0830f890aabaa58d2c0e9224 /src/qdoc/htmlgenerator.cpp | |
parent | b3c9f6cb9eb104bf4fe86b377aee7875473f97b4 (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/htmlgenerator.cpp')
-rw-r--r-- | src/qdoc/htmlgenerator.cpp | 217 |
1 files changed, 167 insertions, 50 deletions
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp index a70ffabb9..a466fe227 100644 --- a/src/qdoc/htmlgenerator.cpp +++ b/src/qdoc/htmlgenerator.cpp @@ -335,8 +335,7 @@ void HtmlGenerator::generateDocs() qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", projectUrl, projectDescription, - this, - true); + this); } if (!preparing()) { @@ -1340,11 +1339,8 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark header file documented in \a node using the code \a marker provided. */ -void HtmlGenerator::generateCppReferencePage(Node* node, CodeMarker* marker) +void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *marker) { - Q_ASSERT(node->isAggregate()); - Aggregate* aggregate = static_cast<Aggregate*>(node); - QString title; QString rawTitle; QString fullTitle; @@ -1549,6 +1545,103 @@ void HtmlGenerator::generateCppReferencePage(Node* node, CodeMarker* marker) generateFooter(aggregate); } +void HtmlGenerator::generateProxyPage(Aggregate *aggregate, CodeMarker *marker) +{ + Q_ASSERT(aggregate->isProxyNode()); + + QString title; + QString rawTitle; + QString fullTitle; + Text subtitleText; + SectionVector *summarySections = 0; + SectionVector *detailsSections = 0; + + Sections sections(aggregate); + rawTitle = aggregate->plainName(); + fullTitle = aggregate->plainFullName(); + title = rawTitle + " Proxy Page"; + summarySections = §ions.stdSummarySections(); + detailsSections = §ions.stdDetailsSections(); + generateHeader(title, aggregate, marker); + generateTitle(title, subtitleText, SmallSubTitle, aggregate, marker); + generateBrief(aggregate, marker); + SectionVector::ConstIterator s = summarySections->constBegin(); + while (s != summarySections->constEnd()) { + if (!s->members().isEmpty()) { + // out() << "<hr />\n"; + QString ref = registerRef(s->title().toLower()); + out() << "<a name=\"" << ref << "\"></a>" << divNavTop << "\n"; + out() << "<h2 id=\"" << ref << "\">" << protectEnc(s->title()) << "</h2>\n"; + generateSection(s->members(), aggregate, marker); + } + ++s; + } + + QString detailsRef = registerRef("details"); + out() << "<a name=\"" << detailsRef << "\"></a>" << divNavTop << '\n'; + + if (!aggregate->doc().isEmpty()) { + generateExtractionMark(aggregate, DetailedDescriptionMark); + //out() << "<hr />\n" + out() << "<div class=\"descr\">\n" // QTBUG-9504 + << "<h2 id=\"" << detailsRef << "\">" << "Detailed Description" << "</h2>\n"; + generateBody(aggregate, marker); + out() << "</div>\n"; // QTBUG-9504 + generateAlsoList(aggregate, marker); + generateMaintainerList(aggregate, marker); + generateExtractionMark(aggregate, EndMark); + } + + s = detailsSections->constBegin(); + while (s != detailsSections->constEnd()) { + if (s->isEmpty()) { + ++s; + continue; + } + //out() << "<hr />\n"; + if (!s->divClass().isEmpty()) + out() << "<div class=\"" << s->divClass() << "\">\n"; // QTBUG-9504 + out() << "<h2>" << protectEnc(s->title()) << "</h2>\n"; + + NodeVector::ConstIterator m = s->members().constBegin(); + while (m != s->members().constEnd()) { + if (!(*m)->isPrivate()) { // ### check necessary? + if (!(*m)->isClass()) + generateDetailedMember(*m, aggregate, marker); + else { + out() << "<h3> class "; + generateFullName(*m, aggregate); + out() << "</h3>"; + generateBrief(*m, marker, aggregate); + } + + QStringList names; + names << (*m)->name(); + if ((*m)->isFunction()) { + const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m); + if (func->isSomeCtor() || func->isDtor() || func->overloadNumber() != 0) + names.clear(); + } else if ((*m)->isEnumType()) { + const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m); + if (enume->flagsType()) + names << enume->flagsType()->name(); + + foreach (const QString &enumName, + enume->doc().enumItemNames().toSet() - + enume->doc().omitEnumItemNames().toSet()) + names << plainCode(marker->markedUpEnumValue(enumName, + enume)); + } + } + ++m; + } + if (!s->divClass().isEmpty()) + out() << "</div>\n"; // QTBUG-9504 + ++s; + } + generateFooter(aggregate); +} + /*! Generate the HTML page for a QML type. \qcn is the QML type. \marker is the code markeup object. @@ -1794,6 +1887,46 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke } /*! + Generate the HTML page for a generic collection. This is usually + a collection of C++ elements that are related to an element in + a different module. + */ +void HtmlGenerator::generateGenericCollectionPage(CollectionNode *cn, CodeMarker *marker) +{ + SubTitleSize subTitleSize = LargeSubTitle; + QString fullTitle = cn->name(); + QString ref; + + generateHeader(fullTitle, cn, marker); + generateKeywordAnchors(cn); + generateTitle(fullTitle, Text() << cn->subtitle(), subTitleSize, cn, marker); + + Text brief; + brief << "Each function or type documented here is related to a class or " + << "namespace that is documented in a different module. The reference " + << "page for that class or namespace will link to the function or type " + << "on this page."; +#if 0 + << Atom(Atom::LinkNode, CodeMarker::stringForNode(NS)) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, " here.") + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); +#endif + out() << "<p>"; + generateText(brief, cn, marker); + out() << "</p>\n"; + + NodeList::ConstIterator m = cn->members().constBegin(); + while (m != cn->members().constEnd()) { + generateDetailedMember(*m, cn, marker); + ++m; + } + + // generateAnnotatedList(cn, marker, cn->members()); + generateFooter(cn); +} + +/*! Returns "html" for this subclass of Generator. */ QString HtmlGenerator::fileExtension() const @@ -2117,12 +2250,11 @@ void HtmlGenerator::generateRequisites(Aggregate *aggregate, CodeMarker *marker) const QString instantiatedByText = "Instantiated By"; const QString qtVariableText = "qmake"; - //add the includes to the map - if (!aggregate->includes().isEmpty()) { + //add the include files to the map + if (!aggregate->includeFiles().isEmpty()) { text.clear(); - text << highlightedCode(indent(codeIndent, - marker->markedUpIncludes(aggregate->includes())), - aggregate); + text << highlightedCode(indent(codeIndent, marker->markedUpIncludes(aggregate->includeFiles())), + aggregate); requisites.insert(headerText, text); } @@ -2381,21 +2513,6 @@ void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, } /*! - This function is not used currently. - It should be removed. MWS 27-06-2018 - */ -void HtmlGenerator::generateIncludes(const Aggregate *aggregate, CodeMarker *marker) -{ - if (!aggregate->includes().isEmpty()) { - out() << "<pre class=\"cpp\">" - << trimmedTrailing(highlightedCode(indent(codeIndent, - marker->markedUpIncludes(aggregate->includes())), - aggregate), codePrefix, codeSuffix) - << "</pre>"; - } -} - -/*! Revised for the new doc format. Generates a table of contents beginning at \a node. */ @@ -3356,7 +3473,7 @@ void HtmlGenerator::generateSectionInheritedList(const Section& section, const N } } -// generateSynopsis(qmn,relative,marker,Section::Details,false); +// generateSynopsis(*m, relative, marker, Section::Summary, alignNames); void HtmlGenerator::generateSynopsis(const Node *node, const Node *relative, CodeMarker *marker, @@ -3439,8 +3556,8 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, html += QLatin1String("</b>"); } else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { - const Node* n = qdb_->findFunctionNode(par1.toString(), relative, genus); - QString link = linkForNode(n, relative); + const FunctionNode *fn = qdb_->findFunctionNode(par1.toString(), relative, genus); + QString link = linkForNode(fn, relative); addLink(link, arg, &html); par1 = QStringRef(); } @@ -3652,7 +3769,7 @@ QString HtmlGenerator::protect(const QString &string, const QString &outputEncod QString HtmlGenerator::fileBase(const Node *node) const { QString result = Generator::fileBase(node); - if (!node->isAggregate() && node->status() == Node::Obsolete) + if (!node->isAggregate() && node->isObsolete()) result += QLatin1String("-obsolete"); return result; } @@ -3809,7 +3926,7 @@ QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) return node->url(); if (fileBase(node).isEmpty()) return QString(); - if (node->access() == Node::Private) + if (node->isPrivate()) return QString(); QString fn = fileName(node); if (node && node->parent() && @@ -3869,7 +3986,7 @@ void HtmlGenerator::generateFullName(const Node *apparentNode, const Node *relat } void HtmlGenerator::generateDetailedMember(const Node *node, - const Aggregate *relative, + const PageNode *relative, CodeMarker *marker) { const EnumNode *etn; @@ -3952,7 +4069,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, generatePrivateSignalNote(node, marker); if (fn->isInvokable()) generateInvokableNote(node, marker); - generateAssociatedPropertyNotes(fn); + generateAssociatedPropertyNotes(const_cast<FunctionNode*>(fn)); } else if (node->isEnumType()) { const EnumNode *etn = static_cast<const EnumNode *>(node); @@ -4094,13 +4211,13 @@ void HtmlGenerator::generateQmlSummary(const Section& section, while (m != section.members().constEnd()) { out() << "<li class=\"fn\">"; generateQmlItem(*m,relative,marker,true); - if ((*m)->isQmlPropertyGroup()) { + if ((*m)->isQmlPropertyGroup() || (*m)->isJsPropertyGroup()) { const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(*m); - if (!qpgn->childNodes().isEmpty()) { - NodeList::ConstIterator p = qpgn->childNodes().constBegin(); + if (qpgn->count() > 0) { + NodeList::ConstIterator p = qpgn->constBegin(); out() << "<ul>\n"; - while (p != qpgn->childNodes().constEnd()) { - if ((*p)->isQmlProperty()) { + while (p != qpgn->constEnd()) { + if ((*p)->isQmlProperty() || (*p)->isJsProperty()) { out() << "<li class=\"fn\">"; generateQmlItem(*p, relative, marker, true); out() << "</li>\n"; @@ -4145,7 +4262,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, QString nodeRef = refForNode(node); if (node->isQmlPropertyGroup() || node->isJsPropertyGroup()) { const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node); - NodeList::ConstIterator p = qpgn->childNodes().constBegin(); + NodeList::ConstIterator p = qpgn->constBegin(); out() << "<div class=\"qmlproto\">"; out() << "<div class=\"table\"><table class=\"qmlname\">"; @@ -4155,7 +4272,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, out() << "<a name=\"" + nodeRef + "\"></a>"; out() << "<b>" << heading << "</b>"; out() << "</p></th></tr>"; - while (p != qpgn->childNodes().constEnd()) { + while (p != qpgn->constEnd()) { if ((*p)->isQmlProperty() || (*p)->isJsProperty()) { qpn = static_cast<QmlPropertyNode*>(*p); nodeRef = refForNode(qpn); @@ -4181,7 +4298,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, if (!qpn->isReadOnlySet()) { if (qpn->declarativeCppNode()) - qpn->setReadOnly(!qpn->isWritable()); + qpn->markReadOnly(!qpn->isWritable()); } if (qpn->isReadOnly()) out() << "<span class=\"qmlreadonly\">[read-only] </span>"; @@ -4323,7 +4440,7 @@ void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType if (!func->hasAssociatedProperties()) { if (func->overloadNumber() == 0) out() << "[overload1]"; - out() << "$$$" + func->name() + func->rawParameters().remove(' '); + out() << "$$$" + func->name() + func->parameters().rawSignature().remove(' '); } } else if (node->isProperty()) { out() << "-prop"; @@ -4332,7 +4449,7 @@ void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType foreach (const Node *propFuncNode, list) { if (propFuncNode->isFunction()) { const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode); - out() << "$$$" + func->name() + func->rawParameters().remove(' '); + out() << "$$$" + func->name() + func->parameters().rawSignature().remove(' '); } } } else if (node->isEnumType()) { @@ -4638,9 +4755,8 @@ void HtmlGenerator::reportOrphans(const Aggregate* parent) return; QString message = "has documentation but no \\relates command"; - for (int i=0; i<children.size(); ++i) { - Node* child = children[i]; - if (!child || child->isInternal() || child->doc().isEmpty() || child->relates()) + foreach (Node *child, children) { + if (!child || child->isInternal() || child->doc().isEmpty() || !child->isRelatedNonmember()) continue; switch (child->nodeType()) { case Node::Enum: @@ -4712,14 +4828,15 @@ QXmlStreamWriter& HtmlGenerator::xmlWriter() Generates bold Note lines that explain how function \a fn is associated with each of its associated properties. */ -void HtmlGenerator::generateAssociatedPropertyNotes(const FunctionNode* fn) +void HtmlGenerator::generateAssociatedPropertyNotes(FunctionNode *fn) { if (fn->hasAssociatedProperties()) { out() << "<p><b>Note:</b> "; - PropNodeList propertyNodes = fn->associatedProperties(); - std::sort(propertyNodes.begin(), propertyNodes.end(), Node::nodeNameLessThan); - foreach (const PropertyNode* pn, propertyNodes) { + NodeList &nodes = fn->associatedProperties(); + std::sort(nodes.begin(), nodes.end(), Node::nodeNameLessThan); + foreach (const Node *n, nodes) { QString msg; + const PropertyNode *pn = static_cast<const PropertyNode*>(n); switch (pn->role(fn)) { case PropertyNode::Getter: msg = QStringLiteral("Getter function "); |