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

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