diff options
author | Paul Wicking <paul.wicking@qt.io> | 2020-06-01 07:22:16 +0200 |
---|---|---|
committer | Paul Wicking <paul.wicking@qt.io> | 2020-06-04 13:09:25 +0200 |
commit | 04355fd6a33b4b0757ed167e6687560b705a2356 (patch) | |
tree | d39a8d3fac8ed324ee61bce5fc29c7e24d8f6027 | |
parent | c00431dd94cc069cd30af437e5546f157d9bfe10 (diff) |
QDoc: Extract Aggregate from Node
Task-number: QTBUG-84578
Change-Id: If28cc8b41401c90dfa48b613211b3d0a4dcd815a
Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
38 files changed, 1206 insertions, 1086 deletions
diff --git a/src/qdoc/.prev_CMakeLists.txt b/src/qdoc/.prev_CMakeLists.txt index 9f55b0e81..cb2056a2e 100644 --- a/src/qdoc/.prev_CMakeLists.txt +++ b/src/qdoc/.prev_CMakeLists.txt @@ -8,6 +8,7 @@ qt_add_tool(qdoc TARGET_DESCRIPTION "Qt Documentation Compiler" SOURCES access.h + aggregate.cpp aggregate.h atom.cpp atom.h clangcodeparser.cpp clangcodeparser.h classnode.cpp classnode.h diff --git a/src/qdoc/CMakeLists.txt b/src/qdoc/CMakeLists.txt index 9a199bcdc..0776e2c9e 100644 --- a/src/qdoc/CMakeLists.txt +++ b/src/qdoc/CMakeLists.txt @@ -9,6 +9,7 @@ qt_add_tool(qdoc TOOLS_TARGET Tools # special case SOURCES access.h + aggregate.cpp aggregate.h atom.cpp atom.h clangcodeparser.cpp clangcodeparser.h classnode.cpp classnode.h diff --git a/src/qdoc/aggregate.cpp b/src/qdoc/aggregate.cpp new file mode 100644 index 000000000..cac53cd1f --- /dev/null +++ b/src/qdoc/aggregate.cpp @@ -0,0 +1,1037 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "aggregate.h" + +#include "functionnode.h" +#include "parameters.h" +#include "typedefnode.h" +#include "qdocdatabase.h" +#include "qmlpropertynode.h" +#include "qmltypenode.h" +#include "sharedcommentnode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Aggregate + */ + +/*! \fn Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString &name) + The constructor should never be called directly. It is only called + by the constructors of subclasses of Aggregate. Those constructors + pass the node \a type they want to create, the \a parent of the new + node, and its \a name. + */ + +/*! + The destructor calls delete for each child of this Aggregate + that has this Aggregate as its parent. A child node that has + some other Aggregate as its parent is deleted by that + Aggregate's destructor. This is a fail-safe test. + + The destructor no longer deletes the collection of children + by calling qDeleteAll() because the child list can contain + pointers to children that have some other Aggregate as their + parent. This is because of how the \e{\\relates} command is + processed. An Aggregate can have a pointer to, for example, + a FunctionNode in its child list, but that FunctionNode has + a differen Aggregate as its parent because a \e{\\relates} + command was used to relate it to that parent. In that case, + the other Aggregate's destructor must delete that node. + + \note This function is the \b only place where delete is + called to delete any subclass of Node. + + \note This strategy depends on the node tree being destroyed + by calling delete on the root node of the tree. This happens + in the destructor of class Tree. + */ +Aggregate::~Aggregate() +{ + m_enumChildren.clear(); + m_nonfunctionMap.clear(); + m_functionMap.clear(); + for (int i = 0; i < m_children.size(); ++i) { + if ((m_children[i] != nullptr) && (m_children[i]->parent() == this)) + delete m_children[i]; + m_children[i] = nullptr; + } + m_children.clear(); +} + +/*! + If \a genus is \c{Node::DontCare}, find the first node in + this node's child list that has the given \a name. If this + node is a QML type, be sure to also look in the children + of its property group nodes. Return the matching node or 0. + + If \a genus is either \c{Node::CPP} or \c {Node::QML}, then + find all this node's children that have the given \a name, + and return the one that satisfies the \a genus requirement. + */ +Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findFlags) const +{ + if (genus == Node::DontCare) { + Node *node = m_nonfunctionMap.value(name); + if (node) + return node; + } else { + NodeList nodes = m_nonfunctionMap.values(name); + if (!nodes.isEmpty()) { + for (int i = 0; i < nodes.size(); ++i) { + Node *node = nodes.at(i); + if (genus == node->genus()) { + if (findFlags & TypesOnly) { + if (!node->isTypedef() && !node->isClassNode() && !node->isQmlType() + && !node->isQmlBasicType() && !node->isJsType() + && !node->isJsBasicType() && !node->isEnumType()) + continue; + } else if (findFlags & IgnoreModules && node->isModule()) + continue; + return node; + } + } + } + } + if (genus != Node::DontCare && this->genus() != genus) + return nullptr; + return m_functionMap.value(name); +} + +/*! + Find all the child nodes of this node that are named + \a name and return them in \a nodes. + */ +void Aggregate::findChildren(const QString &name, NodeVector &nodes) const +{ + nodes.clear(); + int nonfunctionCount = m_nonfunctionMap.count(name); + auto it = m_functionMap.find(name); + if (it != m_functionMap.end()) { + int functionCount = 0; + FunctionNode *fn = it.value(); + while (fn != nullptr) { + ++functionCount; + fn = fn->nextOverload(); + } + nodes.reserve(nonfunctionCount + functionCount); + fn = it.value(); + while (fn != nullptr) { + nodes.append(fn); + fn = fn->nextOverload(); + } + } else { + nodes.reserve(nonfunctionCount); + } + if (nonfunctionCount > 0) { + for (auto it = m_nonfunctionMap.find(name); + it != m_nonfunctionMap.end() && it.key() == name; ++it) { + nodes.append(it.value()); + } + } +} + +/*! + This function searches for a child node of this Aggregate, + such that the child node has the spacified \a name and the + function \a isMatch returns true for the node. The function + passed must be one of the isXxx() functions in class Node + that tests the node type. + */ +Node *Aggregate::findNonfunctionChild(const QString &name, bool (Node::*isMatch)() const) +{ + NodeList nodes = m_nonfunctionMap.values(name); + for (int i = 0; i < nodes.size(); ++i) { + Node *node = nodes.at(i); + if ((node->*(isMatch))()) + return node; + } + return nullptr; +} + +/*! + Find a function node that is a child of this node, such that + the function node has the specified \a name and \a parameters. + If \a parameters is empty but no matching function is found + that has no parameters, return the first non-internal primary + function or overload, whether it has parameters or not. + */ +FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters ¶meters) +{ + auto it = m_functionMap.find(name); + if (it == m_functionMap.end()) + return nullptr; + FunctionNode *fn = it.value(); + + if (parameters.isEmpty() && fn->parameters().isEmpty() && !fn->isInternal()) + return fn; + + while (fn != nullptr) { + if (parameters.count() == fn->parameters().count() && !fn->isInternal()) { + if (parameters.isEmpty()) + return fn; + bool matched = true; + for (int i = 0; i < parameters.count(); i++) { + if (parameters.at(i).type() != fn->parameters().at(i).type()) { + matched = false; + break; + } + } + if (matched) + return fn; + } + fn = fn->nextOverload(); + } + + if (parameters.isEmpty()) { + for (fn = it.value(); fn != nullptr; fn = fn->nextOverload()) + if (!fn->isInternal()) + return fn; + return it.value(); + } + return nullptr; +} + +/*! + Find the function node that is a child of this node, such + that the function described has the same name and signature + as the function described by the function node \a clone. + */ +FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone) +{ + FunctionNode *fn = m_functionMap.value(clone->name()); + while (fn != nullptr) { + if (isSameSignature(clone, fn)) + return fn; + fn = fn->nextOverload(); + } + return nullptr; +} + +/*! + Returns the list of keys from the primary function map. + */ +QStringList Aggregate::primaryKeys() +{ + return m_functionMap.keys(); +} + +/*! + Mark all child nodes that have no documentation as having + private access and internal status. qdoc will then ignore + them for documentation purposes. + */ +void Aggregate::markUndocumentedChildrenInternal() +{ + for (auto *child : qAsConst(m_children)) { + if (!child->isSharingComment() && !child->hasDoc() && !child->isDontDocument()) { + if (!child->docMustBeGenerated()) { + if (child->isFunction()) { + if (static_cast<FunctionNode *>(child)->hasAssociatedProperties()) + continue; + } else if (child->isTypedef()) { + if (static_cast<TypedefNode *>(child)->hasAssociatedEnum()) + continue; + } + child->setAccess(Access::Private); + child->setStatus(Node::Internal); + } + } + if (child->isAggregate()) { + static_cast<Aggregate *>(child)->markUndocumentedChildrenInternal(); + } + } +} + +/*! + This is where we set the overload numbers for function nodes. + \note Overload numbers for related non-members are handled + separately. + */ +void Aggregate::normalizeOverloads() +{ + /* + Ensure that none of the primary functions is inactive, private, + or marked \e {overload}. + */ + for (auto it = m_functionMap.begin(); it != m_functionMap.end(); ++it) { + FunctionNode *fn = it.value(); + if (fn->isOverload()) { + FunctionNode *primary = fn->findPrimaryFunction(); + if (primary) { + primary->setNextOverload(fn); + it.value() = primary; + fn = primary; + } else { + fn->clearOverloadFlag(); + } + } + int count = 0; + fn->setOverloadNumber(0); + FunctionNode *internalFn = nullptr; + while (fn != nullptr) { + FunctionNode *next = fn->nextOverload(); + if (next) { + if (next->isInternal()) { + // internal overloads are moved to a separate list + // and processed last + fn->setNextOverload(next->nextOverload()); + next->setNextOverload(internalFn); + internalFn = next; + } else { + next->setOverloadNumber(++count); + } + fn = fn->nextOverload(); + } else { + fn->setNextOverload(internalFn); + break; + } + } + while (internalFn) { + internalFn->setOverloadNumber(++count); + internalFn = internalFn->nextOverload(); + } + // process next function in function map. + } + /* + Recursive part. + */ + for (auto *node : qAsConst(m_children)) { + if (node->isAggregate()) + static_cast<Aggregate *>(node)->normalizeOverloads(); + } +} + +/*! + Returns a const reference to the list of child nodes of this + aggregate that are not function nodes. Duplicate nodes are + removed from the list. + */ +const NodeList &Aggregate::nonfunctionList() +{ + m_nonfunctionList = m_nonfunctionMap.values(); + std::sort(m_nonfunctionList.begin(), m_nonfunctionList.end(), Node::nodeNameLessThan); + m_nonfunctionList.erase(std::unique(m_nonfunctionList.begin(), m_nonfunctionList.end()), + m_nonfunctionList.end()); + return m_nonfunctionList; +} + +/*! \fn bool Aggregate::isAggregate() const + Returns \c true because this node is an instance of Aggregate, + which means it can have children. + */ + +/*! + Finds the enum type node that has \a enumValue as one of + its enum values and returns a pointer to it. Returns 0 if + no enum type node is found that has \a enumValue as one + of its values. + */ +const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const +{ + for (const auto *node : m_enumChildren) { + const EnumNode *en = static_cast<const EnumNode *>(node); + if (en->hasItem(enumValue)) + return en; + } + return nullptr; +} + +/*! + Appends \a includeFile file to the list of include files. + */ +void Aggregate::addIncludeFile(const QString &includeFile) +{ + m_includeFiles.append(includeFile); +} + +/*! + Sets the list of include files to \a includeFiles. + */ +void Aggregate::setIncludeFiles(const QStringList &includeFiles) +{ + m_includeFiles = includeFiles; +} + +/*! + Compare \a f1 to \a f2 and return \c true if they have the same + signature. Otherwise return \c false. They must have the same + number of parameters, and all the parameter types must be the + same. The functions must have the same constness and refness. + This is a private function. + */ +bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2) +{ + if (f1->parameters().count() != f2->parameters().count()) + return false; + if (f1->isConst() != f2->isConst()) + return false; + if (f1->isRef() != f2->isRef()) + return false; + if (f1->isRefRef() != f2->isRefRef()) + return false; + + const Parameters &p1 = f1->parameters(); + const Parameters &p2 = f2->parameters(); + for (int i = 0; i < p1.count(); i++) { + if (p1.at(i).hasType() && p2.at(i).hasType()) { + QString t1 = p1.at(i).type(); + QString t2 = p2.at(i).type(); + + if (t1.length() < t2.length()) + qSwap(t1, t2); + + /* + ### hack for C++ to handle superfluous + "Foo::" prefixes gracefully + */ + if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) { + // Accept a difference in the template parametters of the type if one + // is omited (eg. "QAtomicInteger" == "QAtomicInteger<T>") + auto ltLoc = t1.indexOf('<'); + auto gtLoc = t1.indexOf('>', ltLoc); + if (ltLoc < 0 || gtLoc < ltLoc) + return false; + t1.remove(ltLoc, gtLoc - ltLoc + 1); + if (t1 != t2) + return false; + } + } + } + return true; +} + +/*! + This function is only called by addChild(), when the child is a + FunctionNode. If the function map does not contain a function with + the name in \a fn, \a fn is inserted into the function map. If the + map already contains a function by that name, \a fn is appended to + that function's linked list of overloads. + + \note A function's overloads appear in the linked list in the same + order they were created. The first overload in the list is the first + overload created. This order is maintained in the numbering of + overloads. In other words, the first overload in the linked list has + overload number 1, and the last overload in the list has overload + number n, where n is the number of overloads not including the + function in the function map. + + \not Adding a function increments the aggregate's function count, + which is the total number of function nodes in the function map, + including the overloads. The overloads are not inserted into the map + but are in a linked list using the FunctionNode's m_nextOverload + pointer. + + \note The function's overload number and overload flag are set in + normalizeOverloads(). + + \note This is a private function. + + \sa normalizeOverloads() + */ +void Aggregate::addFunction(FunctionNode *fn) +{ + auto it = m_functionMap.find(fn->name()); + if (it == m_functionMap.end()) + m_functionMap.insert(fn->name(), fn); + else + it.value()->appendOverload(fn); + m_functionCount++; +} + +/*! + When an Aggregate adopts a function that is a child of + another Aggregate, the function is inserted into this + Aggregate's function map, if the function's name is not + already in the function map. If the function's name is + already in the function map, do nothing. The overload + link is already set correctly. + + \note This is a private function. + */ +void Aggregate::adoptFunction(FunctionNode *fn) +{ + auto it = m_functionMap.find(fn->name()); + if (it == m_functionMap.end()) + m_functionMap.insert(fn->name(), fn); + ++m_functionCount; +} + +/*! + Adds the \a child to this node's child map using \a title + as the key. The \a child is not added to the child list + again, because it is presumed to already be there. We just + want to be able to find the child by its \a title. + */ +void Aggregate::addChildByTitle(Node *child, const QString &title) +{ + m_nonfunctionMap.insert(title, child); +} + +/*! + Adds the \a child to this node's child list and sets the child's + parent pointer to this Aggregate. It then mounts the child with + mountChild(). + + The \a child is then added to this Aggregate's searchable maps + and lists. + + \note This function does not test the child's parent pointer + for null before changing it. If the child's parent pointer + is not null, then it is being reparented. The child becomes + a child of this Aggregate, but it also remains a child of + the Aggregate that is it's old parent. But the child will + only have one parent, and it will be this Aggregate. The is + because of the \c relates command. + + \sa mountChild(), dismountChild() + */ +void Aggregate::addChild(Node *child) +{ + m_children.append(child); + child->setParent(this); + child->setOutputSubdirectory(this->outputSubdirectory()); + child->setUrl(QString()); + child->setIndexNodeFlag(isIndexNode()); + + if (child->isFunction()) { + addFunction(static_cast<FunctionNode *>(child)); + } else if (!child->name().isEmpty()) { + m_nonfunctionMap.insert(child->name(), child); + if (child->isEnumType()) + m_enumChildren.append(child); + } +} + +/*! + This Aggregate becomes the adoptive parent of \a child. The + \a child knows this Aggregate as its parent, but its former + parent continues to have pointers to the child in its child + list and in its searchable data structures. But the child is + also added to the child list and searchable data structures + of this Aggregate. + + The one caveat is that if the child being adopted is a function + node, it's next overload pointer is not altered. + */ +void Aggregate::adoptChild(Node *child) +{ + if (child->parent() != this) { + m_children.append(child); + child->setParent(this); + if (child->isFunction()) { + adoptFunction(static_cast<FunctionNode *>(child)); + } else if (!child->name().isEmpty()) { + m_nonfunctionMap.insert(child->name(), child); + if (child->isEnumType()) + m_enumChildren.append(child); + } + if (child->isSharedCommentNode()) { + SharedCommentNode *scn = static_cast<SharedCommentNode *>(child); + for (Node *n : scn->collective()) + adoptChild(n); + } + } +} + +/*! + Recursively sets the output subdirectory for children + */ +void Aggregate::setOutputSubdirectory(const QString &t) +{ + Node::setOutputSubdirectory(t); + for (auto *node : qAsConst(m_children)) + node->setOutputSubdirectory(t); +} + +/*! + If this node has a child that is a QML property or JS property + named \a n, return a pointer to that child. Otherwise, return \nullptr. + */ +QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const +{ + NodeType goal = Node::QmlProperty; + if (isJsNode()) + goal = Node::JsProperty; + for (auto *child : qAsConst(m_children)) { + if (child->nodeType() == goal) { + if (child->name() == n) + return static_cast<QmlPropertyNode *>(child); + } + } + return nullptr; +} + +/*! + If this node has a child that is a QML property or JS property + named \a n and that also matches \a attached, return a pointer + to that child. + */ +QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) const +{ + NodeType goal = Node::QmlProperty; + if (isJsNode()) + goal = Node::JsProperty; + for (auto *child : qAsConst(m_children)) { + if (child->nodeType() == goal) { + if (child->name() == n && child->isAttached() == attached) + return static_cast<QmlPropertyNode *>(child); + } + } + return nullptr; +} + +/*! + The FunctionNode \a fn is assumed to be a member function + of this Aggregate. The function's name is looked up in the + Aggregate's function map. It should be found because it is + assumed that \a fn is in this Aggregate's function map. But + in case it is not found, \c false is returned. + + Normally, the name will be found in the function map, and + the value of the iterator is used to get the value, which + is a pointer to another FunctionNode, which is not itself + an overload. If that function has a non-null overload + pointer, true is returned. Otherwise false is returned. + + This is a convenience function that you should not need to + use. + */ +bool Aggregate::hasOverloads(const FunctionNode *fn) const +{ + auto it = m_functionMap.find(fn->name()); + return (it == m_functionMap.end() ? false : (it.value()->nextOverload() != nullptr)); +} + +/*! + Prints the inner node's list of children. + For debugging only. + */ +void Aggregate::printChildren(const QString &title) +{ + qDebug() << title << name() << m_children.size(); + if (m_children.size() > 0) { + for (int i = 0; i < m_children.size(); ++i) { + Node *n = m_children.at(i); + qDebug() << " CHILD:" << n->name() << n->nodeTypeString(); + } + } +} + +/*! + Removes \a fn from this aggregate's function map. That's + all it does. If \a fn is in the function map index and it + has an overload, the value pointer in the function map + index is set to the the overload pointer. If the function + has no overload pointer, the function map entry is erased. + + \note When removing a function node from the function map, + it is important to set the removed function node's next + overload pointer to null because the function node might + be added as a child to some other aggregate. + + \note This is a protected function. + */ +void Aggregate::removeFunctionNode(FunctionNode *fn) +{ + auto it = m_functionMap.find(fn->name()); + if (it != m_functionMap.end()) { + if (it.value() == fn) { + if (fn->nextOverload() != nullptr) { + it.value() = fn->nextOverload(); + fn->setNextOverload(nullptr); + fn->setOverloadNumber(0); + } else { + m_functionMap.erase(it); + } + } else { + FunctionNode *current = it.value(); + while (current != nullptr) { + if (current->nextOverload() == fn) { + current->setNextOverload(fn->nextOverload()); + fn->setNextOverload(nullptr); + fn->setOverloadNumber(0); + break; + } + current = current->nextOverload(); + } + } + } +} + +/* + When deciding whether to include a function in the function + index, if the function is marked private, don't include it. + If the function is marked obsolete, don't include it. If the + function is marked internal, don't include it. Or if the + function is a destructor or any kind of constructor, don't + include it. Otherwise include it. + */ +static bool keep(FunctionNode *fn) +{ + if (fn->isPrivate() || fn->isObsolete() || fn->isInternal() || fn->isSomeCtor() || fn->isDtor()) + return false; + return true; +} + +/*! + Insert all functions declared in this aggregate into the + \a functionIndex. Call the function recursively for each + child that is an aggregate. + + Only include functions that are in the public API and + that are not constructors or destructors. + */ +void Aggregate::findAllFunctions(NodeMapMap &functionIndex) +{ + for (auto it = m_functionMap.constBegin(); it != m_functionMap.constEnd(); ++it) { + FunctionNode *fn = it.value(); + if (keep(fn)) + functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn); + fn = fn->nextOverload(); + while (fn != nullptr) { + if (keep(fn)) + functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn); + fn = fn->nextOverload(); + } + } + for (Node *node : qAsConst(m_children)) { + if (node->isAggregate() && !node->isPrivate()) + static_cast<Aggregate *>(node)->findAllFunctions(functionIndex); + } +} + +/*! + For each child of this node, if the child is a namespace node, + insert the child into the \a namespaces multimap. If the child + is an aggregate, call this function recursively for that child. + + When the function called with the root node of a tree, it finds + all the namespace nodes in that tree and inserts them into the + \a namespaces multimap. + + The root node of a tree is a namespace, but it has no name, so + it is not inserted into the map. So, if this function is called + for each tree in the qdoc database, it finds all the namespace + nodes in the database. + */ +void Aggregate::findAllNamespaces(NodeMultiMap &namespaces) +{ + for (auto *node : qAsConst(m_children)) { + if (node->isAggregate() && !node->isPrivate()) { + if (node->isNamespace() && !node->name().isEmpty()) + namespaces.insert(node->name(), node); + static_cast<Aggregate *>(node)->findAllNamespaces(namespaces); + } + } +} + +/*! + Returns true if this aggregate contains at least one child + that is marked obsolete. Otherwise returns false. + */ +bool Aggregate::hasObsoleteMembers() const +{ + for (const auto *node : m_children) { + if (!node->isPrivate() && node->isObsolete()) { + if (node->isFunction() || node->isProperty() || node->isEnumType() || node->isTypedef() + || node->isTypeAlias() || node->isVariable() || node->isQmlProperty() + || node->isJsProperty()) + return true; + } + } + return false; +} + +/*! + Finds all the obsolete C++ classes and QML types in this + aggregate and all the C++ classes and QML types with obsolete + members, and inserts them into maps used elsewhere for + generating documentation. + */ +void Aggregate::findAllObsoleteThings() +{ + for (auto *node : qAsConst(m_children)) { + if (!node->isPrivate()) { + QString name = node->name(); + if (node->isObsolete()) { + if (node->isClassNode()) + QDocDatabase::obsoleteClasses().insert(node->qualifyCppName(), node); + else if (node->isQmlType() || node->isJsType()) + QDocDatabase::obsoleteQmlTypes().insert(node->qualifyQmlName(), node); + } else if (node->isClassNode()) { + Aggregate *a = static_cast<Aggregate *>(node); + if (a->hasObsoleteMembers()) + QDocDatabase::classesWithObsoleteMembers().insert(node->qualifyCppName(), node); + } else if (node->isQmlType() || node->isJsType()) { + Aggregate *a = static_cast<Aggregate *>(node); + if (a->hasObsoleteMembers()) + QDocDatabase::qmlTypesWithObsoleteMembers().insert(node->qualifyQmlName(), + node); + } else if (node->isAggregate()) { + static_cast<Aggregate *>(node)->findAllObsoleteThings(); + } + } + } +} + +/*! + Finds all the C++ classes, QML types, JS types, QML and JS + basic types, and examples in this aggregate and inserts them + into appropriate maps for later use in generating documentation. + */ +void Aggregate::findAllClasses() +{ + for (auto *node : qAsConst(m_children)) { + if (!node->isPrivate() && !node->isInternal() && !node->isDontDocument() + && node->tree()->camelCaseModuleName() != QString("QDoc")) { + if (node->isClassNode()) { + QDocDatabase::cppClasses().insert(node->qualifyCppName().toLower(), node); + } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType() + || node->isJsBasicType()) { + QString name = node->unqualifyQmlName(); + QDocDatabase::qmlTypes().insert(name, node); + // also add to the QML basic type map + if (node->isQmlBasicType() || node->isJsBasicType()) + QDocDatabase::qmlBasicTypes().insert(name, node); + } else if (node->isExample()) { + // use the module index title as key for the example map + QString title = node->tree()->indexTitle(); + if (!QDocDatabase::examples().contains(title, node)) + QDocDatabase::examples().insert(title, node); + } else if (node->isAggregate()) { + static_cast<Aggregate *>(node)->findAllClasses(); + } + } + } +} + +/*! + Find all the attribution pages in this node and insert them + into \a attributions. + */ +void Aggregate::findAllAttributions(NodeMultiMap &attributions) +{ + for (auto *node : qAsConst(m_children)) { + if (!node->isPrivate()) { + if (node->pageType() == Node::AttributionPage) + attributions.insert(node->tree()->indexTitle(), node); + else if (node->isAggregate()) + static_cast<Aggregate *>(node)->findAllAttributions(attributions); + } + } +} + +/*! + Finds all the nodes in this node where a \e{since} command appeared + in the qdoc comment and sorts them into maps according to the kind + of node. + + This function is used for generating the "New Classes... in x.y" + section on the \e{What's New in Qt x.y} page. + */ +void Aggregate::findAllSince() +{ + for (auto *node : qAsConst(m_children)) { + QString sinceString = node->since(); + // Insert a new entry into each map for each new since string found. + if (!node->isPrivate() && !sinceString.isEmpty()) { + auto nsmap = QDocDatabase::newSinceMaps().find(sinceString); + if (nsmap == QDocDatabase::newSinceMaps().end()) + nsmap = QDocDatabase::newSinceMaps().insert(sinceString, NodeMultiMap()); + + auto ncmap = QDocDatabase::newClassMaps().find(sinceString); + if (ncmap == QDocDatabase::newClassMaps().end()) + ncmap = QDocDatabase::newClassMaps().insert(sinceString, NodeMap()); + + auto nqcmap = QDocDatabase::newQmlTypeMaps().find(sinceString); + if (nqcmap == QDocDatabase::newQmlTypeMaps().end()) + nqcmap = QDocDatabase::newQmlTypeMaps().insert(sinceString, NodeMap()); + + if (node->isFunction()) { + // Insert functions into the general since map. + FunctionNode *fn = static_cast<FunctionNode *>(node); + if (!fn->isObsolete() && !fn->isSomeCtor() && !fn->isDtor()) + nsmap.value().insert(fn->name(), fn); + } else if (node->isClassNode()) { + // Insert classes into the since and class maps. + QString name = node->qualifyWithParentName(); + nsmap.value().insert(name, node); + ncmap.value().insert(name, node); + } else if (node->isQmlType() || node->isJsType()) { + // Insert QML elements into the since and element maps. + QString name = node->qualifyWithParentName(); + nsmap.value().insert(name, node); + nqcmap.value().insert(name, node); + } else if (node->isQmlProperty() || node->isJsProperty()) { + // Insert QML properties into the since map. + nsmap.value().insert(node->name(), node); + } else { + // Insert external documents into the general since map. + QString name = node->qualifyWithParentName(); + nsmap.value().insert(name, node); + } + } + // Recursively find child nodes with since commands. + if (node->isAggregate()) + static_cast<Aggregate *>(node)->findAllSince(); + } +} + +/*! + For each QML Type node in this aggregate's children, if the + QML type has a QML base type name but its QML base type node + pointer is nullptr, use the QML base type name to look up the + base type node. If the node is found, set the node's QML base + type node pointer to that node. + */ +void Aggregate::resolveQmlInheritance() +{ + NodeMap previousSearches; + // Do we need recursion? + for (auto *child : qAsConst(m_children)) { + if (!child->isQmlType() && !child->isJsType()) + continue; + QmlTypeNode *type = static_cast<QmlTypeNode *>(child); + if (type->qmlBaseNode() != nullptr) + continue; + if (type->qmlBaseName().isEmpty()) + continue; + QmlTypeNode *base = static_cast<QmlTypeNode *>(previousSearches.value(type->qmlBaseName())); + if (base && (base != type)) { + type->setQmlBaseNode(base); + QmlTypeNode::addInheritedBy(base, type); + } else { + if (!type->importList().isEmpty()) { + const ImportList &imports = type->importList(); + for (int i = 0; i < imports.size(); ++i) { + base = QDocDatabase::qdocDB()->findQmlType(imports[i], type->qmlBaseName()); + if (base && (base != type)) { + if (base->logicalModuleVersion()[0] != imports[i].m_majorMinorVersion[0]) + base = nullptr; // Safeguard for QTBUG-53529 + break; + } + } + } + if (base == nullptr) { + base = QDocDatabase::qdocDB()->findQmlType(QString(), type->qmlBaseName()); + } + if (base && (base != type)) { + type->setQmlBaseNode(base); + QmlTypeNode::addInheritedBy(base, type); + previousSearches.insert(type->qmlBaseName(), base); + } + } + } +} + +/*! + Returns a word representing the kind of Aggregate this node is. + Currently only works for class, struct, and union, but it can + easily be extended. If \a cap is true, the word is capitalised. + */ +QString Aggregate::typeWord(bool cap) const +{ + if (cap) { + switch (nodeType()) { + case Node::Class: + return QLatin1String("Class"); + case Node::Struct: + return QLatin1String("Struct"); + case Node::Union: + return QLatin1String("Union"); + default: + break; + } + } else { + switch (nodeType()) { + case Node::Class: + return QLatin1String("class"); + case Node::Struct: + return QLatin1String("struct"); + case Node::Union: + return QLatin1String("union"); + default: + break; + } + } + return QString(); +} + +/*! \fn int Aggregate::count() const + Returns the number of children in the child list. + */ + +/*! \fn const NodeList &Aggregate::childNodes() const + Returns a const reference to the child list. + */ + +/*! \fn NodeList::ConstIterator Aggregate::constBegin() const + Returns a const iterator pointing at the beginning of the child list. + */ + +/*! \fn NodeList::ConstIterator Aggregate::constEnd() const + Returns a const iterator pointing at the end of the child list. + */ + +/*! \fn const QStringList &Aggregate::includeFiles() const + This function returns a const reference to a list of strings, but + I no longer know what they are. + */ + +/*! \fn QmlTypeNode *Aggregate::qmlBaseNode() const + If this Aggregate is a QmlTypeNode, this function returns a pointer to + the QmlTypeNode that is its base type. Otherwise it returns \c nullptr. + A QmlTypeNode doesn't always have a base type, so even when this Aggregate + is aQmlTypeNode, the pointer returned can be \c nullptr. + */ + +/*! \fn FunctionMap &Aggregate::functionMap() + Returns a reference to this Aggregate's function map, which + is a map of all the children of this Aggregate that are + FunctionNodes. + */ + +/*! \fn void Aggregate::appendToRelatedByProxy(const NodeList &t) + Appends the list of node pointers to the list of elements that are + related to this Aggregate but are documented in a different module. + + \sa relatedByProxy() + */ + +/*! \fn NodeList &Aggregate::relatedByProxy() + Returns a reference to a list of node pointers where each element + points to a node in an index file for some other module, such that + whatever the node represents was documented in that other module, + but it is related to this Aggregate, so when the documentation for + this Aggregate is written, it will contain links to elements in the + other module. + */ + +QT_END_NAMESPACE diff --git a/src/qdoc/aggregate.h b/src/qdoc/aggregate.h new file mode 100644 index 000000000..7e0fb5fe3 --- /dev/null +++ b/src/qdoc/aggregate.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef AGGREGATE_H +#define AGGREGATE_H + +#include "node.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +class FunctionNode; +class QmlTypeNode; +class QmlPropertyNode; + +class Aggregate : public PageNode +{ +public: + Node *findChildNode(const QString &name, Node::Genus genus, int findFlags = 0) const; + Node *findNonfunctionChild(const QString &name, bool (Node::*)() const); + void findChildren(const QString &name, NodeVector &nodes) const; + FunctionNode *findFunctionChild(const QString &name, const Parameters ¶meters); + FunctionNode *findFunctionChild(const FunctionNode *clone); + + void normalizeOverloads(); + void markUndocumentedChildrenInternal(); + + bool isAggregate() const override { return true; } + const EnumNode *findEnumNodeForValue(const QString &enumValue) const; + + int count() const { return m_children.size(); } + const NodeList &childNodes() const { return m_children; } + const NodeList &nonfunctionList(); + NodeList::ConstIterator constBegin() const { return m_children.constBegin(); } + NodeList::ConstIterator constEnd() const { return m_children.constEnd(); } + + void addIncludeFile(const QString &includeFile); + void setIncludeFiles(const QStringList &includeFiles); + const QStringList &includeFiles() const { return m_includeFiles; } + + QStringList primaryKeys(); + QmlPropertyNode *hasQmlProperty(const QString &) const; + QmlPropertyNode *hasQmlProperty(const QString &, bool attached) const; + virtual QmlTypeNode *qmlBaseNode() const { return nullptr; } + void addChildByTitle(Node *child, const QString &title); + void printChildren(const QString &title); + void addChild(Node *child); + void adoptChild(Node *child); + void setOutputSubdirectory(const QString &t) override; + + FunctionMap &functionMap() { return m_functionMap; } + void findAllFunctions(NodeMapMap &functionIndex); + void findAllNamespaces(NodeMultiMap &namespaces); + void findAllAttributions(NodeMultiMap &attributions); + bool hasObsoleteMembers() const; + void findAllObsoleteThings(); + void findAllClasses(); + void findAllSince(); + void resolveQmlInheritance(); + bool hasOverloads(const FunctionNode *fn) const; + void appendToRelatedByProxy(const NodeList &t) { m_relatedByProxy.append(t); } + NodeList &relatedByProxy() { return m_relatedByProxy; } + QString typeWord(bool cap) const; + +protected: + Aggregate(NodeType type, Aggregate *parent, const QString &name) + : PageNode(type, parent, name), m_functionCount(0) + { + } + ~Aggregate() override; + void removeFunctionNode(FunctionNode *fn); + +private: + friend class Node; + void addFunction(FunctionNode *fn); + void adoptFunction(FunctionNode *fn); + static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2); + +protected: + NodeList m_children {}; + NodeList m_relatedByProxy {}; + int m_functionCount {}; + FunctionMap m_functionMap {}; + +private: + QStringList m_includeFiles {}; + NodeList m_enumChildren {}; + NodeMultiMap m_nonfunctionMap {}; + NodeList m_nonfunctionList {}; +}; + +QT_END_NAMESPACE + +#endif // AGGREGATE_H diff --git a/src/qdoc/classnode.h b/src/qdoc/classnode.h index 8252f0421..345c657fa 100644 --- a/src/qdoc/classnode.h +++ b/src/qdoc/classnode.h @@ -29,7 +29,7 @@ #ifndef CLASSNODE_H #define CLASSNODE_H -#include "node.h" +#include "aggregate.h" #include "relatedclass.h" #include "usingclause.h" diff --git a/src/qdoc/docbookgenerator.cpp b/src/qdoc/docbookgenerator.cpp index 74b1e0e34..16955b9e5 100644 --- a/src/qdoc/docbookgenerator.cpp +++ b/src/qdoc/docbookgenerator.cpp @@ -29,6 +29,7 @@ #include "docbookgenerator.h" #include "access.h" +#include "aggregate.h" #include "classnode.h" #include "codemarker.h" #include "collectionnode.h" diff --git a/src/qdoc/docbookgenerator.h b/src/qdoc/docbookgenerator.h index 4f0143abd..69dff804a 100644 --- a/src/qdoc/docbookgenerator.h +++ b/src/qdoc/docbookgenerator.h @@ -38,6 +38,7 @@ QT_BEGIN_NAMESPACE +class Aggregate; class ExampleNode; class FunctionNode; diff --git a/src/qdoc/enumnode.cpp b/src/qdoc/enumnode.cpp index ba3bb5083..2e4b82307 100644 --- a/src/qdoc/enumnode.cpp +++ b/src/qdoc/enumnode.cpp @@ -28,6 +28,7 @@ #include "enumnode.h" +#include "aggregate.h" #include "typedefnode.h" QT_BEGIN_NAMESPACE diff --git a/src/qdoc/functionnode.h b/src/qdoc/functionnode.h index b3518be27..a55955d71 100644 --- a/src/qdoc/functionnode.h +++ b/src/qdoc/functionnode.h @@ -29,6 +29,7 @@ #ifndef FUNCTIONNODE_H #define FUNCTIONNODE_H +#include "aggregate.h" #include "node.h" #include "parameters.h" diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp index e39b576f4..a99d632c2 100644 --- a/src/qdoc/generator.cpp +++ b/src/qdoc/generator.cpp @@ -32,6 +32,7 @@ #include "generator.h" #include "access.h" +#include "aggregate.h" #include "classnode.h" #include "codemarker.h" #include "collectionnode.h" diff --git a/src/qdoc/generator.h b/src/qdoc/generator.h index 1ce761829..79380d6c3 100644 --- a/src/qdoc/generator.h +++ b/src/qdoc/generator.h @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE typedef QMultiMap<QString, Node *> NodeMultiMap; typedef QMap<Node *, NodeMultiMap> ParentMaps; +class Aggregate; class CodeMarker; class ExampleNode; class FunctionNode; diff --git a/src/qdoc/headernode.cpp b/src/qdoc/headernode.cpp index 32da565d0..230b562a6 100644 --- a/src/qdoc/headernode.cpp +++ b/src/qdoc/headernode.cpp @@ -61,7 +61,7 @@ bool HeaderNode::docMustBeGenerated() const */ bool HeaderNode::hasDocumentedChildren() const { - for (const auto *node : qAsConst(children_)) { + for (const auto *node : qAsConst(m_children)) { if (node->isInAPI()) return true; } diff --git a/src/qdoc/headernode.h b/src/qdoc/headernode.h index cf6fb30f2..e50e23c55 100644 --- a/src/qdoc/headernode.h +++ b/src/qdoc/headernode.h @@ -29,7 +29,7 @@ #ifndef HEADERNODE_H #define HEADERNODE_H -#include "node.h" +#include "aggregate.h" #include <QtCore/qglobal.h> #include <QtCore/qstring.h> diff --git a/src/qdoc/helpprojectwriter.cpp b/src/qdoc/helpprojectwriter.cpp index d8e58f84d..d7861ce1b 100644 --- a/src/qdoc/helpprojectwriter.cpp +++ b/src/qdoc/helpprojectwriter.cpp @@ -29,6 +29,7 @@ #include "helpprojectwriter.h" #include "access.h" +#include "aggregate.h" #include "atom.h" #include "classnode.h" #include "collectionnode.h" diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp index 6a9ad978e..4189f3dab 100644 --- a/src/qdoc/htmlgenerator.cpp +++ b/src/qdoc/htmlgenerator.cpp @@ -29,6 +29,7 @@ #include "htmlgenerator.h" #include "access.h" +#include "aggregate.h" #include "classnode.h" #include "collectionnode.h" #include "config.h" diff --git a/src/qdoc/htmlgenerator.h b/src/qdoc/htmlgenerator.h index 783a6b0ed..49748b92b 100644 --- a/src/qdoc/htmlgenerator.h +++ b/src/qdoc/htmlgenerator.h @@ -38,6 +38,7 @@ QT_BEGIN_NAMESPACE +class Aggregate; class Config; class ExampleNode; class HelpProjectWriter; diff --git a/src/qdoc/namespacenode.cpp b/src/qdoc/namespacenode.cpp index 5a3d9b205..27e342f49 100644 --- a/src/qdoc/namespacenode.cpp +++ b/src/qdoc/namespacenode.cpp @@ -59,9 +59,9 @@ QT_BEGIN_NAMESPACE */ NamespaceNode::~NamespaceNode() { - for (int i = 0; i < children_.size(); ++i) { - if (children_[i]->parent() != this) - children_[i] = nullptr; + for (int i = 0; i < m_children.size(); ++i) { + if (m_children[i]->parent() != this) + m_children[i] = nullptr; } } @@ -84,7 +84,7 @@ bool NamespaceNode::isDocumentedHere() const */ bool NamespaceNode::hasDocumentedChildren() const { - for (const auto *node : qAsConst(children_)) { + for (const auto *node : qAsConst(m_children)) { if (node->isInAPI()) return true; } @@ -98,7 +98,7 @@ bool NamespaceNode::hasDocumentedChildren() const */ void NamespaceNode::reportDocumentedChildrenInUndocumentedNamespace() const { - for (const auto *node : qAsConst(children_)) { + for (const auto *node : qAsConst(m_children)) { if (node->isInAPI()) { QString msg1 = node->name(); if (node->isFunction()) diff --git a/src/qdoc/namespacenode.h b/src/qdoc/namespacenode.h index cfb7388f8..08e676339 100644 --- a/src/qdoc/namespacenode.h +++ b/src/qdoc/namespacenode.h @@ -29,7 +29,7 @@ #ifndef NAMESPACENODE_H #define NAMESPACENODE_H -#include "node.h" +#include "aggregate.h" #include <QtCore/qglobal.h> #include <QtCore/qstring.h> diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp index a528f0051..2d7ac4329 100644 --- a/src/qdoc/node.cpp +++ b/src/qdoc/node.cpp @@ -28,6 +28,7 @@ #include "node.h" +#include "aggregate.h" #include "codemarker.h" #include "config.h" #include "cppcodeparser.h" @@ -1925,1002 +1926,6 @@ QString Node::physicalModuleName() const */ /*! - \class Aggregate - */ - -/*! \fn Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString &name) - The constructor should never be called directly. It is only called - by the constructors of subclasses of Aggregate. Those constructors - pass the node \a type they want to create, the \a parent of the new - node, and its \a name. - */ - -/*! - The destructor calls delete for each child of this Aggregate - that has this Aggregate as its parent. A child node that has - some other Aggregate as its parent is deleted by that - Aggregate's destructor. This is a fail-safe test. - - The destructor no longer deletes the collection of children - by calling qDeleteAll() because the child list can contain - pointers to children that have some other Aggregate as their - parent. This is because of how the \e{\\relates} command is - processed. An Aggregate can have a pointer to, for example, - a FunctionNode in its child list, but that FunctionNode has - a differen Aggregate as its parent because a \e{\\relates} - command was used to relate it to that parent. In that case, - the other Aggregate's destructor must delete that node. - - \note This function is the \b only place where delete is - called to delete any subclass of Node. - - \note This strategy depends on the node tree being destroyed - by calling delete on the root node of the tree. This happens - in the destructor of class Tree. - */ -Aggregate::~Aggregate() -{ - enumChildren_.clear(); - nonfunctionMap_.clear(); - functionMap_.clear(); - for (int i = 0; i < children_.size(); ++i) { - if ((children_[i] != nullptr) && (children_[i]->parent() == this)) - delete children_[i]; - children_[i] = nullptr; - } - children_.clear(); -} - -/*! - If \a genus is \c{Node::DontCare}, find the first node in - this node's child list that has the given \a name. If this - node is a QML type, be sure to also look in the children - of its property group nodes. Return the matching node or 0. - - If \a genus is either \c{Node::CPP} or \c {Node::QML}, then - find all this node's children that have the given \a name, - and return the one that satisfies the \a genus requirement. - */ -Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findFlags) const -{ - if (genus == Node::DontCare) { - Node *node = nonfunctionMap_.value(name); - if (node) - return node; - } else { - NodeList nodes = nonfunctionMap_.values(name); - if (!nodes.isEmpty()) { - for (int i = 0; i < nodes.size(); ++i) { - Node *node = nodes.at(i); - if (genus == node->genus()) { - if (findFlags & TypesOnly) { - if (!node->isTypedef() && !node->isClassNode() && !node->isQmlType() - && !node->isQmlBasicType() && !node->isJsType() - && !node->isJsBasicType() && !node->isEnumType()) - continue; - } else if (findFlags & IgnoreModules && node->isModule()) - continue; - return node; - } - } - } - } - if (genus != Node::DontCare && this->genus() != genus) - return nullptr; - return functionMap_.value(name); -} - -/*! - Find all the child nodes of this node that are named - \a name and return them in \a nodes. - */ -void Aggregate::findChildren(const QString &name, NodeVector &nodes) const -{ - nodes.clear(); - int nonfunctionCount = nonfunctionMap_.count(name); - auto it = functionMap_.find(name); - if (it != functionMap_.end()) { - int functionCount = 0; - FunctionNode *fn = it.value(); - while (fn != nullptr) { - ++functionCount; - fn = fn->nextOverload(); - } - nodes.reserve(nonfunctionCount + functionCount); - fn = it.value(); - while (fn != nullptr) { - nodes.append(fn); - fn = fn->nextOverload(); - } - } else { - nodes.reserve(nonfunctionCount); - } - if (nonfunctionCount > 0) { - for (auto it = nonfunctionMap_.find(name); it != nonfunctionMap_.end() && it.key() == name; - ++it) { - nodes.append(it.value()); - } - } -} - -/*! - This function searches for a child node of this Aggregate, - such that the child node has the spacified \a name and the - function \a isMatch returns true for the node. The function - passed must be one of the isXxx() functions in class Node - that tests the node type. - */ -Node *Aggregate::findNonfunctionChild(const QString &name, bool (Node::*isMatch)() const) -{ - NodeList nodes = nonfunctionMap_.values(name); - for (int i = 0; i < nodes.size(); ++i) { - Node *node = nodes.at(i); - if ((node->*(isMatch))()) - return node; - } - return nullptr; -} - -/*! - Find a function node that is a child of this node, such that - the function node has the specified \a name and \a parameters. - If \a parameters is empty but no matching function is found - that has no parameters, return the first non-internal primary - function or overload, whether it has parameters or not. - */ -FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters ¶meters) -{ - auto it = functionMap_.find(name); - if (it == functionMap_.end()) - return nullptr; - FunctionNode *fn = it.value(); - - if (parameters.isEmpty() && fn->parameters().isEmpty() && !fn->isInternal()) - return fn; - - while (fn != nullptr) { - if (parameters.count() == fn->parameters().count() && !fn->isInternal()) { - if (parameters.isEmpty()) - return fn; - bool matched = true; - for (int i = 0; i < parameters.count(); i++) { - if (parameters.at(i).type() != fn->parameters().at(i).type()) { - matched = false; - break; - } - } - if (matched) - return fn; - } - fn = fn->nextOverload(); - } - - if (parameters.isEmpty()) { - for (fn = it.value(); fn != nullptr; fn = fn->nextOverload()) - if (!fn->isInternal()) - return fn; - return it.value(); - } - return nullptr; -} - -/*! - Find the function node that is a child of this node, such - that the function described has the same name and signature - as the function described by the function node \a clone. - */ -FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone) -{ - FunctionNode *fn = functionMap_.value(clone->name()); - while (fn != nullptr) { - if (isSameSignature(clone, fn)) - return fn; - fn = fn->nextOverload(); - } - return nullptr; -} - -/*! - Returns the list of keys from the primary function map. - */ -QStringList Aggregate::primaryKeys() -{ - return functionMap_.keys(); -} - -/*! - Mark all child nodes that have no documentation as having - private access and internal status. qdoc will then ignore - them for documentation purposes. - */ -void Aggregate::markUndocumentedChildrenInternal() -{ - for (auto *child : qAsConst(children_)) { - if (!child->isSharingComment() && !child->hasDoc() && !child->isDontDocument()) { - if (!child->docMustBeGenerated()) { - if (child->isFunction()) { - if (static_cast<FunctionNode *>(child)->hasAssociatedProperties()) - continue; - } else if (child->isTypedef()) { - if (static_cast<TypedefNode *>(child)->hasAssociatedEnum()) - continue; - } - child->setAccess(Access::Private); - child->setStatus(Node::Internal); - } - } - if (child->isAggregate()) { - static_cast<Aggregate *>(child)->markUndocumentedChildrenInternal(); - } - } -} - -/*! - This is where we set the overload numbers for function nodes. - \note Overload numbers for related non-members are handled - separately. - */ -void Aggregate::normalizeOverloads() -{ - /* - Ensure that none of the primary functions is inactive, private, - or marked \e {overload}. - */ - for (auto it = functionMap_.begin(); it != functionMap_.end(); ++it) { - FunctionNode *fn = it.value(); - if (fn->isOverload()) { - FunctionNode *primary = fn->findPrimaryFunction(); - if (primary) { - primary->setNextOverload(fn); - it.value() = primary; - fn = primary; - } else { - fn->clearOverloadFlag(); - } - } - int count = 0; - fn->setOverloadNumber(0); - FunctionNode *internalFn = nullptr; - while (fn != nullptr) { - FunctionNode *next = fn->nextOverload(); - if (next) { - if (next->isInternal()) { - // internal overloads are moved to a separate list - // and processed last - fn->setNextOverload(next->nextOverload()); - next->setNextOverload(internalFn); - internalFn = next; - } else { - next->setOverloadNumber(++count); - } - fn = fn->nextOverload(); - } else { - fn->setNextOverload(internalFn); - break; - } - } - while (internalFn) { - internalFn->setOverloadNumber(++count); - internalFn = internalFn->nextOverload(); - } - // process next function in function map. - } - /* - Recursive part. - */ - for (auto *node : qAsConst(children_)) { - if (node->isAggregate()) - static_cast<Aggregate *>(node)->normalizeOverloads(); - } -} - -/*! - Returns a const reference to the list of child nodes of this - aggregate that are not function nodes. Duplicate nodes are - removed from the list. - */ -const NodeList &Aggregate::nonfunctionList() -{ - nonfunctionList_ = nonfunctionMap_.values(); - std::sort(nonfunctionList_.begin(), nonfunctionList_.end(), Node::nodeNameLessThan); - nonfunctionList_.erase(std::unique(nonfunctionList_.begin(), nonfunctionList_.end()), - nonfunctionList_.end()); - return nonfunctionList_; -} - -/*! \fn bool Aggregate::isAggregate() const - Returns \c true because this node is an instance of Aggregate, - which means it can have children. - */ - -/*! - Finds the enum type node that has \a enumValue as one of - its enum values and returns a pointer to it. Returns 0 if - no enum type node is found that has \a enumValue as one - of its values. - */ -const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const -{ - for (const auto *node : enumChildren_) { - const EnumNode *en = static_cast<const EnumNode *>(node); - if (en->hasItem(enumValue)) - return en; - } - return nullptr; -} - -/*! - Appends \a includeFile file to the list of include files. - */ -void Aggregate::addIncludeFile(const QString &includeFile) -{ - includeFiles_.append(includeFile); -} - -/*! - Sets the list of include files to \a includeFiles. - */ -void Aggregate::setIncludeFiles(const QStringList &includeFiles) -{ - includeFiles_ = includeFiles; -} - -/*! - Compare \a f1 to \a f2 and return \c true if they have the same - signature. Otherwise return \c false. They must have the same - number of parameters, and all the parameter types must be the - same. The functions must have the same constness and refness. - This is a private function. - */ -bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2) -{ - if (f1->parameters().count() != f2->parameters().count()) - return false; - if (f1->isConst() != f2->isConst()) - return false; - if (f1->isRef() != f2->isRef()) - return false; - if (f1->isRefRef() != f2->isRefRef()) - return false; - - const Parameters &p1 = f1->parameters(); - const Parameters &p2 = f2->parameters(); - for (int i = 0; i < p1.count(); i++) { - if (p1.at(i).hasType() && p2.at(i).hasType()) { - QString t1 = p1.at(i).type(); - QString t2 = p2.at(i).type(); - - if (t1.length() < t2.length()) - qSwap(t1, t2); - - /* - ### hack for C++ to handle superfluous - "Foo::" prefixes gracefully - */ - if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) { - // Accept a difference in the template parametters of the type if one - // is omited (eg. "QAtomicInteger" == "QAtomicInteger<T>") - auto ltLoc = t1.indexOf('<'); - auto gtLoc = t1.indexOf('>', ltLoc); - if (ltLoc < 0 || gtLoc < ltLoc) - return false; - t1.remove(ltLoc, gtLoc - ltLoc + 1); - if (t1 != t2) - return false; - } - } - } - return true; -} - -/*! - This function is only called by addChild(), when the child is a - FunctionNode. If the function map does not contain a function with - the name in \a fn, \a fn is inserted into the function map. If the - map already contains a function by that name, \a fn is appended to - that function's linked list of overloads. - - \note A function's overloads appear in the linked list in the same - order they were created. The first overload in the list is the first - overload created. This order is maintained in the numbering of - overloads. In other words, the first overload in the linked list has - overload number 1, and the last overload in the list has overload - number n, where n is the number of overloads not including the - function in the function map. - - \not Adding a function increments the aggregate's function count, - which is the total number of function nodes in the function map, - including the overloads. The overloads are not inserted into the map - but are in a linked list using the FunctionNode's m_nextOverload - pointer. - - \note The function's overload number and overload flag are set in - normalizeOverloads(). - - \note This is a private function. - - \sa normalizeOverloads() - */ -void Aggregate::addFunction(FunctionNode *fn) -{ - auto it = functionMap_.find(fn->name()); - if (it == functionMap_.end()) - functionMap_.insert(fn->name(), fn); - else - it.value()->appendOverload(fn); - functionCount_++; -} - -/*! - When an Aggregate adopts a function that is a child of - another Aggregate, the function is inserted into this - Aggregate's function map, if the function's name is not - already in the function map. If the function's name is - already in the function map, do nothing. The overload - link is already set correctly. - - \note This is a private function. - */ -void Aggregate::adoptFunction(FunctionNode *fn) -{ - auto it = functionMap_.find(fn->name()); - if (it == functionMap_.end()) - functionMap_.insert(fn->name(), fn); - ++functionCount_; -} - -/*! - Adds the \a child to this node's child map using \a title - as the key. The \a child is not added to the child list - again, because it is presumed to already be there. We just - want to be able to find the child by its \a title. - */ -void Aggregate::addChildByTitle(Node *child, const QString &title) -{ - nonfunctionMap_.insert(title, child); -} - -/*! - Adds the \a child to this node's child list and sets the child's - parent pointer to this Aggregate. It then mounts the child with - mountChild(). - - The \a child is then added to this Aggregate's searchable maps - and lists. - - \note This function does not test the child's parent pointer - for null before changing it. If the child's parent pointer - is not null, then it is being reparented. The child becomes - a child of this Aggregate, but it also remains a child of - the Aggregate that is it's old parent. But the child will - only have one parent, and it will be this Aggregate. The is - because of the \c relates command. - - \sa mountChild(), dismountChild() - */ -void Aggregate::addChild(Node *child) -{ - children_.append(child); - child->setParent(this); - child->setOutputSubdirectory(this->outputSubdirectory()); - child->setUrl(QString()); - child->setIndexNodeFlag(isIndexNode()); - - if (child->isFunction()) { - addFunction(static_cast<FunctionNode *>(child)); - } else if (!child->name().isEmpty()) { - nonfunctionMap_.insert(child->name(), child); - if (child->isEnumType()) - enumChildren_.append(child); - } -} - -/*! - This Aggregate becomes the adoptive parent of \a child. The - \a child knows this Aggregate as its parent, but its former - parent continues to have pointers to the child in its child - list and in its searchable data structures. But the child is - also added to the child list and searchable data structures - of this Aggregate. - - The one caveat is that if the child being adopted is a function - node, it's next overload pointer is not altered. - */ -void Aggregate::adoptChild(Node *child) -{ - if (child->parent() != this) { - children_.append(child); - child->setParent(this); - if (child->isFunction()) { - adoptFunction(static_cast<FunctionNode *>(child)); - } else if (!child->name().isEmpty()) { - nonfunctionMap_.insert(child->name(), child); - if (child->isEnumType()) - enumChildren_.append(child); - } - if (child->isSharedCommentNode()) { - SharedCommentNode *scn = static_cast<SharedCommentNode *>(child); - for (Node *n : scn->collective()) - adoptChild(n); - } - } -} - -/*! - Recursively sets the output subdirectory for children - */ -void Aggregate::setOutputSubdirectory(const QString &t) -{ - Node::setOutputSubdirectory(t); - for (auto *node : qAsConst(children_)) - node->setOutputSubdirectory(t); -} - -/*! - If this node has a child that is a QML property or JS property - named \a n, return a pointer to that child. Otherwise, return \nullptr. - */ -QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const -{ - NodeType goal = Node::QmlProperty; - if (isJsNode()) - goal = Node::JsProperty; - for (auto *child : qAsConst(children_)) { - if (child->nodeType() == goal) { - if (child->name() == n) - return static_cast<QmlPropertyNode *>(child); - } - } - return nullptr; -} - -/*! - If this node has a child that is a QML property or JS property - named \a n and that also matches \a attached, return a pointer - to that child. - */ -QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) const -{ - NodeType goal = Node::QmlProperty; - if (isJsNode()) - goal = Node::JsProperty; - for (auto *child : qAsConst(children_)) { - if (child->nodeType() == goal) { - if (child->name() == n && child->isAttached() == attached) - return static_cast<QmlPropertyNode *>(child); - } - } - return nullptr; -} - -/*! - The FunctionNode \a fn is assumed to be a member function - of this Aggregate. The function's name is looked up in the - Aggregate's function map. It should be found because it is - assumed that \a fn is in this Aggregate's function map. But - in case it is not found, \c false is returned. - - Normally, the name will be found in the function map, and - the value of the iterator is used to get the value, which - is a pointer to another FunctionNode, which is not itself - an overload. If that function has a non-null overload - pointer, true is returned. Otherwise false is returned. - - This is a convenience function that you should not need to - use. - */ -bool Aggregate::hasOverloads(const FunctionNode *fn) const -{ - auto it = functionMap_.find(fn->name()); - return (it == functionMap_.end() ? false : (it.value()->nextOverload() != nullptr)); -} - -/*! - Prints the inner node's list of children. - For debugging only. - */ -void Aggregate::printChildren(const QString &title) -{ - qDebug() << title << name() << children_.size(); - if (children_.size() > 0) { - for (int i = 0; i < children_.size(); ++i) { - Node *n = children_.at(i); - qDebug() << " CHILD:" << n->name() << n->nodeTypeString(); - } - } -} - -/*! - Removes \a fn from this aggregate's function map. That's - all it does. If \a fn is in the function map index and it - has an overload, the value pointer in the function map - index is set to the the overload pointer. If the function - has no overload pointer, the function map entry is erased. - - \note When removing a function node from the function map, - it is important to set the removed function node's next - overload pointer to null because the function node might - be added as a child to some other aggregate. - - \note This is a protected function. - */ -void Aggregate::removeFunctionNode(FunctionNode *fn) -{ - auto it = functionMap_.find(fn->name()); - if (it != functionMap_.end()) { - if (it.value() == fn) { - if (fn->nextOverload() != nullptr) { - it.value() = fn->nextOverload(); - fn->setNextOverload(nullptr); - fn->setOverloadNumber(0); - } else { - functionMap_.erase(it); - } - } else { - FunctionNode *current = it.value(); - while (current != nullptr) { - if (current->nextOverload() == fn) { - current->setNextOverload(fn->nextOverload()); - fn->setNextOverload(nullptr); - fn->setOverloadNumber(0); - break; - } - current = current->nextOverload(); - } - } - } -} - -/* - When deciding whether to include a function in the function - index, if the function is marked private, don't include it. - If the function is marked obsolete, don't include it. If the - function is marked internal, don't include it. Or if the - function is a destructor or any kind of constructor, don't - include it. Otherwise include it. - */ -static bool keep(FunctionNode *fn) -{ - if (fn->isPrivate() || fn->isObsolete() || fn->isInternal() || fn->isSomeCtor() || fn->isDtor()) - return false; - return true; -} - -/*! - Insert all functions declared in this aggregate into the - \a functionIndex. Call the function recursively for each - child that is an aggregate. - - Only include functions that are in the public API and - that are not constructors or destructors. - */ -void Aggregate::findAllFunctions(NodeMapMap &functionIndex) -{ - for (auto it = functionMap_.constBegin(); it != functionMap_.constEnd(); ++it) { - FunctionNode *fn = it.value(); - if (keep(fn)) - functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn); - fn = fn->nextOverload(); - while (fn != nullptr) { - if (keep(fn)) - functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn); - fn = fn->nextOverload(); - } - } - for (Node *node : qAsConst(children_)) { - if (node->isAggregate() && !node->isPrivate()) - static_cast<Aggregate *>(node)->findAllFunctions(functionIndex); - } -} - -/*! - For each child of this node, if the child is a namespace node, - insert the child into the \a namespaces multimap. If the child - is an aggregate, call this function recursively for that child. - - When the function called with the root node of a tree, it finds - all the namespace nodes in that tree and inserts them into the - \a namespaces multimap. - - The root node of a tree is a namespace, but it has no name, so - it is not inserted into the map. So, if this function is called - for each tree in the qdoc database, it finds all the namespace - nodes in the database. - */ -void Aggregate::findAllNamespaces(NodeMultiMap &namespaces) -{ - for (auto *node : qAsConst(children_)) { - if (node->isAggregate() && !node->isPrivate()) { - if (node->isNamespace() && !node->name().isEmpty()) - namespaces.insert(node->name(), node); - static_cast<Aggregate *>(node)->findAllNamespaces(namespaces); - } - } -} - -/*! - Returns true if this aggregate contains at least one child - that is marked obsolete. Otherwise returns false. - */ -bool Aggregate::hasObsoleteMembers() const -{ - for (const auto *node : children_) { - if (!node->isPrivate() && node->isObsolete()) { - if (node->isFunction() || node->isProperty() || node->isEnumType() || node->isTypedef() - || node->isTypeAlias() || node->isVariable() || node->isQmlProperty() - || node->isJsProperty()) - return true; - } - } - return false; -} - -/*! - Finds all the obsolete C++ classes and QML types in this - aggregate and all the C++ classes and QML types with obsolete - members, and inserts them into maps used elsewhere for - generating documentation. - */ -void Aggregate::findAllObsoleteThings() -{ - for (auto *node : qAsConst(children_)) { - if (!node->isPrivate()) { - QString name = node->name(); - if (node->isObsolete()) { - if (node->isClassNode()) - QDocDatabase::obsoleteClasses().insert(node->qualifyCppName(), node); - else if (node->isQmlType() || node->isJsType()) - QDocDatabase::obsoleteQmlTypes().insert(node->qualifyQmlName(), node); - } else if (node->isClassNode()) { - Aggregate *a = static_cast<Aggregate *>(node); - if (a->hasObsoleteMembers()) - QDocDatabase::classesWithObsoleteMembers().insert(node->qualifyCppName(), node); - } else if (node->isQmlType() || node->isJsType()) { - Aggregate *a = static_cast<Aggregate *>(node); - if (a->hasObsoleteMembers()) - QDocDatabase::qmlTypesWithObsoleteMembers().insert(node->qualifyQmlName(), - node); - } else if (node->isAggregate()) { - static_cast<Aggregate *>(node)->findAllObsoleteThings(); - } - } - } -} - -/*! - Finds all the C++ classes, QML types, JS types, QML and JS - basic types, and examples in this aggregate and inserts them - into appropriate maps for later use in generating documentation. - */ -void Aggregate::findAllClasses() -{ - for (auto *node : qAsConst(children_)) { - if (!node->isPrivate() && !node->isInternal() && !node->isDontDocument() - && node->tree()->camelCaseModuleName() != QString("QDoc")) { - if (node->isClassNode()) { - QDocDatabase::cppClasses().insert(node->qualifyCppName().toLower(), node); - } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType() - || node->isJsBasicType()) { - QString name = node->unqualifyQmlName(); - QDocDatabase::qmlTypes().insert(name, node); - // also add to the QML basic type map - if (node->isQmlBasicType() || node->isJsBasicType()) - QDocDatabase::qmlBasicTypes().insert(name, node); - } else if (node->isExample()) { - // use the module index title as key for the example map - QString title = node->tree()->indexTitle(); - if (!QDocDatabase::examples().contains(title, node)) - QDocDatabase::examples().insert(title, node); - } else if (node->isAggregate()) { - static_cast<Aggregate *>(node)->findAllClasses(); - } - } - } -} - -/*! - Find all the attribution pages in this node and insert them - into \a attributions. - */ -void Aggregate::findAllAttributions(NodeMultiMap &attributions) -{ - for (auto *node : qAsConst(children_)) { - if (!node->isPrivate()) { - if (node->pageType() == Node::AttributionPage) - attributions.insert(node->tree()->indexTitle(), node); - else if (node->isAggregate()) - static_cast<Aggregate *>(node)->findAllAttributions(attributions); - } - } -} - -/*! - Finds all the nodes in this node where a \e{since} command appeared - in the qdoc comment and sorts them into maps according to the kind - of node. - - This function is used for generating the "New Classes... in x.y" - section on the \e{What's New in Qt x.y} page. - */ -void Aggregate::findAllSince() -{ - for (auto *node : qAsConst(children_)) { - QString sinceString = node->since(); - // Insert a new entry into each map for each new since string found. - if (!node->isPrivate() && !sinceString.isEmpty()) { - auto nsmap = QDocDatabase::newSinceMaps().find(sinceString); - if (nsmap == QDocDatabase::newSinceMaps().end()) - nsmap = QDocDatabase::newSinceMaps().insert(sinceString, NodeMultiMap()); - - auto ncmap = QDocDatabase::newClassMaps().find(sinceString); - if (ncmap == QDocDatabase::newClassMaps().end()) - ncmap = QDocDatabase::newClassMaps().insert(sinceString, NodeMap()); - - auto nqcmap = QDocDatabase::newQmlTypeMaps().find(sinceString); - if (nqcmap == QDocDatabase::newQmlTypeMaps().end()) - nqcmap = QDocDatabase::newQmlTypeMaps().insert(sinceString, NodeMap()); - - if (node->isFunction()) { - // Insert functions into the general since map. - FunctionNode *fn = static_cast<FunctionNode *>(node); - if (!fn->isObsolete() && !fn->isSomeCtor() && !fn->isDtor()) - nsmap.value().insert(fn->name(), fn); - } else if (node->isClassNode()) { - // Insert classes into the since and class maps. - QString name = node->qualifyWithParentName(); - nsmap.value().insert(name, node); - ncmap.value().insert(name, node); - } else if (node->isQmlType() || node->isJsType()) { - // Insert QML elements into the since and element maps. - QString name = node->qualifyWithParentName(); - nsmap.value().insert(name, node); - nqcmap.value().insert(name, node); - } else if (node->isQmlProperty() || node->isJsProperty()) { - // Insert QML properties into the since map. - nsmap.value().insert(node->name(), node); - } else { - // Insert external documents into the general since map. - QString name = node->qualifyWithParentName(); - nsmap.value().insert(name, node); - } - } - // Recursively find child nodes with since commands. - if (node->isAggregate()) - static_cast<Aggregate *>(node)->findAllSince(); - } -} - -/*! - For each QML Type node in this aggregate's children, if the - QML type has a QML base type name but its QML base type node - pointer is nullptr, use the QML base type name to look up the - base type node. If the node is found, set the node's QML base - type node pointer to that node. - */ -void Aggregate::resolveQmlInheritance() -{ - NodeMap previousSearches; - // Do we need recursion? - for (auto *child : qAsConst(children_)) { - if (!child->isQmlType() && !child->isJsType()) - continue; - QmlTypeNode *type = static_cast<QmlTypeNode *>(child); - if (type->qmlBaseNode() != nullptr) - continue; - if (type->qmlBaseName().isEmpty()) - continue; - QmlTypeNode *base = static_cast<QmlTypeNode *>(previousSearches.value(type->qmlBaseName())); - if (base && (base != type)) { - type->setQmlBaseNode(base); - QmlTypeNode::addInheritedBy(base, type); - } else { - if (!type->importList().isEmpty()) { - const ImportList &imports = type->importList(); - for (int i = 0; i < imports.size(); ++i) { - base = QDocDatabase::qdocDB()->findQmlType(imports[i], type->qmlBaseName()); - if (base && (base != type)) { - if (base->logicalModuleVersion()[0] != imports[i].m_majorMinorVersion[0]) - base = nullptr; // Safeguard for QTBUG-53529 - break; - } - } - } - if (base == nullptr) { - base = QDocDatabase::qdocDB()->findQmlType(QString(), type->qmlBaseName()); - } - if (base && (base != type)) { - type->setQmlBaseNode(base); - QmlTypeNode::addInheritedBy(base, type); - previousSearches.insert(type->qmlBaseName(), base); - } - } - } -} - -/*! - Returns a word representing the kind of Aggregate this node is. - Currently only works for class, struct, and union, but it can - easily be extended. If \a cap is true, the word is capitalised. - */ -QString Aggregate::typeWord(bool cap) const -{ - if (cap) { - switch (nodeType()) { - case Node::Class: - return QLatin1String("Class"); - case Node::Struct: - return QLatin1String("Struct"); - case Node::Union: - return QLatin1String("Union"); - default: - break; - } - } else { - switch (nodeType()) { - case Node::Class: - return QLatin1String("class"); - case Node::Struct: - return QLatin1String("struct"); - case Node::Union: - return QLatin1String("union"); - default: - break; - } - } - return QString(); -} - -/*! \fn int Aggregate::count() const - Returns the number of children in the child list. - */ - -/*! \fn const NodeList &Aggregate::childNodes() const - Returns a const reference to the child list. - */ - -/*! \fn NodeList::ConstIterator Aggregate::constBegin() const - Returns a const iterator pointing at the beginning of the child list. - */ - -/*! \fn NodeList::ConstIterator Aggregate::constEnd() const - Returns a const iterator pointing at the end of the child list. - */ - -/*! \fn const QStringList &Aggregate::includeFiles() const - This function returns a const reference to a list of strings, but - I no longer know what they are. - */ - -/*! \fn QmlTypeNode *Aggregate::qmlBaseNode() const - If this Aggregate is a QmlTypeNode, this function returns a pointer to - the QmlTypeNode that is its base type. Otherwise it returns \c nullptr. - A QmlTypeNode doesn't always have a base type, so even when this Aggregate - is aQmlTypeNode, the pointer returned can be \c nullptr. - */ - -/*! \fn FunctionMap &Aggregate::functionMap() - Returns a reference to this Aggregate's function map, which - is a map of all the children of this Aggregate that are - FunctionNodes. - */ - -/*! \fn void Aggregate::appendToRelatedByProxy(const NodeList &t) - Appends the list of node pointers to the list of elements that are - related to this Aggregate but are documented in a different module. - - \sa relatedByProxy() - */ - -/*! \fn NodeList &Aggregate::relatedByProxy() - Returns a reference to a list of node pointers where each element - points to a node in an index file for some other module, such that - whatever the node represents was documented in that other module, - but it is related to this Aggregate, so when the documentation for - this Aggregate is written, it will contain links to elements in the - other module. - */ - -/*! \class PageNode \brief A PageNode is a Node that generates a documentation page. diff --git a/src/qdoc/node.h b/src/qdoc/node.h index 3d7d021a1..898a12dc2 100644 --- a/src/qdoc/node.h +++ b/src/qdoc/node.h @@ -451,84 +451,6 @@ protected: QStringList groupNames_; }; -class Aggregate : public PageNode -{ -public: - Node *findChildNode(const QString &name, Node::Genus genus, int findFlags = 0) const; - Node *findNonfunctionChild(const QString &name, bool (Node::*)() const); - void findChildren(const QString &name, NodeVector &nodes) const; - FunctionNode *findFunctionChild(const QString &name, const Parameters ¶meters); - FunctionNode *findFunctionChild(const FunctionNode *clone); - - void normalizeOverloads(); - void markUndocumentedChildrenInternal(); - - bool isAggregate() const override { return true; } - const EnumNode *findEnumNodeForValue(const QString &enumValue) const; - - int count() const { return children_.size(); } - const NodeList &childNodes() const { return children_; } - const NodeList &nonfunctionList(); - NodeList::ConstIterator constBegin() const { return children_.constBegin(); } - NodeList::ConstIterator constEnd() const { return children_.constEnd(); } - - void addIncludeFile(const QString &includeFile); - void setIncludeFiles(const QStringList &includeFiles); - const QStringList &includeFiles() const { return includeFiles_; } - - QStringList primaryKeys(); - QmlPropertyNode *hasQmlProperty(const QString &) const; - QmlPropertyNode *hasQmlProperty(const QString &, bool attached) const; - virtual QmlTypeNode *qmlBaseNode() const { return nullptr; } - void addChildByTitle(Node *child, const QString &title); - void printChildren(const QString &title); - void addChild(Node *child); - void adoptChild(Node *child); - void setOutputSubdirectory(const QString &t) override; - - FunctionMap &functionMap() { return functionMap_; } - void findAllFunctions(NodeMapMap &functionIndex); - void findAllNamespaces(NodeMultiMap &namespaces); - void findAllAttributions(NodeMultiMap &attributions); - bool hasObsoleteMembers() const; - void findAllObsoleteThings(); - void findAllClasses(); - void findAllSince(); - void resolveQmlInheritance(); - bool hasOverloads(const FunctionNode *fn) const; - void appendToRelatedByProxy(const NodeList &t) { relatedByProxy_.append(t); } - NodeList &relatedByProxy() { return relatedByProxy_; } - QString typeWord(bool cap) const; - -protected: - Aggregate(NodeType type, Aggregate *parent, const QString &name) - : PageNode(type, parent, name), functionCount_(0) - { - } - ~Aggregate() override; - void removeFunctionNode(FunctionNode *fn); - -private: - friend class Node; - void addFunction(FunctionNode *fn); - void adoptFunction(FunctionNode *fn); - static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2); - -protected: - NodeList children_; - NodeList relatedByProxy_; - -private: - QStringList includeFiles_; - NodeList enumChildren_; - NodeMultiMap nonfunctionMap_; - NodeList nonfunctionList_; - -protected: - int functionCount_; - FunctionMap functionMap_; -}; - QT_END_NAMESPACE #endif diff --git a/src/qdoc/propertynode.cpp b/src/qdoc/propertynode.cpp index f6667c05b..824ad851a 100644 --- a/src/qdoc/propertynode.cpp +++ b/src/qdoc/propertynode.cpp @@ -28,6 +28,8 @@ #include "propertynode.h" +#include "aggregate.h" + QT_BEGIN_NAMESPACE /*! diff --git a/src/qdoc/propertynode.h b/src/qdoc/propertynode.h index 3caf38dee..d6ee33547 100644 --- a/src/qdoc/propertynode.h +++ b/src/qdoc/propertynode.h @@ -37,6 +37,8 @@ QT_BEGIN_NAMESPACE +class Aggregate; + class PropertyNode : public Node { public: diff --git a/src/qdoc/proxynode.h b/src/qdoc/proxynode.h index 634025598..4cfcb8d0a 100644 --- a/src/qdoc/proxynode.h +++ b/src/qdoc/proxynode.h @@ -29,7 +29,7 @@ #ifndef PROXYNODE_H #define PROXYNODE_H -#include "node.h" +#include "aggregate.h" #include <QtCore/qglobal.h> diff --git a/src/qdoc/qdoc.pro b/src/qdoc/qdoc.pro index e085b6903..9e36809bc 100644 --- a/src/qdoc/qdoc.pro +++ b/src/qdoc/qdoc.pro @@ -31,6 +31,7 @@ win32-icc*|win32-msvc*:{ } HEADERS += access.h \ + aggregate.h \ atom.h \ clangcodeparser.h \ classnode.h \ @@ -91,7 +92,8 @@ HEADERS += access.h \ webxmlgenerator.h \ xmlgenerator.h -SOURCES += atom.cpp \ +SOURCES += aggregate.cpp \ + atom.cpp \ clangcodeparser.cpp \ classnode.cpp \ codechunk.cpp \ diff --git a/src/qdoc/qdoctagfiles.cpp b/src/qdoc/qdoctagfiles.cpp index 550be97a9..874fe187a 100644 --- a/src/qdoc/qdoctagfiles.cpp +++ b/src/qdoc/qdoctagfiles.cpp @@ -29,6 +29,7 @@ #include "qdoctagfiles.h" #include "access.h" +#include "aggregate.h" #include "classnode.h" #include "enumnode.h" #include "functionnode.h" diff --git a/src/qdoc/qmlpropertynode.h b/src/qdoc/qmlpropertynode.h index 2ab9d8bc5..b86346a9c 100644 --- a/src/qdoc/qmlpropertynode.h +++ b/src/qdoc/qmlpropertynode.h @@ -29,6 +29,7 @@ #ifndef QMLPROPERTYNODE_H #define QMLPROPERTYNODE_H +#include "aggregate.h" #include "node.h" #include <QtCore/qglobal.h> diff --git a/src/qdoc/qmltypenode.h b/src/qdoc/qmltypenode.h index 8dc153ccb..ab124e1e6 100644 --- a/src/qdoc/qmltypenode.h +++ b/src/qdoc/qmltypenode.h @@ -30,7 +30,7 @@ #define QMLTYPENODE_H #include "importrec.h" -#include "node.h" +#include "aggregate.h" #include <QtCore/qglobal.h> #include <QtCore/qstring.h> diff --git a/src/qdoc/qmlvisitor.cpp b/src/qdoc/qmlvisitor.cpp index 00aff56ab..158531444 100644 --- a/src/qdoc/qmlvisitor.cpp +++ b/src/qdoc/qmlvisitor.cpp @@ -28,6 +28,7 @@ #include "qmlvisitor.h" +#include "aggregate.h" #include "codechunk.h" #include "codeparser.h" #include "functionnode.h" diff --git a/src/qdoc/qmlvisitor.h b/src/qdoc/qmlvisitor.h index 6d24b50a3..578de7997 100644 --- a/src/qdoc/qmlvisitor.h +++ b/src/qdoc/qmlvisitor.h @@ -50,6 +50,8 @@ namespace QQmlJS { # endif #endif +class Aggregate; + struct QmlPropArgs { QString type_; diff --git a/src/qdoc/sections.cpp b/src/qdoc/sections.cpp index 81a3603fb..b786f5abe 100644 --- a/src/qdoc/sections.cpp +++ b/src/qdoc/sections.cpp @@ -28,6 +28,7 @@ #include "sections.h" +#include "aggregate.h" #include "classnode.h" #include "config.h" #include "enumnode.h" diff --git a/src/qdoc/sections.h b/src/qdoc/sections.h index dfcaaed7b..82c7e3d31 100644 --- a/src/qdoc/sections.h +++ b/src/qdoc/sections.h @@ -35,6 +35,8 @@ QT_BEGIN_NAMESPACE +class Aggregate; + typedef QMultiMap<QString, Node *> MemberMap; // the string is the member signature typedef QPair<const QmlTypeNode *, MemberMap> ClassMap; // the node is the QML type typedef QVector<ClassMap *> ClassMapList; diff --git a/src/qdoc/sharedcommentnode.cpp b/src/qdoc/sharedcommentnode.cpp index fc512fe25..e1190adaa 100644 --- a/src/qdoc/sharedcommentnode.cpp +++ b/src/qdoc/sharedcommentnode.cpp @@ -28,6 +28,7 @@ #include "sharedcommentnode.h" +#include "aggregate.h" #include "functionnode.h" #include "qmltypenode.h" diff --git a/src/qdoc/sharedcommentnode.h b/src/qdoc/sharedcommentnode.h index ccb9f2c83..e7f786b4d 100644 --- a/src/qdoc/sharedcommentnode.h +++ b/src/qdoc/sharedcommentnode.h @@ -36,6 +36,7 @@ QT_BEGIN_NAMESPACE +class Aggregate; class QmlTypeNode; class SharedCommentNode : public Node diff --git a/src/qdoc/typedefnode.cpp b/src/qdoc/typedefnode.cpp index e2d1eb2c2..6046a9aaa 100644 --- a/src/qdoc/typedefnode.cpp +++ b/src/qdoc/typedefnode.cpp @@ -28,6 +28,8 @@ #include "typedefnode.h" +#include "aggregate.h" + QT_BEGIN_NAMESPACE /*! diff --git a/src/qdoc/typedefnode.h b/src/qdoc/typedefnode.h index 3cebccaa1..308f62a3a 100644 --- a/src/qdoc/typedefnode.h +++ b/src/qdoc/typedefnode.h @@ -37,6 +37,8 @@ QT_BEGIN_NAMESPACE +class Aggregate; + class TypedefNode : public Node { public: diff --git a/src/qdoc/variablenode.h b/src/qdoc/variablenode.h index bdc36bcad..a197c33a6 100644 --- a/src/qdoc/variablenode.h +++ b/src/qdoc/variablenode.h @@ -29,6 +29,7 @@ #ifndef VARIABLENODE_H #define VARIABLENODE_H +#include "aggregate.h" #include "node.h" #include <QtCore/qglobal.h> diff --git a/src/qdoc/webxmlgenerator.cpp b/src/qdoc/webxmlgenerator.cpp index b23758260..1b3e00e88 100644 --- a/src/qdoc/webxmlgenerator.cpp +++ b/src/qdoc/webxmlgenerator.cpp @@ -28,6 +28,7 @@ #include "webxmlgenerator.h" +#include "aggregate.h" #include "collectionnode.h" #include "config.h" #include "helpprojectwriter.h" diff --git a/src/qdoc/webxmlgenerator.h b/src/qdoc/webxmlgenerator.h index f8b3fa80e..9aaabf39c 100644 --- a/src/qdoc/webxmlgenerator.h +++ b/src/qdoc/webxmlgenerator.h @@ -38,6 +38,8 @@ QT_BEGIN_NAMESPACE +class Aggregate; + class WebXMLGenerator : public HtmlGenerator, public IndexSectionWriter { public: |