1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "aggregate.h"
5
6#include "functionnode.h"
7#include "parameters.h"
8#include "typedefnode.h"
9#include "qdocdatabase.h"
10#include "qmlpropertynode.h"
11#include "qmltypenode.h"
12#include "sharedcommentnode.h"
13#include <vector>
14
15using namespace Qt::Literals::StringLiterals;
16
17QT_BEGIN_NAMESPACE
18
19/*!
20 \class Aggregate
21 */
22
23/*! \fn Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString &name)
24 The constructor should never be called directly. It is only called
25 by the constructors of subclasses of Aggregate. Those constructors
26 pass the node \a type they want to create, the \a parent of the new
27 node, and its \a name.
28 */
29
30/*!
31 Recursively set all non-related members in the list of children to
32 \nullptr, after which each aggregate can safely delete all children
33 in their list. Aggregate's destructor calls this only on the root
34 namespace node.
35 */
36void Aggregate::dropNonRelatedMembers()
37{
38 for (auto &child : m_children) {
39 if (!child)
40 continue;
41 if (child->parent() != this)
42 child = nullptr;
43 else if (child->isAggregate())
44 static_cast<Aggregate*>(child)->dropNonRelatedMembers();
45 }
46}
47
48/*!
49 Destroys this Aggregate; deletes each child.
50 */
51Aggregate::~Aggregate()
52{
53 // If this is the root, clear non-related children first
54 if (isNamespace() && name().isEmpty())
55 dropNonRelatedMembers();
56
57 m_enumChildren.clear();
58 m_nonfunctionMap.clear();
59 m_functionMap.clear();
60 qDeleteAll(begin: m_children.begin(), end: m_children.end());
61 m_children.clear();
62}
63
64/*!
65 If \a genus is \c{Node::DontCare}, find the first node in
66 this node's child list that has the given \a name. If this
67 node is a QML type, be sure to also look in the children
68 of its property group nodes. Return the matching node or \c nullptr.
69
70 If \a genus is either \c{Node::CPP} or \c {Node::QML}, then
71 find all this node's children that have the given \a name,
72 and return the one that satisfies the \a genus requirement.
73 */
74Node *Aggregate::findChildNode(const QString &name, Genus genus, int findFlags) const
75{
76 if (genus == Genus::DontCare) {
77 Node *node = m_nonfunctionMap.value(key: name);
78 if (node)
79 return node;
80 } else {
81 const NodeList &nodes = m_nonfunctionMap.values(key: name);
82 for (auto *node : nodes) {
83 if (hasCommonGenusType(searchMask: genus, candidate: node->genus())) {
84 if (findFlags & TypesOnly) {
85 if (!node->isTypedef() && !node->isClassNode()
86 && !node->isQmlType() && !node->isEnumType())
87 continue;
88 } else if (findFlags & IgnoreModules && node->isModule())
89 continue;
90 return node;
91 }
92 }
93 }
94 if (genus != Genus::DontCare && !(hasCommonGenusType(searchMask: genus, candidate: this->genus())))
95 return nullptr;
96
97 if (findFlags & TypesOnly)
98 return nullptr;
99
100 auto it = m_functionMap.find(key: name);
101 return it != m_functionMap.end() ? (*(*it).begin()) : nullptr;
102}
103
104/*!
105 Find all the child nodes of this node that are named
106 \a name and return them in \a nodes.
107 */
108void Aggregate::findChildren(const QString &name, NodeVector &nodes) const
109{
110 nodes.clear();
111 const auto &functions = m_functionMap.value(key: name);
112 nodes.reserve(asize: functions.size() + m_nonfunctionMap.count(key: name));
113 for (auto f : functions)
114 nodes.emplace_back(args&: f);
115 auto [it, end] = m_nonfunctionMap.equal_range(akey: name);
116 while (it != end) {
117 nodes.emplace_back(args: *it);
118 ++it;
119 }
120}
121
122/*!
123 This function searches for a child node of this Aggregate,
124 such that the child node has the spacified \a name and the
125 function \a isMatch returns true for the node. The function
126 passed must be one of the isXxx() functions in class Node
127 that tests the node type.
128 */
129Node *Aggregate::findNonfunctionChild(const QString &name, bool (Node::*isMatch)() const)
130{
131 const NodeList &nodes = m_nonfunctionMap.values(key: name);
132 for (auto *node : nodes) {
133 if ((node->*(isMatch))())
134 return node;
135 }
136 return nullptr;
137}
138
139/*!
140 Find a function node that is a child of this node, such that
141 the function node has the specified \a name and \a parameters.
142 If \a parameters is empty but no matching function is found
143 that has no parameters, return the first non-internal primary
144 function or overload, whether it has parameters or not.
145
146 \sa normalizeOverloads()
147 */
148FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters &parameters)
149{
150 auto map_it = m_functionMap.find(key: name);
151 if (map_it == m_functionMap.end())
152 return nullptr;
153
154 auto match_it = std::find_if(first: (*map_it).begin(), last: (*map_it).end(),
155 pred: [&parameters](const FunctionNode *fn) {
156 if (fn->isInternal())
157 return false;
158 if (parameters.count() != fn->parameters().count())
159 return false;
160 for (int i = 0; i < parameters.count(); ++i)
161 if (parameters.at(i).type() != fn->parameters().at(i).type())
162 return false;
163 return true;
164 });
165
166 if (match_it != (*map_it).end())
167 return *match_it;
168
169 // If no exact match was found and parameters are empty (e.g., from \overload command),
170 // try to find the best available function to link to.
171 if (parameters.isEmpty()) {
172 // First, try to find a non-deprecated, non-internal function
173 auto best_it = std::find_if(first: (*map_it).begin(), last: (*map_it).end(),
174 pred: [](const FunctionNode *fn) {
175 return !fn->isInternal() && !fn->isDeprecated();
176 });
177
178 if (best_it != (*map_it).end())
179 return *best_it;
180
181 // If no non-deprecated function found, fall back to any non-internal function
182 auto fallback_it = std::find_if(first: (*map_it).begin(), last: (*map_it).end(),
183 pred: [](const FunctionNode *fn) {
184 return !fn->isInternal();
185 });
186
187 if (fallback_it != (*map_it).end())
188 return *fallback_it;
189 }
190
191 return nullptr;
192}
193
194/*!
195 Returns the function node that is a child of this node, such
196 that the function described has the same name and signature
197 as the function described by the function node \a clone.
198
199 Returns \nullptr if no matching function was found.
200 */
201FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone)
202{
203 auto funcs_it = m_functionMap.find(key: clone->name());
204 if (funcs_it == m_functionMap.end())
205 return nullptr;
206
207 auto func_it = std::find_if(first: (*funcs_it).begin(), last: (*funcs_it).end(),
208 pred: [clone](const FunctionNode *fn) {
209 return compare(f1: clone, f2: fn) == 0;
210 });
211
212 return func_it != (*funcs_it).end() ? *func_it : nullptr;
213}
214
215/*!
216 Mark all child nodes that have no documentation as having
217 private access and internal status. qdoc will then ignore
218 them for documentation purposes.
219 */
220void Aggregate::markUndocumentedChildrenInternal()
221{
222 for (auto *child : std::as_const(t&: m_children)) {
223 if (!child->hasDoc() && !child->isDontDocument()) {
224 if (!child->docMustBeGenerated()) {
225 if (child->isFunction()) {
226 if (static_cast<FunctionNode *>(child)->hasAssociatedProperties())
227 continue;
228 } else if (child->isTypedef()) {
229 if (static_cast<TypedefNode *>(child)->hasAssociatedEnum())
230 continue;
231 }
232 child->setAccess(Access::Private);
233 child->setStatus(Node::Internal);
234 }
235 }
236 if (child->isAggregate()) {
237 static_cast<Aggregate *>(child)->markUndocumentedChildrenInternal();
238 }
239 }
240}
241
242/*!
243 Adopts each non-aggregate C++ node (function/macro, typedef, enum, variable,
244 or a shared comment node with genus Node::CPP) to the aggregate specified in
245 the node's documentation using the \\relates command.
246
247 If the target Aggregate is not found in the primary tree, creates a new
248 ProxyNode to use as the parent.
249*/
250void Aggregate::resolveRelates()
251{
252 auto *database = QDocDatabase::qdocDB();
253
254 for (auto *node : m_children) {
255 if (node->isRelatedNonmember())
256 continue;
257 if (node->genus() != Genus::CPP)
258 continue;
259
260 if (!node->isAggregate()) {
261 const auto &relates_args = node->doc().metaCommandArgs(metaCommand: "relates"_L1);
262 if (relates_args.isEmpty())
263 continue;
264
265 auto *aggregate = database->findRelatesNode(path: relates_args[0].first.split(sep: "::"_L1));
266 if (!aggregate)
267 aggregate = new ProxyNode(this, relates_args[0].first);
268 else if (node->parent() == aggregate)
269 continue;
270
271 aggregate->adoptChild(child: node);
272 node->setRelatedNonmember(true);
273 } else {
274 static_cast<Aggregate*>(node)->resolveRelates();
275 }
276 }
277}
278
279/*!
280 Sorts the lists of overloads in the function map and assigns overload
281 numbers.
282
283 For sorting, active functions take precedence over internal ones, as well
284 as ones marked as \\overload - the latter ones typically do not contain
285 full documentation, so selecting them as the \e primary function
286 would cause unnecessary warnings to be generated.
287
288 Otherwise, the order is set as determined by FunctionNode::compare().
289 */
290void Aggregate::normalizeOverloads()
291{
292 for (auto &map_it : m_functionMap) {
293 if (map_it.size() == 1) {
294 map_it.front()->setOverloadNumber(0);
295 } else if (map_it.size() > 1) {
296 std::sort(first: map_it.begin(), last: map_it.end(),
297 comp: [](const FunctionNode *f1, const FunctionNode *f2) -> bool {
298 if (f1->isInternal() != f2->isInternal())
299 return f2->isInternal();
300 if (f1->isOverload() != f2->isOverload())
301 return f2->isOverload();
302 // Prioritize documented over undocumented
303 if (f1->hasDoc() != f2->hasDoc())
304 return f1->hasDoc();
305 return (compare(f1, f2) < 0);
306 });
307 // Set overload numbers only if the functions are documented.
308 // They are not visible if undocumented.
309 signed short n{0};
310 for (auto *fn : map_it) {
311 if (fn->hasDoc())
312 fn->setOverloadNumber(n++);
313 }
314 }
315 }
316
317 for (auto *node : std::as_const(t&: m_children)) {
318 if (node->isAggregate())
319 static_cast<Aggregate *>(node)->normalizeOverloads();
320 }
321}
322
323/*!
324 Returns a const reference to the list of child nodes of this
325 aggregate that are not function nodes. Duplicate nodes are
326 removed from the list and the list is sorted.
327 */
328const NodeList &Aggregate::nonfunctionList()
329{
330 m_nonfunctionList = m_nonfunctionMap.values();
331 // Sort based on node name
332 std::sort(first: m_nonfunctionList.begin(), last: m_nonfunctionList.end(), comp: Node::nodeNameLessThan);
333 // Erase duplicates
334 m_nonfunctionList.erase(abegin: std::unique(first: m_nonfunctionList.begin(), last: m_nonfunctionList.end()),
335 aend: m_nonfunctionList.end());
336 return m_nonfunctionList;
337}
338
339/*! \fn bool Aggregate::isAggregate() const
340 Returns \c true because this node is an instance of Aggregate,
341 which means it can have children.
342 */
343
344/*!
345 Finds the enum type node that has \a enumValue as one of
346 its enum values and returns a pointer to it. Returns 0 if
347 no enum type node is found that has \a enumValue as one
348 of its values.
349 */
350const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const
351{
352 for (const auto *node : m_enumChildren) {
353 const auto *en = static_cast<const EnumNode *>(node);
354 if (en->hasItem(name: enumValue))
355 return en;
356 }
357 return nullptr;
358}
359
360/*!
361 Adds the \a child to this node's child map using \a title
362 as the key. The \a child is not added to the child list
363 again, because it is presumed to already be there. We just
364 want to be able to find the child by its \a title.
365 */
366void Aggregate::addChildByTitle(Node *child, const QString &title)
367{
368 m_nonfunctionMap.insert(key: title, value: child);
369}
370
371/*!
372 Adds the \a child to this node's child list and sets the child's
373 parent pointer to this Aggregate. It then mounts the child with
374 mountChild().
375
376 The \a child is then added to this Aggregate's searchable maps
377 and lists.
378
379 \note This function does not test the child's parent pointer
380 for null before changing it. If the child's parent pointer
381 is not null, then it is being reparented. The child becomes
382 a child of this Aggregate, but it also remains a child of
383 the Aggregate that is it's old parent. But the child will
384 only have one parent, and it will be this Aggregate. The is
385 because of the \c relates command.
386
387 \sa mountChild(), dismountChild()
388 */
389void Aggregate::addChild(Node *child)
390{
391 m_children.append(t: child);
392 child->setParent(this);
393 child->setUrl(QString());
394 child->setIndexNodeFlag(isIndexNode());
395
396 if (child->isFunction()) {
397 m_functionMap[child->name()].emplace_back(args: static_cast<FunctionNode *>(child));
398 } else if (!child->name().isEmpty()) {
399 m_nonfunctionMap.insert(key: child->name(), value: child);
400 if (child->isEnumType())
401 m_enumChildren.append(t: child);
402 }
403}
404
405/*!
406 This Aggregate becomes the adoptive parent of \a child. The
407 \a child knows this Aggregate as its parent, but its former
408 parent continues to have pointers to the child in its child
409 list and in its searchable data structures. But the child is
410 also added to the child list and searchable data structures
411 of this Aggregate.
412 */
413void Aggregate::adoptChild(Node *child)
414{
415 if (child->parent() != this) {
416 m_children.append(t: child);
417 child->setParent(this);
418 if (child->isFunction()) {
419 m_functionMap[child->name()].emplace_back(args: static_cast<FunctionNode *>(child));
420 } else if (!child->name().isEmpty()) {
421 m_nonfunctionMap.insert(key: child->name(), value: child);
422 if (child->isEnumType())
423 m_enumChildren.append(t: child);
424 }
425 if (child->isSharedCommentNode()) {
426 auto *scn = static_cast<SharedCommentNode *>(child);
427 for (Node *n : scn->collective())
428 adoptChild(child: n);
429 }
430 }
431}
432
433/*!
434 If this node has a child that is a QML property named \a n, return a
435 pointer to that child. Otherwise, return \nullptr.
436 */
437QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const
438{
439 NodeType goal = NodeType::QmlProperty;
440 for (auto *child : std::as_const(t: m_children)) {
441 if (child->nodeType() == goal) {
442 if (child->name() == n)
443 return static_cast<QmlPropertyNode *>(child);
444 }
445 }
446 return nullptr;
447}
448
449/*!
450 If this node has a child that is a QML property named \a n and that
451 also matches \a attached, return a pointer to that child.
452 */
453QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) const
454{
455 NodeType goal = NodeType::QmlProperty;
456 for (auto *child : std::as_const(t: m_children)) {
457 if (child->nodeType() == goal) {
458 if (child->name() == n && child->isAttached() == attached)
459 return static_cast<QmlPropertyNode *>(child);
460 }
461 }
462 return nullptr;
463}
464
465/*!
466 Returns \c true if this aggregate has multiple function
467 overloads matching the name of \a fn.
468
469 \note Assumes \a fn is a member of this aggregate.
470*/
471bool Aggregate::hasOverloads(const FunctionNode *fn) const
472{
473 auto it = m_functionMap.find(key: fn->name());
474 return !(it == m_functionMap.end()) && (it.value().size() > 1);
475}
476
477/*
478 When deciding whether to include a function in the function
479 index, if the function is marked private, don't include it.
480 If the function is marked obsolete, don't include it. If the
481 function is marked internal, don't include it. Or if the
482 function is a destructor or any kind of constructor, don't
483 include it. Otherwise include it.
484 */
485static bool keep(FunctionNode *fn)
486{
487 if (fn->isPrivate() || fn->isDeprecated() || fn->isInternal() || fn->isSomeCtor() || fn->isDtor())
488 return false;
489 return true;
490}
491
492/*!
493 Insert all functions declared in this aggregate into the
494 \a functionIndex. Call the function recursively for each
495 child that is an aggregate.
496
497 Only include functions that are in the public API and
498 that are not constructors or destructors.
499 */
500void Aggregate::findAllFunctions(NodeMapMap &functionIndex)
501{
502 for (auto functions : m_functionMap) {
503 std::for_each(first: functions.begin(), last: functions.end(),
504 f: [&functionIndex](FunctionNode *fn) {
505 if (keep(fn))
506 functionIndex[fn->name()].insert(key: fn->parent()->fullDocumentName(), value: fn);
507 }
508 );
509 }
510
511 for (Node *node : std::as_const(t&: m_children)) {
512 if (node->isAggregate() && !node->isPrivate() && !node->isDontDocument())
513 static_cast<Aggregate *>(node)->findAllFunctions(functionIndex);
514 }
515}
516
517/*!
518 For each child of this node, if the child is a namespace node,
519 insert the child into the \a namespaces multimap. If the child
520 is an aggregate, call this function recursively for that child.
521
522 When the function called with the root node of a tree, it finds
523 all the namespace nodes in that tree and inserts them into the
524 \a namespaces multimap.
525
526 The root node of a tree is a namespace, but it has no name, so
527 it is not inserted into the map. So, if this function is called
528 for each tree in the qdoc database, it finds all the namespace
529 nodes in the database.
530 */
531void Aggregate::findAllNamespaces(NodeMultiMap &namespaces)
532{
533 for (auto *node : std::as_const(t&: m_children)) {
534 if (node->isAggregate() && !node->isPrivate()) {
535 if (node->isNamespace() && !node->name().isEmpty())
536 namespaces.insert(key: node->name(), value: node);
537 static_cast<Aggregate *>(node)->findAllNamespaces(namespaces);
538 }
539 }
540}
541
542/*!
543 Returns true if this aggregate contains at least one child
544 that is marked obsolete. Otherwise returns false.
545 */
546bool Aggregate::hasObsoleteMembers() const
547{
548 for (const auto *node : m_children)
549 if (!node->isPrivate() && node->isDeprecated()) {
550 if (node->isFunction() || node->isProperty() || node->isEnumType() || node->isTypedef()
551 || node->isTypeAlias() || node->isVariable() || node->isQmlProperty())
552 return true;
553 }
554 return false;
555}
556
557/*!
558 Finds all the obsolete C++ classes and QML types in this
559 aggregate and all the C++ classes and QML types with obsolete
560 members, and inserts them into maps used elsewhere for
561 generating documentation.
562 */
563void Aggregate::findAllObsoleteThings()
564{
565 for (auto *node : std::as_const(t&: m_children)) {
566 if (!node->isPrivate()) {
567 if (node->isDeprecated()) {
568 if (node->isClassNode())
569 QDocDatabase::obsoleteClasses().insert(key: node->qualifyCppName(), value: node);
570 else if (node->isQmlType())
571 QDocDatabase::obsoleteQmlTypes().insert(key: node->qualifyQmlName(), value: node);
572 } else if (node->isClassNode()) {
573 auto *a = static_cast<Aggregate *>(node);
574 if (a->hasObsoleteMembers())
575 QDocDatabase::classesWithObsoleteMembers().insert(key: node->qualifyCppName(), value: node);
576 } else if (node->isQmlType()) {
577 auto *a = static_cast<Aggregate *>(node);
578 if (a->hasObsoleteMembers())
579 QDocDatabase::qmlTypesWithObsoleteMembers().insert(key: node->qualifyQmlName(),
580 value: node);
581 } else if (node->isAggregate()) {
582 static_cast<Aggregate *>(node)->findAllObsoleteThings();
583 }
584 }
585 }
586}
587
588/*!
589 Finds all the C++ classes, QML types, QML basic types, and examples
590 in this aggregate and inserts them into appropriate maps for later
591 use in generating documentation.
592 */
593void Aggregate::findAllClasses()
594{
595 for (auto *node : std::as_const(t&: m_children)) {
596 if (!node->isPrivate() && !node->isInternal() && !node->isDontDocument()
597 && node->tree()->camelCaseModuleName() != QString("QDoc")) {
598 if (node->isClassNode()) {
599 QDocDatabase::cppClasses().insert(key: node->qualifyCppName().toLower(), value: node);
600 } else if (node->isQmlType()) {
601 QString name = node->name().toLower();
602 QDocDatabase::qmlTypes().insert(key: name, value: node);
603 // also add to the QML basic type map
604 if (node->isQmlBasicType())
605 QDocDatabase::qmlBasicTypes().insert(key: name, value: node);
606 } else if (node->isExample()) {
607 // use the module index title as key for the example map
608 QString title = node->tree()->indexTitle();
609 if (!QDocDatabase::examples().contains(key: title, value: node))
610 QDocDatabase::examples().insert(key: title, value: node);
611 } else if (node->isAggregate()) {
612 static_cast<Aggregate *>(node)->findAllClasses();
613 }
614 }
615 }
616}
617
618/*!
619 Find all the attribution pages in this node and insert them
620 into \a attributions.
621 */
622void Aggregate::findAllAttributions(NodeMultiMap &attributions)
623{
624 for (auto *node : std::as_const(t&: m_children)) {
625 if (!node->isPrivate()) {
626 if (node->isPageNode() && static_cast<PageNode*>(node)->isAttribution())
627 attributions.insert(key: node->tree()->indexTitle(), value: node);
628 else if (node->isAggregate())
629 static_cast<Aggregate *>(node)->findAllAttributions(attributions);
630 }
631 }
632}
633
634/*!
635 Finds all the nodes in this node where a \e{since} command appeared
636 in the qdoc comment and sorts them into maps according to the kind
637 of node.
638
639 This function is used for generating the "New Classes... in x.y"
640 section on the \e{What's New in Qt x.y} page.
641 */
642void Aggregate::findAllSince()
643{
644 for (auto *node : std::as_const(t&: m_children)) {
645 if (node->isRelatedNonmember() && node->parent() != this)
646 continue;
647 QString sinceString = node->since();
648 // Insert a new entry into each map for each new since string found.
649 if (node->isInAPI() && !sinceString.isEmpty()) {
650 // operator[] will insert a default-constructed value into the
651 // map if key is not found, which is what we want here.
652 auto &nsmap = QDocDatabase::newSinceMaps()[sinceString];
653 auto &ncmap = QDocDatabase::newClassMaps()[sinceString];
654 auto &nqcmap = QDocDatabase::newQmlTypeMaps()[sinceString];
655
656 if (node->isFunction()) {
657 // Insert functions into the general since map.
658 auto *fn = static_cast<FunctionNode *>(node);
659 if (!fn->isDeprecated() && !fn->isSomeCtor() && !fn->isDtor())
660 nsmap.insert(key: fn->name(), value: fn);
661 } else if (node->isClassNode()) {
662 // Insert classes into the since and class maps.
663 QString name = node->qualifyWithParentName();
664 nsmap.insert(key: name, value: node);
665 ncmap.insert(key: name, value: node);
666 } else if (node->isQmlType()) {
667 // Insert QML elements into the since and element maps.
668 QString name = node->qualifyWithParentName();
669 nsmap.insert(key: name, value: node);
670 nqcmap.insert(key: name, value: node);
671 } else if (node->isQmlProperty()) {
672 // Insert QML properties into the since map.
673 nsmap.insert(key: node->name(), value: node);
674 } else {
675 // Insert external documents into the general since map.
676 QString name = node->qualifyWithParentName();
677 nsmap.insert(key: name, value: node);
678 }
679 }
680 // Enum values - a special case as EnumItem is not a Node subclass
681 if (node->isInAPI() && node->isEnumType()) {
682 for (const auto &val : static_cast<EnumNode *>(node)->items()) {
683 sinceString = val.since();
684 if (sinceString.isEmpty())
685 continue;
686 // Insert to enum value map
687 QDocDatabase::newEnumValueMaps()[sinceString].insert(
688 key: node->name() + "::" + val.name(), value: node);
689 // Ugly hack: Insert into general map with an empty key -
690 // we need something in there to mark the corresponding
691 // section populated. See Sections class constructor.
692 QDocDatabase::newSinceMaps()[sinceString].replace(key: QString(), value: node);
693 }
694 }
695
696 // Recursively find child nodes with since commands.
697 if (node->isAggregate())
698 static_cast<Aggregate *>(node)->findAllSince();
699 }
700}
701
702/*!
703 Resolves the inheritance information for all QML type children
704 of this aggregate.
705*/
706void Aggregate::resolveQmlInheritance()
707{
708 NodeMap previousSearches;
709 for (auto *child : std::as_const(t&: m_children)) {
710 if (!child->isQmlType())
711 continue;
712 static_cast<QmlTypeNode *>(child)->resolveInheritance(previousSearches);
713 }
714
715 // At this point we check for cycles in the inheritance of QML types.
716 for (auto *child : std::as_const(t&: m_children)) {
717 if (child->isQmlType())
718 static_cast<QmlTypeNode *>(child)->checkInheritance();
719 }
720}
721
722/*!
723 Returns a word representing the kind of Aggregate this node is.
724 Currently recognizes class, struct, union, and namespace.
725 If \a cap is true, the word is capitalised.
726 */
727QString Aggregate::typeWord(bool cap) const
728{
729 if (cap) {
730 switch (nodeType()) {
731 case NodeType::Class:
732 return "Class"_L1;
733 case NodeType::Struct:
734 return "Struct"_L1;
735 case NodeType::Union:
736 return "Union"_L1;
737 case NodeType::Namespace:
738 return "Namespace"_L1;
739 default:
740 break;
741 }
742 } else {
743 switch (nodeType()) {
744 case NodeType::Class:
745 return "class"_L1;
746 case NodeType::Struct:
747 return "struct"_L1;
748 case NodeType::Union:
749 return "union"_L1;
750 case NodeType::Namespace:
751 return "namespace"_L1;
752 default:
753 break;
754 }
755 }
756 return QString();
757}
758
759/*! \fn int Aggregate::count() const
760 Returns the number of children in the child list.
761 */
762
763/*! \fn const NodeList &Aggregate::childNodes() const
764 Returns a const reference to the child list.
765 */
766
767/*! \fn NodeList::ConstIterator Aggregate::constBegin() const
768 Returns a const iterator pointing at the beginning of the child list.
769 */
770
771/*! \fn NodeList::ConstIterator Aggregate::constEnd() const
772 Returns a const iterator pointing at the end of the child list.
773 */
774
775/*! \fn QmlTypeNode *Aggregate::qmlBaseNode() const
776 If this Aggregate is a QmlTypeNode, this function returns a pointer to
777 the QmlTypeNode that is its base type. Otherwise it returns \c nullptr.
778 A QmlTypeNode doesn't always have a base type, so even when this Aggregate
779 is aQmlTypeNode, the pointer returned can be \c nullptr.
780 */
781
782/*! \fn FunctionMap &Aggregate::functionMap()
783 Returns a reference to this Aggregate's function map, which
784 is a map of all the children of this Aggregate that are
785 FunctionNodes.
786 */
787
788/*! \fn void Aggregate::appendToRelatedByProxy(const NodeList &t)
789 Appends the list of node pointers to the list of elements that are
790 related to this Aggregate but are documented in a different module.
791
792 \sa relatedByProxy()
793 */
794
795/*! \fn NodeList &Aggregate::relatedByProxy()
796 Returns a reference to a list of node pointers where each element
797 points to a node in an index file for some other module, such that
798 whatever the node represents was documented in that other module,
799 but it is related to this Aggregate, so when the documentation for
800 this Aggregate is written, it will contain links to elements in the
801 other module.
802 */
803
804QT_END_NAMESPACE
805

source code of qttools/src/qdoc/qdoc/src/qdoc/aggregate.cpp