/**************************************************************************** ** ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "sections.h" #include "config.h" #include "generator.h" #include "loggingcategory.h" #include #include #include QT_BEGIN_NAMESPACE // Aggregate *Sections::aggregate_ = nullptr; static bool sectionsInitialized = false; QVector
Sections::stdSummarySections_(7, Section(Section::Summary, Section::Active)); QVector
Sections::stdDetailsSections_(7, Section(Section::Details, Section::Active)); QVector
Sections::stdCppClassSummarySections_(18, Section(Section::Summary, Section::Active)); QVector
Sections::stdCppClassDetailsSections_(6, Section(Section::Details, Section::Active)); QVector
Sections::sinceSections_(15, Section(Section::Details, Section::Active)); QVector
Sections::allMembers_(1, Section(Section::AllMembers, Section::Active)); QVector
Sections::stdQmlTypeSummarySections_(7, Section(Section::Summary, Section::Active)); QVector
Sections::stdQmlTypeDetailsSections_(7, Section(Section::Details, Section::Active)); /*! \class Section \brief A class for containing the elements of one documentation section */ /*! The constructor used when the \a style and \a status must be provided. */ Section::Section(Style style, Status status) : style_(style), status_(status), aggregate_(nullptr) { // members_.reserve(100); // obsoleteMembers_.reserve(50); // reimplementedMembers_.reserve(50); } /*! The destructor must delete the members of collections when the members are allocated on the heap. */ Section::~Section() { clear(); } /*! A Section is now an element in a static vector, so we don't have to repeatedly construct and destroy them. But we do need to clear them before each call to build the sections for a C++ or QML entity. */ void Section::clear() { memberMap_.clear(); obsoleteMemberMap_.clear(); reimplementedMemberMap_.clear(); if (!classMapList_.isEmpty()) { for (int i = 0; i < classMapList_.size(); i++) { ClassMap *cm = classMapList_[i]; classMapList_[i] = nullptr; delete cm; } classMapList_.clear(); } keys_.clear(); obsoleteKeys_.clear(); members_.clear(); obsoleteMembers_.clear(); reimplementedMembers_.clear(); inheritedMembers_.clear(); if (!classKeysNodesList_.isEmpty()) { for (int i = 0; i < classKeysNodesList_.size(); i++) { ClassKeysNodes *ckn = classKeysNodesList_[i]; classKeysNodesList_[i] = nullptr; delete ckn; } classKeysNodesList_.clear(); } aggregate_ = nullptr; } /*! Construct a name for the \a node that can be used for sorting a set of nodes into equivalence classes. If \a name is provided, start with that name. Itherwise start with the name in \a node. */ QString Section::sortName(const Node *node, const QString *name) { QString nodeName; if (name != nullptr) nodeName = *name; else nodeName = node->name(); int numDigits = 0; for (int i = nodeName.size() - 1; i > 0; --i) { if (nodeName.at(i).digitValue() == -1) break; ++numDigits; } // we want 'qint8' to appear before 'qint16' if (numDigits > 0) { for (int i = 0; i < 4 - numDigits; ++i) nodeName.insert(nodeName.size() - numDigits - 1, QLatin1Char('0')); } if (node->isFunction()) { const FunctionNode *fn = static_cast(node); if (fn->isCppFunction()) { QString sortNo; if (fn->isSomeCtor()) sortNo = QLatin1String("C"); else if (fn->isDtor()) sortNo = QLatin1String("D"); else if (nodeName.startsWith(QLatin1String("operator")) && nodeName.length() > 8 && !nodeName[8].isLetterOrNumber()) sortNo = QLatin1String("F"); else sortNo = QLatin1String("E"); return sortNo + nodeName + QLatin1Char(' ') + QString::number(fn->overloadNumber(), 36); } if (fn->isQmlMethod() || fn->isQmlSignal() || fn->isQmlSignalHandler()) return QLatin1Char('E') + nodeName; if (fn->isJsMethod() || fn->isJsSignal() || fn->isJsSignalHandler()) return QLatin1Char('E') + nodeName; } if (node->isClassNode()) return QLatin1Char('A') + nodeName; if (node->isProperty() || node->isVariable()) return QLatin1Char('E') + nodeName; return QLatin1Char('B') + nodeName; } /*! Inserts the \a node into this section if it is appropriate for this section. */ void Section::insert(Node *node) { bool irrelevant = false; bool inherited = false; if (!node->isRelatedNonmember()) { Aggregate *p = node->parent(); if (!p->isNamespace() && p != aggregate_) { if ((!p->isQmlType() && !p->isJsType()) || !p->isAbstract()) inherited = true; } } if (node->isPrivate() || node->isInternal()) { irrelevant = true; } else if (node->isFunction()) { FunctionNode *func = static_cast(node); irrelevant = (inherited && (func->isSomeCtor() || func->isDtor())); } else if (node->isClassNode() || node->isEnumType() || node->isTypedef() || node->isVariable()) { irrelevant = (inherited && style_ != AllMembers); if (!irrelevant && style_ == Details && node->isTypedef()) { const TypedefNode *tdn = static_cast(node); if (tdn->associatedEnum()) irrelevant = true; } } if (!irrelevant) { QString key = sortName(node); if (node->isObsolete()) { obsoleteMemberMap_.insertMulti(key, node); } else { if (!inherited) memberMap_.insertMulti(key, node); else if (style_ == AllMembers) { if (!memberMap_.contains(key)) memberMap_.insertMulti(key, node); } if (inherited && (node->parent()->isClassNode() || node->parent()->isNamespace())) { if (inheritedMembers_.isEmpty() || inheritedMembers_.last().first != node->parent()) { QPair p(node->parent(), 0); inheritedMembers_.append(p); } inheritedMembers_.last().second++; } } } } /*! Returns \c true if the \a node is a reimplemented member function of the current class. If true, the \a node is inserted into the reimplemented member map. The test is performed only when the section status is \e Active. True is returned only if \a node is inserted into the map. That is, false is returned if the \a node is already in the map. */ bool Section::insertReimplementedMember(Node *node) { if (!node->isPrivate() && !node->isRelatedNonmember()) { const FunctionNode *fn = static_cast(node); if (!fn->overridesThis().isEmpty() && (status_ == Active)) { if (fn->parent() == aggregate_) { QString key = sortName(fn); if (!reimplementedMemberMap_.contains(key)) { reimplementedMemberMap_.insert(key, node); return true; } } } } return false; } /*! Allocate a new ClassMap on the heap for the \a aggregate node, append it to the list of class maps, and return a pointer to the new class map. */ ClassMap *Section::newClassMap(const Aggregate *aggregate) { ClassMap *classMap = new ClassMap; classMap->first = static_cast(aggregate); classMapList_.append(classMap); return classMap; } /*! Add node \a n to the \a classMap and to the member map. */ void Section::add(ClassMap *classMap, Node *n) { QString key = n->name(); key = sortName(n, &key); memberMap_.insert(key, n); classMap->second.insert(key, n); } /*! If this section is not empty, convert its maps to sequential structures for better traversal during doc generation. */ void Section::reduce() { if (!isEmpty()) { keys_ = memberMap_.keys(); obsoleteKeys_ = obsoleteMemberMap_.keys(); members_ = memberMap_.values().toVector(); obsoleteMembers_ = obsoleteMemberMap_.values().toVector(); reimplementedMembers_ = reimplementedMemberMap_.values().toVector(); for (int i = 0; i < classMapList_.size(); i++) { ClassMap *cm = classMapList_[i]; ClassKeysNodes *ckn = new ClassKeysNodes; ckn->first = cm->first; ckn->second.second = cm->second.values().toVector(); ckn->second.first = cm->second.keys(); classKeysNodesList_.append(ckn); } } } /*! \class Sections \brief A class for creating vectors of collections for documentation Each element in a vector is an instance of Section, which contains all the elements that will be documented in one section of a reference documentation page. */ /*! This constructor builds the vectors of sections based on the type of the \a aggregate node. */ Sections::Sections(Aggregate *aggregate) : aggregate_(aggregate) { initSections(); initAggregate(allMembers_, aggregate_); switch (aggregate_->nodeType()) { case Node::Class: case Node::Struct: case Node::Union: initAggregate(stdCppClassSummarySections_, aggregate_); initAggregate(stdCppClassDetailsSections_, aggregate_); buildStdCppClassRefPageSections(); break; case Node::JsType: case Node::JsBasicType: case Node::QmlType: case Node::QmlBasicType: initAggregate(stdQmlTypeSummarySections_, aggregate_); initAggregate(stdQmlTypeDetailsSections_, aggregate_); buildStdQmlTypeRefPageSections(); break; case Node::Namespace: case Node::HeaderFile: case Node::Proxy: default: initAggregate(stdSummarySections_, aggregate_); initAggregate(stdDetailsSections_, aggregate_); buildStdRefPageSections(); break; } } /*! This constructor builds a vector of sections from the \e since node map, \a nsmap */ Sections::Sections(const NodeMultiMap &nsmap) : aggregate_(nullptr) { initSections(); if (nsmap.isEmpty()) return; SectionVector §ions = sinceSections(); for (auto it = nsmap.constBegin(); it != nsmap.constEnd(); ++it) { Node *node = it.value(); switch (node->nodeType()) { case Node::JsType: case Node::QmlType: sections[SinceQmlTypes].appendMember(node); break; case Node::Namespace: sections[SinceNamespaces].appendMember(node); break; case Node::Class: case Node::Struct: case Node::Union: sections[SinceClasses].appendMember(node); break; case Node::Enum: sections[SinceEnumTypes].appendMember(node); break; case Node::Typedef: sections[SinceTypedefs].appendMember(node); break; case Node::Function: { const FunctionNode *fn = static_cast(node); switch (fn->metaness()) { case FunctionNode::JsSignal: case FunctionNode::QmlSignal: sections[SinceQmlSignals].appendMember(node); break; case FunctionNode::JsSignalHandler: case FunctionNode::QmlSignalHandler: sections[SinceQmlSignalHandlers].appendMember(node); break; case FunctionNode::JsMethod: case FunctionNode::QmlMethod: sections[SinceQmlMethods].appendMember(node); break; default: if (fn->isMacro()) sections[SinceMacros].appendMember(node); else { Node *p = fn->parent(); if (p) { if (p->isClassNode()) sections[SinceMemberFunctions].appendMember(node); else if (p->isNamespace()) { if (p->name().isEmpty()) sections[SinceGlobalFunctions].appendMember(node); else sections[SinceNamespaceFunctions].appendMember(node); } else sections[SinceGlobalFunctions].appendMember(node); } else sections[SinceGlobalFunctions].appendMember(node); } break; } break; } case Node::Property: sections[SinceProperties].appendMember(node); break; case Node::Variable: sections[SinceVariables].appendMember(node); break; case Node::JsProperty: case Node::QmlProperty: sections[SinceQmlProperties].appendMember(node); break; default: break; } } } /*! The behavior of the destructor depends on the type of the Aggregate node that was passed to the constructor. If the constructor was passed a multimap, the destruction is a bit different because there was no Aggregate node. */ Sections::~Sections() { if (aggregate_) { switch (aggregate_->nodeType()) { case Node::Class: case Node::Struct: case Node::Union: clear(stdCppClassSummarySections()); clear(stdCppClassDetailsSections()); allMembersSection().clear(); break; case Node::JsType: case Node::JsBasicType: case Node::QmlType: case Node::QmlBasicType: clear(stdQmlTypeSummarySections()); clear(stdQmlTypeDetailsSections()); allMembersSection().clear(); break; default: clear(stdSummarySections()); clear(stdDetailsSections()); allMembersSection().clear(); break; } aggregate_ = nullptr; } else { clear(sinceSections()); } } /*! Initialize the Aggregate in each Section of vector \a v with \a aggregate. */ void Sections::initAggregate(SectionVector &v, Aggregate *aggregate) { for (int i = 0; i < v.size(); ++i) v[i].setAggregate(aggregate); } /*! This function is called once to initialize all the instances of QVector
. The vectors have already been constructed with the correct number of Section entries in each. Each Section entry has already been constructed with the correct values of Style and Status for the vector it is in. This function adds the correct text strings to each section in each vector. */ void Sections::initSections() { if (sectionsInitialized) return; sectionsInitialized = true; allMembers_[0].init("member", "members"); { QVector
&v = stdCppClassSummarySections_; v[0].init("Public Types", "public type", "public types"); v[1].init("Properties", "property", "properties"); v[2].init("Public Functions", "public function", "public functions"); v[3].init("Public Slots", "public slot", "public slots"); v[4].init("Signals", "signal", "signals"); v[5].init("Public Variables", "public variable", "public variables"); v[6].init("Static Public Members", "static public member", "static public members"); v[7].init("Protected Types", "protected type", "protected types"); v[8].init("Protected Functions", "protected function", "protected functions"); v[9].init("Protected Slots", "protected slot", "protected slots"); v[10].init("Protected Variables", "protected type", "protected variables"); v[11].init("Static Protected Members", "static protected member", "static protected members"); v[12].init("Private Types", "private type", "private types"); v[13].init("Private Functions", "private function", "private functions"); v[14].init("Private Slots", "private slot", "private slots"); v[15].init("Static Private Members", "static private member", "static private members"); v[16].init("Related Non-Members", "related non-member", "related non-members"); v[17].init("Macros", "macro", "macros"); } { QVector
&v = stdCppClassDetailsSections_; v[0].init("Member Type Documentation", "types", "member", "members"); v[1].init("Property Documentation", "prop", "member", "members"); v[2].init("Member Function Documentation", "func", "member", "members"); v[3].init("Member Variable Documentation", "vars", "member", "members"); v[4].init("Related Non-Members", "relnonmem", "member", "members"); v[5].init("Macro Documentation", "macros", "member", "members"); } { QVector
&v = stdSummarySections_; v[0].init("Namespaces", "namespace", "namespaces"); v[1].init("Classes", "class", "classes"); v[2].init("Types", "type", "types"); v[3].init("Variables", "variable", "variables"); v[4].init("Static Variables", "static variable", "static variables"); v[5].init("Functions", "function", "functions"); v[6].init("Macros", "macro", "macros"); } { QVector
&v = stdDetailsSections_; v[0].init("Namespaces", "nmspace", "namespace", "namespaces"); v[1].init("Classes", "classes", "class", "classes"); v[2].init("Type Documentation", "types", "type", "types"); v[3].init("Variable Documentation", "vars", "variable", "variables"); v[4].init("Static Variables", QString(), "static variable", "static variables"); v[5].init("Function Documentation", "func", "function", "functions"); v[6].init("Macro Documentation", "macros", "macro", "macros"); } { QVector
&v = sinceSections_; v[SinceNamespaces].init(" New Namespaces"); v[SinceClasses].init(" New Classes"); v[SinceMemberFunctions].init(" New Member Functions"); v[SinceNamespaceFunctions].init(" New Functions in Namespaces"); v[SinceGlobalFunctions].init(" New Global Functions"); v[SinceMacros].init(" New Macros"); v[SinceEnumTypes].init(" New Enum Types"); v[SinceTypedefs].init(" New Typedefs"); v[SinceProperties].init(" New Properties"); v[SinceVariables].init(" New Variables"); v[SinceQmlTypes].init(" New QML Types"); v[SinceQmlProperties].init(" New QML Properties"); v[SinceQmlSignals].init(" New QML Signals"); v[SinceQmlSignalHandlers].init(" New QML Signal Handlers"); v[SinceQmlMethods].init(" New QML Methods"); } { QVector
&v = stdQmlTypeSummarySections_; v[0].init("Properties", "property", "properties"); v[1].init("Attached Properties", "attached property", "attached properties"); v[2].init("Signals", "signal", "signals"); v[3].init("Signal Handlers", "signal handler", "signal handlers"); v[4].init("Attached Signals", "attached signal", "attached signals"); v[5].init("Methods", "method", "methods"); v[6].init("Attached Methods", "attached method", "attached methods"); } { QVector
&v = stdQmlTypeDetailsSections_; v[0].init("Property Documentation", "qmlprop", "member", "members"); v[1].init("Attached Property Documentation", "qmlattprop", "member", "members"); v[2].init("Signal Documentation", "qmlsig", "signal", "signals"); v[3].init("Signal Handler Documentation", "qmlsighan", "signal handler", "signal handlers"); v[4].init("Attached Signal Documentation", "qmlattsig", "signal", "signals"); v[5].init("Method Documentation", "qmlmeth", "member", "members"); v[6].init("Attached Method Documentation", "qmlattmeth", "member", "members"); } } /*! Reset each Section in vector \a v to its initialized state. */ void Sections::clear(QVector
&v) { for (int i = 0; i < v.size(); ++i) v[i].clear(); } /*! Linearize the maps in each Section in \a v. */ void Sections::reduce(QVector
&v) { for (int i = 0; i < v.size(); ++i) v[i].reduce(); } /*! This is a private helper function for buildStdRefPageSections(). */ void Sections::stdRefPageSwitch(SectionVector &v, Node *n, Node *t) { // t is the reference node to be tested, n is the node to be distributed. // t differs from n only for shared comment nodes. if (!t) t = n; switch (t->nodeType()) { case Node::Namespace: v[StdNamespaces].insert(n); return; case Node::Class: case Node::Struct: case Node::Union: v[StdClasses].insert(n); return; case Node::Enum: case Node::Typedef: v[StdTypes].insert(n); return; case Node::Function: { FunctionNode *func = static_cast(t); if (func->isMacro()) v[StdMacros].insert(n); else v[StdFunctions].insert(n); } return; case Node::Variable: { const VariableNode *var = static_cast(t); if (!var->doc().isEmpty()) { if (var->isStatic()) v[StdStaticVariables].insert(n); else v[StdVariables].insert(n); } } return; case Node::SharedComment: { SharedCommentNode *scn = static_cast(t); if (!scn->doc().isEmpty() && scn->collective().count()) stdRefPageSwitch( v, scn, scn->collective().first()); // TODO: warn about mixed node types in collective? } return; default: return; } } /*! Build the section vectors for a standard reference page, when the aggregate node is not a C++ class or a QML type. If this is for a namespace page then if the namespace node itself does not have documentation, only its children that have documentation should be documented. In other words, there are cases where a namespace is declared but does not have documentation, but some of the elements declared in that namespace do have documentation. This special processing of namespaces that do not have a documentation comment is meant to allow documenting its members that do have documentation while avoiding posting error messages for its members that are not documented. */ void Sections::buildStdRefPageSections() { const NamespaceNode *ns = nullptr; bool documentAll = true; // document all the children if (aggregate_->isNamespace()) { ns = static_cast(aggregate_); if (!ns->hasDoc()) documentAll = false; // only document children that have documentation } for (auto it = aggregate_->constBegin(); it != aggregate_->constEnd(); ++it) { Node *n = *it; if (documentAll || n->hasDoc()) { stdRefPageSwitch(stdSummarySections(), n); stdRefPageSwitch(stdDetailsSections(), n); } } if (!aggregate_->relatedByProxy().isEmpty()) { const QList &relatedBy = aggregate_->relatedByProxy(); for (const auto &node : relatedBy) stdRefPageSwitch(stdSummarySections(), node); } /* If we are building the sections for the reference page for a namespace node, include all the namespace node's included children in the sections. */ if (ns && !ns->includedChildren().isEmpty()) { const QList &children = ns->includedChildren(); for (const auto &child : children) { if (documentAll || child->hasDoc()) stdRefPageSwitch(stdSummarySections(), child); } } reduce(stdSummarySections()); reduce(stdDetailsSections()); allMembersSection().reduce(); } /*! Inserts the node \a n in one of the entries in the vector \a v depending on the node's type, access attribute, and a few other attributes if the node is a signal, slot, or function. */ void Sections::distributeNodeInSummaryVector(SectionVector &sv, Node *n) { if (n->isSharedCommentNode()) return; if (n->isFunction()) { FunctionNode *fn = static_cast(n); if (fn->isRelatedNonmember()) { if (fn->isMacro()) sv[Macros].insert(n); else sv[RelatedNonmembers].insert(n); return; } if (fn->isIgnored()) return; if (fn->isSlot()) { if (fn->isPublic()) sv[PublicSlots].insert(fn); else if (fn->isPrivate()) sv[PrivateSlots].insert(fn); else sv[ProtectedSlots].insert(fn); } else if (fn->isSignal()) { if (fn->isPublic()) sv[Signals].insert(fn); } else if (fn->isPublic()) { if (fn->isStatic()) sv[StaticPublicMembers].insert(fn); else if (!sv[PublicFunctions].insertReimplementedMember(fn)) sv[PublicFunctions].insert(fn); } else if (fn->isPrivate()) { if (fn->isStatic()) sv[StaticPrivateMembers].insert(fn); else if (!sv[PrivateFunctions].insertReimplementedMember(fn)) sv[PrivateFunctions].insert(fn); } else { // protected if (fn->isStatic()) sv[StaticProtectedMembers].insert(fn); else if (!sv[ProtectedFunctions].insertReimplementedMember(fn)) sv[ProtectedFunctions].insert(fn); } return; } if (n->isRelatedNonmember()) { sv[RelatedNonmembers].insert(n); return; } if (n->isVariable()) { if (n->isStatic()) { if (n->isPublic()) sv[StaticPublicMembers].insert(n); else if (n->isPrivate()) sv[StaticPrivateMembers].insert(n); else sv[StaticProtectedMembers].insert(n); } else { if (n->isPublic()) sv[PublicVariables].insert(n); else if (!n->isPrivate()) sv[ProtectedVariables].insert(n); } return; } /* Getting this far means the node is either a property or some kind of type, like an enum or a typedef. */ if (n->isTypedef() && (n->name() == QLatin1String("QtGadgetHelper"))) return; if (n->isProperty()) sv[Properties].insert(n); else if (n->isPublic()) sv[PublicTypes].insert(n); else if (n->isPrivate()) sv[PrivateTypes].insert(n); else sv[ProtectedTypes].insert(n); } /*! Inserts the node \a n in one of the entries in the vector \a v depending on the node's type, access attribute, and a few other attributes if the node is a signal, slot, or function. */ void Sections::distributeNodeInDetailsVector(SectionVector &dv, Node *n) { if (n->isSharingComment()) return; // t is the reference node to be tested - typically it's this node (n), but for // shared comment nodes we need to distribute based on the nodes in its collective. Node *t = n; if (n->isSharedCommentNode() && n->hasDoc()) { SharedCommentNode *scn = static_cast(n); if (scn->collective().count()) t = scn->collective().first(); // TODO: warn about mixed node types in collective? } if (t->isFunction()) { FunctionNode *fn = static_cast(t); if (fn->isRelatedNonmember()) { if (fn->isMacro()) dv[DetailsMacros].insert(n); else dv[DetailsRelatedNonmembers].insert(n); return; } if (fn->isIgnored()) return; if (!fn->hasAssociatedProperties() || !fn->doc().isEmpty()) dv[DetailsMemberFunctions].insert(n); return; } if (t->isRelatedNonmember()) { dv[DetailsRelatedNonmembers].insert(n); return; } if (t->isEnumType() || t->isTypedef()) { if (t->name() != QLatin1String("QtGadgetHelper")) dv[DetailsMemberTypes].insert(n); return; } if (t->isProperty()) dv[DetailsProperties].insert(n); else if (t->isVariable() && !t->doc().isEmpty()) dv[DetailsMemberVariables].insert(n); } void Sections::distributeQmlNodeInDetailsVector(SectionVector &dv, Node *n) { if (n->isSharingComment()) return; // t is the reference node to be tested - typically it's this node (n), but for // shared comment nodes we need to distribute based on the nodes in its collective. Node *t = n; if (n->isSharedCommentNode() && n->hasDoc()) { if (n->isPropertyGroup()) { dv[QmlProperties].insert(n); return; } SharedCommentNode *scn = static_cast(n); if (scn->collective().count()) t = scn->collective().first(); // TODO: warn about mixed node types in collective? } if (t->isQmlProperty() || t->isJsProperty()) { QmlPropertyNode *pn = static_cast(t); if (pn->isAttached()) dv[QmlAttachedProperties].insert(n); else dv[QmlProperties].insert(n); } else if (t->isFunction()) { FunctionNode *fn = static_cast(t); if (fn->isQmlSignal() || fn->isJsSignal()) { if (fn->isAttached()) dv[QmlAttachedSignals].insert(n); else dv[QmlSignals].insert(n); } else if (fn->isQmlSignalHandler() || fn->isJsSignalHandler()) { dv[QmlSignalHandlers].insert(n); } else if (fn->isQmlMethod() || fn->isJsMethod()) { if (fn->isAttached()) dv[QmlAttachedMethods].insert(n); else dv[QmlMethods].insert(n); } } } /*! Distributes a node \a n into the correct place in the summary section vector \a sv. Nodes that are sharing a comment are handled recursively - for recursion, the \a sharing parameter is set to \c true. */ void Sections::distributeQmlNodeInSummaryVector(SectionVector &sv, Node *n, bool sharing) { if (n->isSharingComment() && !sharing) return; if (n->isQmlProperty() || n->isJsProperty()) { QmlPropertyNode *pn = static_cast(n); if (pn->isAttached()) sv[QmlAttachedProperties].insert(pn); else sv[QmlProperties].insert(pn); } else if (n->isFunction()) { FunctionNode *fn = static_cast(n); if (fn->isQmlSignal() || fn->isJsSignal()) { if (fn->isAttached()) sv[QmlAttachedSignals].insert(fn); else sv[QmlSignals].insert(fn); } else if (fn->isQmlSignalHandler() || fn->isJsSignalHandler()) { sv[QmlSignalHandlers].insert(fn); } else if (fn->isQmlMethod() || fn->isJsMethod()) { if (fn->isAttached()) sv[QmlAttachedMethods].insert(fn); else sv[QmlMethods].insert(fn); } } else if (n->isSharedCommentNode()) { SharedCommentNode *scn = static_cast(n); if (scn->isPropertyGroup()) { sv[QmlProperties].insert(scn); } else { for (const auto &child : scn->collective()) distributeQmlNodeInSummaryVector(sv, child, true); } } } static void pushBaseClasses(QStack &stack, ClassNode *cn) { const QVector baseClasses = cn->baseClasses(); for (const auto &cls : baseClasses) { if (cls.node_) stack.prepend(cls.node_); } } /*! Build the section vectors for a standard reference page, when the aggregate node is a C++. */ void Sections::buildStdCppClassRefPageSections() { SectionVector &summarySections = stdCppClassSummarySections(); SectionVector &detailsSections = stdCppClassDetailsSections(); Section &allMembers = allMembersSection(); bool documentAll = true; if (aggregate_->parent() && !aggregate_->name().isEmpty() && !aggregate_->hasDoc()) documentAll = false; for (auto it = aggregate_->constBegin(); it != aggregate_->constEnd(); ++it) { Node *n = *it; if (!n->isPrivate() && !n->isProperty() && !n->isRelatedNonmember() && !n->isSharedCommentNode()) allMembers.insert(n); if (!documentAll && !n->hasDoc()) continue; distributeNodeInSummaryVector(summarySections, n); distributeNodeInDetailsVector(detailsSections, n); } if (!aggregate_->relatedByProxy().isEmpty()) { const QList relatedBy = aggregate_->relatedByProxy(); for (const auto &node : relatedBy) distributeNodeInSummaryVector(summarySections, node); } QStack stack; ClassNode *cn = static_cast(aggregate_); pushBaseClasses(stack, cn); while (!stack.isEmpty()) { ClassNode *cn = stack.pop(); for (auto it = cn->constBegin(); it != cn->constEnd(); ++it) { Node *n = *it; if (!n->isPrivate() && !n->isProperty() && !n->isRelatedNonmember() && !n->isSharedCommentNode()) allMembers.insert(n); if (!documentAll && !n->hasDoc()) continue; } pushBaseClasses(stack, cn); } reduce(summarySections); reduce(detailsSections); allMembers.reduce(); } /*! Build the section vectors for a standard reference page, when the aggregate node is a QML type. */ void Sections::buildStdQmlTypeRefPageSections() { ClassMap *classMap = nullptr; SectionVector &summarySections = stdQmlTypeSummarySections(); SectionVector &detailsSections = stdQmlTypeDetailsSections(); Section &allMembers = allMembersSection(); const Aggregate *qtn = aggregate_; while (qtn) { if (!qtn->isAbstract() || !classMap) classMap = allMembers.newClassMap(qtn); for (const auto n : qtn->childNodes()) { if (n->isInternal()) continue; if (!n->isSharedCommentNode() || n->isPropertyGroup()) allMembers.add(classMap, n); if (qtn == aggregate_ || qtn->isAbstract()) { distributeQmlNodeInSummaryVector(summarySections, n); distributeQmlNodeInDetailsVector(detailsSections, n); } } if (qtn->qmlBaseNode() == qtn) { qCDebug(lcQdoc, "error: circular type definition: '%s' inherits itself", qPrintable(qtn->name())); break; } qtn = static_cast(qtn->qmlBaseNode()); } reduce(summarySections); reduce(detailsSections); allMembers.reduce(); } /*! Returns true if any sections in this object contain obsolete members. If it returns false, then \a summary_spv and \a details_spv have not been modified. Otherwise, both vectors will contain pointers to the sections that contain obsolete members. */ bool Sections::hasObsoleteMembers(SectionPtrVector *summary_spv, SectionPtrVector *details_spv) const { const SectionVector *sections = nullptr; if (aggregate_->isClassNode()) sections = &stdCppClassSummarySections(); else if (aggregate_->isQmlType() || aggregate_->isQmlBasicType()) sections = &stdQmlTypeSummarySections(); else sections = &stdSummarySections(); for (const auto §ion : *sections) { if (!section.obsoleteMembers().isEmpty()) summary_spv->append(§ion); } if (aggregate_->isClassNode()) sections = &stdCppClassDetailsSections(); else if (aggregate_->isQmlType() || aggregate_->isQmlBasicType()) sections = &stdQmlTypeDetailsSections(); else sections = &stdDetailsSections(); for (const auto &it : *sections) { if (!it.obsoleteMembers().isEmpty()) details_spv->append(&it); } return !summary_spv->isEmpty(); } QT_END_NAMESPACE