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 | |
14 | QT_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 | */ |
33 | void 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 | */ |
48 | Aggregate::~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 | */ |
71 | Node *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 | */ |
100 | void 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 | */ |
136 | Node *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 | */ |
153 | FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters ¶meters) |
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 | */ |
194 | FunctionNode *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 | */ |
210 | void 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 | */ |
235 | void 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 | */ |
289 | const 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 | */ |
309 | const 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 | */ |
326 | bool 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 | */ |
395 | void 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 | */ |
414 | void 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 | */ |
440 | void 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 | */ |
463 | void 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 | */ |
488 | void 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 | */ |
512 | void 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 | */ |
523 | QmlPropertyNode *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 | */ |
539 | QmlPropertyNode *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 | */ |
567 | bool 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 | */ |
581 | static 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 | */ |
596 | void 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 | */ |
629 | void 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 | */ |
644 | bool 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 | */ |
661 | void 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 | */ |
691 | void 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 | */ |
720 | void 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 | */ |
740 | void 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 | */ |
804 | void 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 | */ |
819 | QString 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 | |
892 | QT_END_NAMESPACE |
893 | |