Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
tree.cpp
Go to the documentation of this file.
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 "tree.h"
5
6#include "classnode.h"
8#include "config.h"
9#include "doc.h"
10#include "enumnode.h"
11#include "functionnode.h"
12#include "htmlgenerator.h"
15#include "location.h"
16#include "node.h"
17#include "qdocdatabase.h"
18#include "text.h"
19#include "typedefnode.h"
20
22
23using namespace Qt::StringLiterals;
24
25/*!
26 \class Tree
27
28 This class constructs and maintains a tree of instances of
29 the subclasses of Node.
30
31 This class is now private. Only class QDocDatabase has access.
32 Please don't change this. If you must access class Tree, do it
33 though the pointer to the singleton QDocDatabase.
34
35 Tree is being converted to a forest. A static member provides a
36 map of Tree *values with the module names as the keys. There is
37 one Tree in the map for each index file read, and there is one
38 tree that is not in the map for the module whose documentation
39 is being generated.
40 */
41
42/*!
43 \class TargetRec
44 \brief A record of a linkable target within the documentation.
45*/
46
47/*!
48 \enum TargetRec::TargetType
49
50 A type of a linkable target record.
51
52 \value Unknown
53 Unknown/target not set.
54 \value Target
55 A location marked with a \\target command.
56 \value Keyword
57 A location marked with a \\keyword command.
58 \value Contents
59 A table of contents item (section title).
60 \value ContentsKeyword
61 A \\keyword tied to a section title.
62*/
63
64/*!
65 Constructs a Tree. \a qdb is the pointer to the singleton
66 qdoc database that is constructing the tree. This might not
67 be necessary, and it might be removed later.
68
69 \a camelCaseModuleName is the project name for this tree
70 as it appears in the qdocconf file.
71 */
72Tree::Tree(const QString &camelCaseModuleName, QDocDatabase *qdb)
73 : m_camelCaseModuleName(camelCaseModuleName),
74 m_physicalModuleName(camelCaseModuleName.toLower()),
75 m_qdb(qdb),
76 m_root(nullptr, QString())
77{
78 m_root.setPhysicalModuleName(m_physicalModuleName);
79 m_root.setTree(this);
80}
81
82/*!
83 Destroys the Tree.
84
85 There are two maps of targets, keywords, and contents.
86 One map is indexed by ref, the other by title. Both maps
87 use the same set of TargetRec objects as the values,
88 so we only need to delete the values from one of them.
89
90 The Node instances themselves are destroyed by the root
91 node's (\c m_root) destructor.
92 */
93Tree::~Tree()
94{
95 qDeleteAll(m_nodesByTargetRef);
96 m_nodesByTargetRef.clear();
97 m_nodesByTargetTitle.clear();
98}
99
100/* API members */
101
102/*!
103 Calls findClassNode() first with \a path and \a start. If
104 it finds a node, the node is returned. If not, it calls
105 findNamespaceNode() with the same parameters. The result
106 is returned.
107 */
108Node *Tree::findNodeForInclude(const QStringList &path) const
109{
110 Node *n = findClassNode(path);
111 if (n == nullptr)
112 n = findNamespaceNode(path);
113 return n;
114}
115
116/*!
117 This function searches this tree for an Aggregate node with
118 the specified \a name. It returns the pointer to that node
119 or nullptr.
120
121 We might need to split the name on '::' but we assume the
122 name is a single word at the moment.
123 */
124Aggregate *Tree::findAggregate(const QString &name)
125{
126 QStringList path = name.split(QLatin1String("::"));
127 return static_cast<Aggregate *>(findNodeRecursive(path, 0, const_cast<NamespaceNode *>(root()),
128 &Node::isFirstClassAggregate));
129}
130
131/*!
132 Find the C++ class node named \a path. Begin the search at the
133 \a start node. If the \a start node is 0, begin the search
134 at the root of the tree. Only a C++ class node named \a path is
135 acceptible. If one is not found, 0 is returned.
136 */
137ClassNode *Tree::findClassNode(const QStringList &path, const Node *start) const
138{
139 if (start == nullptr)
140 start = const_cast<NamespaceNode *>(root());
141 return static_cast<ClassNode *>(findNodeRecursive(path, 0, start, &Node::isClassNode));
142}
143
144/*!
145 Find the Namespace node named \a path. Begin the search at
146 the root of the tree. Only a Namespace node named \a path
147 is acceptible. If one is not found, 0 is returned.
148 */
149NamespaceNode *Tree::findNamespaceNode(const QStringList &path) const
150{
151 Node *start = const_cast<NamespaceNode *>(root());
152 return static_cast<NamespaceNode *>(findNodeRecursive(path, 0, start, &Node::isNamespace));
153}
154
155/*!
156 This function searches for the node specified by \a path.
157 The matching node can be one of several different types
158 including a C++ class, a C++ namespace, or a C++ header
159 file.
160
161 I'm not sure if it can be a QML type, but if that is a
162 possibility, the code can easily accommodate it.
163
164 If a matching node is found, a pointer to it is returned.
165 Otherwise 0 is returned.
166 */
167Aggregate *Tree::findRelatesNode(const QStringList &path)
168{
169 Node *n = findNodeRecursive(path, 0, root(), &Node::isRelatableType);
170 return (((n != nullptr) && n->isAggregate()) ? static_cast<Aggregate *>(n) : nullptr);
171}
172
173/*!
174 Inserts function name \a funcName and function role \a funcRole into
175 the property function map for the specified \a property.
176 */
177void Tree::addPropertyFunction(PropertyNode *property, const QString &funcName,
178 PropertyNode::FunctionRole funcRole)
179{
180 m_unresolvedPropertyMap[property].insert(funcRole, funcName);
181}
182
183/*!
184 This function resolves C++ inheritance and reimplementation
185 settings for each C++ class node found in the tree beginning
186 at \a n. It also calls itself recursively for each C++ class
187 node or namespace node it encounters.
188
189 This function does not resolve QML inheritance.
190 */
191void Tree::resolveBaseClasses(Aggregate *n)
192{
193 for (auto it = n->constBegin(); it != n->constEnd(); ++it) {
194 if ((*it)->isClassNode()) {
195 auto *cn = static_cast<ClassNode *>(*it);
196 QList<RelatedClass> &bases = cn->baseClasses_mutable();
197 for (auto &base : bases) {
198 if (base.m_node == nullptr) {
199 Node *n = m_qdb->findClassNode(base.m_path);
200 /*
201 If the node for the base class was not found,
202 the reason might be that the subclass is in a
203 namespace and the base class is in the same
204 namespace, but the base class name was not
205 qualified with the namespace name. That is the
206 case most of the time. Then restart the search
207 at the parent of the subclass node (the namespace
208 node) using the unqualified base class name.
209 */
210 if (n == nullptr) {
211 Aggregate *parent = cn->parent();
212 if (parent != nullptr)
213 // Exclude the root namespace
214 if (parent->isNamespace() && !parent->name().isEmpty())
215 n = findClassNode(base.m_path, parent);
216 }
217 if (n != nullptr) {
218 auto *bcn = static_cast<ClassNode *>(n);
219 base.m_node = bcn;
220 bcn->addDerivedClass(base.m_access, cn);
221 }
222 }
223 }
224 resolveBaseClasses(cn);
225 } else if ((*it)->isNamespace()) {
226 resolveBaseClasses(static_cast<NamespaceNode *>(*it));
227 }
228 }
229}
230
231/*!
232 */
233void Tree::resolvePropertyOverriddenFromPtrs(Aggregate *n)
234{
235 for (auto node = n->constBegin(); node != n->constEnd(); ++node) {
236 if ((*node)->isClassNode()) {
237 auto *cn = static_cast<ClassNode *>(*node);
238 for (auto property = cn->constBegin(); property != cn->constEnd(); ++property) {
239 if ((*property)->isProperty())
240 cn->resolvePropertyOverriddenFromPtrs(static_cast<PropertyNode *>(*property));
241 }
242 resolvePropertyOverriddenFromPtrs(cn);
243 } else if ((*node)->isNamespace()) {
244 resolvePropertyOverriddenFromPtrs(static_cast<NamespaceNode *>(*node));
245 }
246 }
247}
248
249/*!
250 Resolves access functions associated with each PropertyNode stored
251 in \c m_unresolvedPropertyMap, and adds them into the property node.
252 This allows the property node to list the access functions when
253 generating their documentation.
254 */
255void Tree::resolveProperties()
256{
257 for (auto propEntry = m_unresolvedPropertyMap.constBegin();
258 propEntry != m_unresolvedPropertyMap.constEnd(); ++propEntry) {
259 PropertyNode *property = propEntry.key();
260 Aggregate *parent = property->parent();
261 QString getterName = (*propEntry)[PropertyNode::FunctionRole::Getter];
262 QString setterName = (*propEntry)[PropertyNode::FunctionRole::Setter];
263 QString resetterName = (*propEntry)[PropertyNode::FunctionRole::Resetter];
264 QString notifierName = (*propEntry)[PropertyNode::FunctionRole::Notifier];
265 QString bindableName = (*propEntry)[PropertyNode::FunctionRole::Bindable];
266
267 for (auto it = parent->constBegin(); it != parent->constEnd(); ++it) {
268 if ((*it)->isFunction()) {
269 auto *function = static_cast<FunctionNode *>(*it);
270 if (function->access() == property->access()
271 && (function->status() == property->status() || function->doc().isEmpty())) {
272 if (function->name() == getterName) {
274 } else if (function->name() == setterName) {
276 } else if (function->name() == resetterName) {
278 } else if (function->name() == notifierName) {
280 } else if (function->name() == bindableName) {
282 }
283 }
284 }
285 }
286 }
287
288 for (auto propEntry = m_unresolvedPropertyMap.constBegin();
289 propEntry != m_unresolvedPropertyMap.constEnd(); ++propEntry) {
290 PropertyNode *property = propEntry.key();
291 // redo it to set the property functions
292 if (property->overriddenFrom())
294 }
295
296 m_unresolvedPropertyMap.clear();
297}
298
299/*!
300 Validates that all properties in the documentation tree that require
301 documentation according to the inclusion policy have documentation.
302 Generates warnings for undocumented properties.
303
304 This method recursively traverses the tree starting from \a aggregate,
305 checking each PropertyNode for documentation. Properties without
306 documentation that require it according to the inclusion policy will
307 generate a warning.
308
309 \sa InclusionFilter::requiresDocumentation()
310 */
311void Tree::validatePropertyDocumentation(const Aggregate *aggregate) const
312{
313 const auto &config = Config::instance();
314 const InclusionPolicy policy = config.createInclusionPolicy();
315 validatePropertyDocumentation(aggregate, policy);
316}
317
318/*!
319 \internal
320 \overload
321
322 Private helper that takes \a policy by const reference to avoid
323 recreating it on each recursive call while traversing \a aggregate.
324 */
325void Tree::validatePropertyDocumentation(const Aggregate *aggregate, const InclusionPolicy &policy) const
326{
327 for (auto it = aggregate->constBegin(); it != aggregate->constEnd(); ++it) {
328 Node *node = *it;
329
330 if (node->isProperty() && !node->hasDoc() && !node->isWrapper()) {
331 const NodeContext context = node->createContext();
332 if (InclusionFilter::requiresDocumentation(policy, context))
333 node->location().warning(u"Undocumented property '%1'"_s.arg(node->plainFullName()));
334 }
335
336 if (node->isAggregate())
337 validatePropertyDocumentation(static_cast<Aggregate *>(node), policy);
338 }
339}
340
341/*!
342 For each QML class node that points to a C++ class node,
343 follow its C++ class node pointer and set the C++ class
344 node's QML class node pointer back to the QML class node.
345 */
346void Tree::resolveCppToQmlLinks()
347{
348
349 const NodeList &children = m_root.childNodes();
350 for (auto *child : children) {
351 if (child->isQmlType()) {
352 auto *qcn = static_cast<QmlTypeNode *>(child);
353 auto *cn = const_cast<ClassNode *>(qcn->classNode());
354 if (cn)
355 cn->insertQmlNativeType(qcn);
356 }
357 }
358}
359
360/*!
361 For each \a aggregate, recursively set the \\since version based on
362 \\since information from the associated physical or logical module.
363 That is, C++ and QML types inherit the \\since of their module,
364 unless that command is explicitly used in the type documentation.
365
366 In addition, resolve the since information for individual enum
367 values.
368*/
369void Tree::resolveSince(Aggregate &aggregate)
370{
371 for (auto *child : aggregate.childNodes()) {
372 // Order matters; resolve since-clauses in enum values
373 // first as EnumNode is not an Aggregate
374 if (child->isEnumType())
375 resolveEnumValueSince(static_cast<EnumNode&>(*child));
376 if (!child->isAggregate())
377 continue;
378 if (!child->since().isEmpty())
379 continue;
380
381 if (const auto collectionNode = m_qdb->getModuleNode(child))
382 child->setSince(collectionNode->since());
383
384 resolveSince(static_cast<Aggregate&>(*child));
385 }
386}
387
388/*!
389 Resolve since information for values of enum node \a en.
390
391 Enum values are not derived from Node, but they can have
392 'since' information associated with them. Since-strings
393 for each enum item are initially stored in the Doc
394 instance of EnumNode as SinceTag atoms; parse the doc
395 and store them into each EnumItem.
396*/
397void Tree::resolveEnumValueSince(EnumNode &en)
398{
399 const QStringList enumItems{en.doc().enumItemNames()};
400 const Atom *atom = en.doc().body().firstAtom();
401 if (!atom)
402 return;
403 while ((atom = atom->find(Atom::ListTagLeft))) {
404 if (atom = atom->next(); !atom)
405 break;
406 if (const auto &val = atom->string(); enumItems.contains(val)) {
407 if (atom = atom->next(); atom && atom->next(Atom::SinceTagLeft))
408 en.setSince(val, atom->next()->next()->string());
409 }
410 }
411}
412
413/*!
414 Traverse this Tree and for each ClassNode found, remove
415 from its list of base classes any that are marked private
416 or internal. When a class is removed from a base class
417 list, promote its public pase classes to be base classes
418 of the class where the base class was removed. This is
419 done for documentation purposes. The function is recursive
420 on namespace nodes.
421 */
422void Tree::removePrivateAndInternalBases(NamespaceNode *rootNode)
423{
424 if (rootNode == nullptr)
425 rootNode = root();
426
427 for (auto node = rootNode->constBegin(); node != rootNode->constEnd(); ++node) {
428 if ((*node)->isClassNode())
429 static_cast<ClassNode *>(*node)->removePrivateAndInternalBases();
430 else if ((*node)->isNamespace())
431 removePrivateAndInternalBases(static_cast<NamespaceNode *>(*node));
432 }
433}
434
435/*!
436 */
437ClassList Tree::allBaseClasses(const ClassNode *classNode) const
438{
439 ClassList result;
440 const auto &baseClasses = classNode->baseClasses();
441 for (const auto &relatedClass : baseClasses) {
442 if (relatedClass.m_node != nullptr) {
443 result += relatedClass.m_node;
444 result += allBaseClasses(relatedClass.m_node);
445 }
446 }
447 return result;
448}
449
450/*!
451 Find the node with the specified \a path name that is of
452 the specified \a type and \a subtype. Begin the search at
453 the \a start node. If the \a start node is 0, begin the
454 search at the tree root. \a subtype is not used unless
455 \a type is \c{Page}.
456 */
457Node *Tree::findNodeByNameAndType(const QStringList &path, bool (Node::*isMatch)() const) const
458{
459 return findNodeRecursive(path, 0, root(), isMatch);
460}
461
462/*!
463 Recursive search for a node identified by \a path. Each
464 path element is a name. \a pathIndex specifies the index
465 of the name in \a path to try to match. \a start is the
466 node whose children shoulod be searched for one that has
467 that name. Each time a match is found, increment the
468 \a pathIndex and call this function recursively.
469
470 If the end of the path is reached (i.e. if a matching
471 node is found for each name in the \a path), the \a type
472 must match the type of the last matching node, and if the
473 type is \e{Page}, the \a subtype must match as well.
474
475 If the algorithm is successful, the pointer to the final
476 node is returned. Otherwise 0 is returned.
477 */
478Node *Tree::findNodeRecursive(const QStringList &path, int pathIndex, const Node *start,
479 bool (Node::*isMatch)() const) const
480{
481 if (start == nullptr || path.isEmpty())
482 return nullptr;
483 Node *node = const_cast<Node *>(start);
484 if (!node->isAggregate())
485 return ((pathIndex >= path.size()) ? node : nullptr);
486 auto *current = static_cast<Aggregate *>(node);
487 const NodeList &children = current->childNodes();
488 const QString &name = path.at(pathIndex);
489 for (auto *node : children) {
490 if (node == nullptr)
491 continue;
492 if (node->name() == name) {
493 if (pathIndex + 1 >= path.size()) {
494 if ((node->*(isMatch))())
495 return node;
496 continue;
497 } else { // Search the children of n for the next name in the path.
498 node = findNodeRecursive(path, pathIndex + 1, node, isMatch);
499 if (node != nullptr)
500 return node;
501 }
502 }
503 }
504 return nullptr;
505}
506
507/*!
508 Searches the tree for a node that matches the \a path plus
509 the \a target. The search begins at \a start and moves up
510 the parent chain from there, or, if \a start is 0, the search
511 begins at the root.
512
513 The \a flags can indicate whether to search base classes and/or
514 the enum values in enum types. \a genus further restricts
515 the type of nodes to match, i.e. CPP or QML.
516
517 If a matching node is found, \a ref is set to the HTML fragment
518 identifier to use for the link. On return, the optional
519 \a targetType parameter contains the type of the resolved
520 target; section title (Contents), \\target, \\keyword, or other
521 (Unknown).
522 */
523const Node *Tree::findNodeForTarget(const QStringList &path, const QString &target,
524 const Node *start, int flags, Genus genus,
525 QString &ref, TargetRec::TargetType *targetType) const
526{
527 const Node *node = nullptr;
528
529 // Retrieves and sets ref from target for Node n.
530 // Returns n on valid (or empty) target, or nullptr on an invalid target.
531 auto set_ref_from_target = [this, &ref, &target](const Node *n) -> const Node* {
532 if (!target.isEmpty()) {
533 if (ref = getRef(target, n); ref.isEmpty())
534 return nullptr;
535 }
536 return n;
537 };
538
539 if (genus == Genus::DontCare || genus == Genus::DOC) {
540 if (node = findPageNodeByTitle(path.at(0)); node) {
541 if (node = set_ref_from_target(node); node)
542 return node;
543 }
544 }
545
546 /*
547 For C++ class and QML type contexts, prioritize hierarchical search (including
548 base classes/types) over global target maps. This allows inherited members to
549 take precedence over unrelated global targets such as section titles in other
550 documentation pages. See QTBUG-72107 and QTBUG-141606.
551 */
552 const bool prioritizeHierarchy = start &&
553 ((start->isClassNode() && (genus == Genus::CPP || genus == Genus::DontCare)) ||
554 (start->isQmlType() && (genus == Genus::QML || genus == Genus::DontCare)));
555
556 const TargetRec *result = nullptr;
557 if (!prioritizeHierarchy) {
558 result = findUnambiguousTarget(path.join(QLatin1String("::")), genus);
559 if (result) {
560 ref = result->m_ref;
561 if (node = set_ref_from_target(result->m_node); node) {
562 // Delay returning references to section titles as we
563 // may find a better match below
564 if (result->m_type != TargetRec::Contents) {
565 if (targetType)
566 *targetType = result->m_type;
567 return node;
568 }
569 ref.clear();
570 }
571 }
572 }
573
574 const Node *current = start ? start : root();
575 /*
576 If the path contains one or two double colons ("::"),
577 check if the first two path elements refer to a QML type.
578 If so, path[0] is QML module identifier, and path[1] is
579 the type.
580 */
581 int path_idx = 0;
582 if ((genus == Genus::QML || genus == Genus::DontCare)
583 && path.size() >= 2 && !path[0].isEmpty()) {
584 if (auto *qcn = lookupQmlType(path.sliced(0, 2).join(QLatin1String("::")), start); qcn) {
585 current = qcn;
586 // No further elements in the path, return the type
587 if (path.size() == 2)
588 return set_ref_from_target(qcn);
589 path_idx = 2;
590 }
591 }
592
593 while (current) {
594 if (current->isAggregate()) {
595 if (const Node *match = matchPathAndTarget(
596 path, path_idx, target, current, flags, genus, ref);
597 match != nullptr)
598 return match;
599 }
600 current = current->parent();
601 path_idx = 0;
602 }
603
604 // If we prioritized hierarchy but found nothing, try global targets as fallback
605 if (prioritizeHierarchy) {
606 result = findUnambiguousTarget(path.join(QLatin1String("::")), genus);
607 if (result) {
608 ref = result->m_ref;
609 if (node = set_ref_from_target(result->m_node); node) {
610 if (targetType)
611 *targetType = result->m_type;
612 return node;
613 }
614 }
615 }
616
617 if (node && result) {
618 // Fall back to previously found section title
619 ref = result->m_ref;
620 if (targetType)
621 *targetType = result->m_type;
622 }
623 return node;
624}
625
626/*!
627 First, the \a path is used to find a node. The \a path
628 matches some part of the node's fully quallified name.
629 If the \a target is not empty, it must match a target
630 in the matching node. If the matching of the \a path
631 and the \a target (if present) is successful, \a ref
632 is set from the \a target, and the pointer to the
633 matching node is returned. \a idx is the index into the
634 \a path where to begin the matching. The function is
635 recursive with idx being incremented for each recursive
636 call.
637
638 The matching node must be of the correct \a genus, i.e.
639 either QML or C++, but \a genus can be set to \c DontCare.
640 \a flags indicates whether to search base classes and
641 whether to search for an enum value. \a node points to
642 the node where the search should begin, assuming the
643 \a path is a not a fully-qualified name. \a node is
644 most often the root of this Tree.
645 */
646const Node *Tree::matchPathAndTarget(const QStringList &path, int idx, const QString &target,
647 const Node *node, int flags, Genus genus,
648 QString &ref) const
649{
650 /*
651 If the path has been matched, then if there is a target,
652 try to match the target. If there is a target, but you
653 can't match it at the end of the path, give up; return 0.
654 */
655 if (idx == path.size()) {
656 if (!target.isEmpty()) {
657 ref = getRef(target, node);
658 if (ref.isEmpty())
659 return nullptr;
660 }
661 if (node->isFunction() && node->name() == node->parent()->name())
662 node = node->parent();
663 return node;
664 }
665
666 QString name = path.at(idx);
667 if (node->isAggregate()) {
668 NodeVector nodes;
669 static_cast<const Aggregate *>(node)->findChildren(name, nodes);
670 for (const auto *child : std::as_const(nodes)) {
671 if (genus != Genus::DontCare && !(hasCommonGenusType(genus, child->genus())))
672 continue;
673 const Node *t = matchPathAndTarget(path, idx + 1, target, child, flags, genus, ref);
674 if (t && !t->isPrivate() && !t->isInternal())
675 return t;
676 }
677 }
678 if (target.isEmpty() && (flags & SearchEnumValues)) {
679 const auto *enumNode = node->isAggregate() ?
680 findEnumNode(nullptr, node, path, idx) :
681 findEnumNode(node, nullptr, path, idx);
682 if (enumNode)
683 return enumNode;
684 }
685 if (((genus == Genus::CPP) || (genus == Genus::DontCare)) && node->isClassNode()
686 && (flags & SearchBaseClasses)) {
687 const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(node));
688 for (const auto *base : bases) {
689 const Node *t = matchPathAndTarget(path, idx, target, base, flags, genus, ref);
690 if (t && !t->isPrivate() && !t->isInternal())
691 return t;
692 if (target.isEmpty() && (flags & SearchEnumValues)) {
693 if ((t = findEnumNode(base->findChildNode(path.at(idx), genus, flags), base, path, idx)))
694 return t;
695 }
696 }
697 }
698 if (((genus == Genus::QML) || (genus == Genus::DontCare)) && node->isQmlType()
699 && (flags & SearchBaseClasses)) {
700 const QmlTypeNode *qtn = static_cast<const QmlTypeNode *>(node);
701 while (qtn && qtn->qmlBaseNode()) {
702 qtn = qtn->qmlBaseNode();
703 const Node *t = matchPathAndTarget(path, idx, target, qtn, flags, genus, ref);
704 if (t && !t->isPrivate() && !t->isInternal())
705 return t;
706 }
707 }
708 return nullptr;
709}
710
711/*!
712 Searches the tree for a node that matches the \a path. The
713 search begins at \a start but can move up the parent chain
714 recursively if no match is found. The \a flags are used to
715 restrict the search.
716 */
717const Node *Tree::findNode(const QStringList &path, const Node *start, int flags,
718 Genus genus) const
719{
720 const Node *current = start;
721 if (current == nullptr)
722 current = root();
723
724 do {
725 const Node *node = current;
726 int i;
727 int start_idx = 0;
728
729 /*
730 If the path contains one or two double colons ("::"),
731 check first to see if the first two path strings refer
732 to a QML element. If they do, path[0] will be the QML
733 module identifier, and path[1] will be the QML type.
734 If the answer is yes, the reference identifies a QML
735 type node.
736 */
737 if (((genus == Genus::QML) || (genus == Genus::DontCare)) && (path.size() >= 2)
738 && !path[0].isEmpty()) {
739 QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]), start);
740 if (qcn != nullptr) {
741 node = qcn;
742 if (path.size() == 2)
743 return node;
744 start_idx = 2;
745 }
746 }
747
748 for (i = start_idx; i < path.size(); ++i) {
749 if (node == nullptr || !node->isAggregate())
750 break;
751
752 // Clear the TypesOnly flag until the last path segment, as e.g. namespaces are not
753 // types. We also ignore module nodes as they are not aggregates and thus have no
754 // children.
755 int tmpFlags = (i < path.size() - 1) ? (flags & ~TypesOnly) | IgnoreModules : flags;
756
757 const Node *next = static_cast<const Aggregate *>(node)->findChildNode(path.at(i),
758 genus, tmpFlags);
759 const Node *enumNode = (flags & SearchEnumValues) ?
760 findEnumNode(next, node, path, i) : nullptr;
761
762 if (enumNode)
763 return enumNode;
764
765
766 if (!next && ((genus == Genus::CPP) || (genus == Genus::DontCare))
767 && node->isClassNode() && (flags & SearchBaseClasses)) {
768 const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(node));
769 for (const auto *base : bases) {
770 next = base->findChildNode(path.at(i), genus, tmpFlags);
771 if (flags & SearchEnumValues)
772 if ((enumNode = findEnumNode(next, base, path, i)))
773 return enumNode;
774 if (next)
775 break;
776 }
777 }
778 if (!next && ((genus == Genus::QML) || (genus == Genus::DontCare))
779 && node->isQmlType() && (flags & SearchBaseClasses)) {
780 const QmlTypeNode *qtn = static_cast<const QmlTypeNode *>(node);
781 while (qtn && qtn->qmlBaseNode() && !next) {
782 qtn = qtn->qmlBaseNode();
783 next = qtn->findChildNode(path.at(i), genus, tmpFlags);
784 }
785 }
786 node = next;
787 }
788 if ((node != nullptr) && i == path.size())
789 return node;
790 current = current->parent();
791 } while (current != nullptr);
792
793 return nullptr;
794}
795
796
797/*!
798 \internal
799
800 Helper function to return an enum that matches the \a path at a specified \a offset.
801 If \a node is a valid enum node, the enum name is assumed to be included in the path
802 (i.e, a scoped enum). Otherwise, query the \a aggregate (typically, the class node)
803 for enum node that includes the value at the last position in \a path.
804 */
805const Node *Tree::findEnumNode(const Node *node, const Node *aggregate, const QStringList &path, int offset) const
806{
807 // Scoped enum (path ends in enum_name :: enum_value)
808 if (node && node->isEnumType() && offset == path.size() - 1) {
809 const auto *en = static_cast<const EnumNode*>(node);
810 if (en->isScoped() && en->hasItem(path.last()))
811 return en;
812 }
813
814 // Standard enum (path ends in class_name :: enum_value)
815 return (!node && aggregate && offset == path.size() - 1) ?
816 static_cast<const Aggregate *>(aggregate)->findEnumNodeForValue(path.last()) :
817 nullptr;
818}
819
820/*!
821 This function searches for a node with a canonical title
822 constructed from \a target. If the node it finds is \a node,
823 it returns the ref from that node. Otherwise it returns an
824 empty string.
825 */
826QString Tree::getRef(const QString &target, const Node *node) const
827{
828 auto it = m_nodesByTargetTitle.constFind(target);
829 if (it != m_nodesByTargetTitle.constEnd()) {
830 do {
831 if (it.value()->m_node == node)
832 return it.value()->m_ref;
833 ++it;
834 } while (it != m_nodesByTargetTitle.constEnd() && it.key() == target);
835 }
836 QString key = Utilities::asAsciiPrintable(target);
837 it = m_nodesByTargetRef.constFind(key);
838 if (it != m_nodesByTargetRef.constEnd()) {
839 do {
840 if (it.value()->m_node == node)
841 return it.value()->m_ref;
842 ++it;
843 } while (it != m_nodesByTargetRef.constEnd() && it.key() == key);
844 }
845 return QString();
846}
847
848/*!
849 Inserts a new target into the target table. \a name is the
850 key. The target record contains the \a type, a pointer to
851 the \a node, the \a priority. and a canonicalized form of
852 the \a name, which is later used.
853 */
854void Tree::insertTarget(const QString &name, const QString &title, TargetRec::TargetType type,
855 Node *node, int priority)
856{
857 auto *target = new TargetRec(name, type, node, priority);
858 m_nodesByTargetRef.insert(name, target);
859 m_nodesByTargetTitle.insert(title, target);
860}
861
862/*!
863 \internal
864
865 \a root is the root node of the tree to resolve targets for. This function
866 traverses the tree starting from the root node and processes each child
867 node. If the child node is an aggregate node, this function is called
868 recursively on the child node.
869 */
870void Tree::resolveTargets(Aggregate *root)
871{
872 for (auto *child : root->childNodes()) {
873 addToPageNodeByTitleMap(child);
874 populateTocSectionTargetMap(child);
875 addKeywordsToTargetMaps(child);
876 addTargetsToTargetMap(child);
877
878 if (child->isAggregate())
879 resolveTargets(static_cast<Aggregate *>(child));
880 }
881}
882
883/*!
884 \internal
885
886 Updates the target maps for targets associated with the given \a node.
887 */
888void Tree::addTargetsToTargetMap(Node *node) {
889 if (!node || !node->doc().hasTargets())
890 return;
891
892 for (Atom *i : std::as_const(node->doc().targets())) {
893 const QString ref = refForAtom(i);
894 const QString title = i->string();
895 if (!ref.isEmpty() && !title.isEmpty()) {
896 QString key = Utilities::asAsciiPrintable(title);
897 auto *target = new TargetRec(std::move(ref), TargetRec::Target, node, 2);
898 m_nodesByTargetRef.insert(key, target);
899 m_nodesByTargetTitle.insert(title, target);
900 }
901 }
902}
903
904/*
905 If atom \a a is immediately followed by a
906 section title (\section1..\section4 command),
907 returns the SectionLeft atom; otherwise nullptr.
908*/
909static const Atom *nextSection(const Atom *a)
910{
911 while (a && a->next(Atom::SectionRight))
912 a = a->next(); // skip closing section atoms
913 return a ? a->next(Atom::SectionLeft) : nullptr;
914}
915
916/*!
917 \internal
918
919 Updates the target maps for keywords associated with the given \a node.
920 */
921void Tree::addKeywordsToTargetMaps(Node *node) {
922 if (!node->doc().hasKeywords())
923 return;
924
925 for (Atom *i : std::as_const(node->doc().keywords())) {
926 QString ref = refForAtom(i);
927 QString title = i->string();
928 if (!ref.isEmpty() && !title.isEmpty()) {
929 auto *target = new TargetRec(ref, nextSection(i) ? TargetRec::ContentsKeyword : TargetRec::Keyword, node, 1);
930 m_nodesByTargetRef.insert(Utilities::asAsciiPrintable(title), target);
931 m_nodesByTargetTitle.insert(title, target);
932 if (!target->isEmpty())
933 i->append(target->m_ref);
934 }
935 }
936}
937
938/*!
939 \internal
940
941 Populates the map of targets for each section in the table of contents for
942 the given \a node while ensuring that each target has a unique reference.
943 */
944void Tree::populateTocSectionTargetMap(Node *node) {
945 if (!node || !node->doc().hasTableOfContents())
946 return;
947
948 QStack<Atom *> tocLevels;
949 QSet<QString> anchors;
950
951 qsizetype index = 0;
952
953 for (Atom *atom: std::as_const(node->doc().tableOfContents())) {
954 while (!tocLevels.isEmpty() && tocLevels.top()->string().toInt() >= atom->string().toInt())
955 tocLevels.pop();
956
957 tocLevels.push(atom);
958
959 QString ref = refForAtom(atom);
960 const QString &title = Text::sectionHeading(atom).toString();
961 if (ref.isEmpty() || title.isEmpty())
962 continue;
963
964 if (anchors.contains(ref)) {
965 QStringList refParts;
966 for (const auto tocLevel : tocLevels)
967 refParts << refForAtom(tocLevel);
968
969 refParts << QString::number(index);
970 ref = refParts.join(QLatin1Char('-'));
971 }
972
973 anchors.insert(ref);
974 if (atom->next(Atom::SectionHeadingLeft))
975 atom->next()->append(ref);
976 ++index;
977
978 const QString &key = Utilities::asAsciiPrintable(title);
979 auto *target = new TargetRec(ref, TargetRec::Contents, node, 3);
980 m_nodesByTargetRef.insert(key, target);
981 m_nodesByTargetTitle.insert(title, target);
982 }
983}
984
985/*!
986 \internal
987
988 Checks if the \a node's title is registered in the page nodes by title map.
989 If not, it stores the page node in the map.
990 */
991void Tree::addToPageNodeByTitleMap(Node *node) {
992 if (!node || !node->isTextPageNode())
993 return;
994
995 auto *pageNode = static_cast<PageNode *>(node);
996 QString key = pageNode->title();
997 if (key.isEmpty())
998 return;
999
1000 if (key.contains(QChar(' ')))
1001 key = Utilities::asAsciiPrintable(key);
1002 const QList<PageNode *> nodes = m_pageNodesByTitle.values(key);
1003
1004 bool alreadyThere = std::any_of(nodes.cbegin(), nodes.cend(), [&](const auto &knownNode) {
1005 return knownNode->isExternalPage() && knownNode->name() == pageNode->name();
1006 });
1007
1008 if (!alreadyThere)
1009 m_pageNodesByTitle.insert(key, pageNode);
1010}
1011
1012/*!
1013 Searches for a \a target anchor, matching the given \a genus, and returns
1014 the associated TargetRec instance.
1015 */
1016const TargetRec *Tree::findUnambiguousTarget(const QString &target, Genus genus) const
1017{
1018 auto findBestCandidate = [&](const TargetMap &tgtMap, const QString &key) {
1019 TargetRec *best = nullptr;
1020 auto [it, end] = tgtMap.equal_range(key);
1021 while (it != end) {
1022 TargetRec *candidate = it.value();
1023 if ((genus == Genus::DontCare) || (hasCommonGenusType(genus, candidate->genus()))) {
1024 if (!best || (candidate->m_priority < best->m_priority))
1025 best = candidate;
1026 }
1027 ++it;
1028 }
1029 return best;
1030 };
1031
1032 TargetRec *bestTarget = findBestCandidate(m_nodesByTargetTitle, target);
1033 if (!bestTarget)
1034 bestTarget = findBestCandidate(m_nodesByTargetRef, Utilities::asAsciiPrintable(target));
1035
1036 return bestTarget;
1037}
1038
1039/*!
1040 This function searches for a node with the specified \a title.
1041 */
1042const PageNode *Tree::findPageNodeByTitle(const QString &title) const
1043{
1044 PageNodeMultiMap::const_iterator it;
1045 if (title.contains(QChar(' ')))
1046 it = m_pageNodesByTitle.constFind(Utilities::asAsciiPrintable(title));
1047 else
1048 it = m_pageNodesByTitle.constFind(title);
1049 if (it != m_pageNodesByTitle.constEnd()) {
1050 /*
1051 Reporting all these duplicate section titles is probably
1052 overkill. We should report the duplicate file and let
1053 that suffice.
1054 */
1055 PageNodeMultiMap::const_iterator j = it;
1056 ++j;
1057 if (j != m_pageNodesByTitle.constEnd() && j.key() == it.key()) {
1058 while (j != m_pageNodesByTitle.constEnd()) {
1059 if (j.key() == it.key() && j.value()->url().isEmpty()) {
1060 break; // Just report one duplicate for now.
1061 }
1062 ++j;
1063 }
1064 if (j != m_pageNodesByTitle.cend()) {
1065 it.value()->location().warning("This page title exists in more than one file: "
1066 + title);
1067 j.value()->location().warning("[It also exists here]");
1068 }
1069 }
1070 return it.value();
1071 }
1072 return nullptr;
1073}
1074
1075/*!
1076 Returns a canonical title for the \a atom, if the \a atom
1077 is a SectionLeft, SectionHeadingLeft, Keyword, or Target.
1078
1079 If a target or a keyword is immediately followed by a
1080 section, the former adopts the title (ref) of the latter.
1081 */
1082QString Tree::refForAtom(const Atom *atom)
1083{
1084 Q_ASSERT(atom);
1085
1086 switch (atom->type()) {
1087 case Atom::SectionLeft:
1088 atom = atom->next();
1089 [[fallthrough]];
1091 if (atom->count() == 2)
1092 return atom->string(1);
1093 return Utilities::asAsciiPrintable(Text::sectionHeading(atom).toString());
1094 case Atom::Target:
1095 [[fallthrough]];
1096 case Atom::Keyword:
1097 if (const auto *section = nextSection(atom))
1098 return refForAtom(section);
1099 return Utilities::asAsciiPrintable(atom->string());
1100 default:
1101 return {};
1102 }
1103}
1104
1105/*!
1106 \fn const CNMap &Tree::groups() const
1107 Returns a const reference to the collection of all
1108 group nodes.
1109*/
1110
1111/*!
1112 \fn const ModuleMap &Tree::modules() const
1113 Returns a const reference to the collection of all
1114 module nodes.
1115*/
1116
1117/*!
1118 \fn const QmlModuleMap &Tree::qmlModules() const
1119 Returns a const reference to the collection of all
1120 QML module nodes.
1121*/
1122
1123/*!
1124 Returns a pointer to the collection map specified by \a type.
1125 Returns null if \a type is not specified.
1126 */
1127CNMap *Tree::getCollectionMap(NodeType type)
1128{
1129 switch (type) {
1130 case NodeType::Group:
1131 return &m_groups;
1132 case NodeType::Module:
1133 return &m_modules;
1135 return &m_qmlModules;
1136 default:
1137 break;
1138 }
1139 return nullptr;
1140}
1141
1142/*!
1143 Searches this tree for a collection named \a name with the
1144 specified \a type. If the collection is found, a pointer
1145 to it is returned. If a collection is not found, null is
1146 returned.
1147 */
1148CollectionNode *Tree::getCollection(const QString &name, NodeType type)
1149{
1150 CNMap *map = getCollectionMap(type);
1151 if (map) {
1152 auto it = map->constFind(name);
1153 if (it != map->cend())
1154 return it.value();
1155 }
1156 return nullptr;
1157}
1158
1159/*!
1160 Find the group, module, or QML module named \a name and return a
1161 pointer to that collection node. \a type specifies which kind of
1162 collection node you want. If a collection node with the specified \a
1163 name and \a type is not found, a new one is created, and the pointer
1164 to the new one is returned.
1165
1166 If a new collection node is created, its parent is the tree
1167 root, and the new collection node is marked \e{not seen}.
1168
1169 \a genus must be specified, i.e. it must not be \c{DontCare}.
1170 If it is \c{DontCare}, 0 is returned, which is a programming
1171 error.
1172 */
1173CollectionNode *Tree::findCollection(const QString &name, NodeType type)
1174{
1175 CNMap *m = getCollectionMap(type);
1176 if (!m) // error
1177 return nullptr;
1178 auto it = m->constFind(name);
1179 if (it != m->cend())
1180 return it.value();
1181 CollectionNode *cn = new CollectionNode(type, root(), name);
1183 m->insert(name, cn);
1184 return cn;
1185}
1186
1187/*! \fn CollectionNode *Tree::findGroup(const QString &name)
1188 Find the group node named \a name and return a pointer
1189 to it. If the group node is not found, add a new group
1190 node named \a name and return a pointer to the new one.
1191
1192 If a new group node is added, its parent is the tree root,
1193 and the new group node is marked \e{not seen}.
1194 */
1195
1196/*! \fn CollectionNode *Tree::findModule(const QString &name)
1197 Find the module node named \a name and return a pointer
1198 to it. If a matching node is not found, add a new module
1199 node named \a name and return a pointer to that one.
1200
1201 If a new module node is added, its parent is the tree root,
1202 and the new module node is marked \e{not seen}.
1203 */
1204
1205/*! \fn CollectionNode *Tree::findQmlModule(const QString &name)
1206 Find the QML module node named \a name and return a pointer
1207 to it. If a matching node is not found, add a new QML module
1208 node named \a name and return a pointer to that one.
1209
1210 If a new QML module node is added, its parent is the tree root,
1211 and the new node is marked \e{not seen}.
1212 */
1213
1214/*! \fn CollectionNode *Tree::addGroup(const QString &name)
1215 Looks up the group node named \a name in the collection
1216 of all group nodes. If a match is found, a pointer to the
1217 node is returned. Otherwise, a new group node named \a name
1218 is created and inserted into the collection, and the pointer
1219 to that node is returned.
1220 */
1221
1222/*! \fn CollectionNode *Tree::addModule(const QString &name)
1223 Looks up the module node named \a name in the collection
1224 of all module nodes. If a match is found, a pointer to the
1225 node is returned. Otherwise, a new module node named \a name
1226 is created and inserted into the collection, and the pointer
1227 to that node is returned.
1228 */
1229
1230/*! \fn CollectionNode *Tree::addQmlModule(const QString &name)
1231 Looks up the QML module node named \a name in the collection
1232 of all QML module nodes. If a match is found, a pointer to the
1233 node is returned. Otherwise, a new QML module node named \a name
1234 is created and inserted into the collection, and the pointer
1235 to that node is returned.
1236 */
1237
1238/*!
1239 Looks up the group node named \a name in the collection
1240 of all group nodes. If a match is not found, a new group
1241 node named \a name is created and inserted into the collection.
1242 Then append \a node to the group's members list, and append the
1243 group name to the list of group names in \a node. The parent of
1244 \a node is not changed by this function. Returns a pointer to
1245 the group node.
1246 */
1247CollectionNode *Tree::addToGroup(const QString &name, Node *node)
1248{
1249 CollectionNode *cn = findGroup(name);
1250 if (!node->isInternal()) {
1251 cn->addMember(node);
1252 node->appendGroupName(name);
1253 }
1254 return cn;
1255}
1256
1257/*!
1258 Looks up the module node named \a name in the collection
1259 of all module nodes. If a match is not found, a new module
1260 node named \a name is created and inserted into the collection.
1261 Then append \a node to the module's members list. The parent of
1262 \a node is not changed by this function. Returns the module node.
1263 */
1264CollectionNode *Tree::addToModule(const QString &name, Node *node)
1265{
1266 CollectionNode *cn = findModule(name);
1267 cn->addMember(node);
1268 node->setPhysicalModuleName(name);
1269 return cn;
1270}
1271
1272/*!
1273 Looks up the QML module named \a name. If it isn't there,
1274 create it. Then append \a node to the QML module's member
1275 list. The parent of \a node is not changed by this function.
1276 Returns the pointer to the QML module node.
1277 */
1278CollectionNode *Tree::addToQmlModule(const QString &name, Node *node)
1279{
1280 QStringList qmid;
1281 QStringList dotSplit;
1282 QStringList blankSplit = name.split(QLatin1Char(' '));
1283 qmid.append(blankSplit[0]);
1284 if (blankSplit.size() > 1) {
1285 qmid.append(blankSplit[0] + blankSplit[1]);
1286 dotSplit = blankSplit[1].split(QLatin1Char('.'));
1287 qmid.append(blankSplit[0] + dotSplit[0]);
1288 }
1289
1290 CollectionNode *cn = findQmlModule(blankSplit[0]);
1291 cn->addMember(node);
1292 node->setQmlModule(cn);
1293 if (node->isQmlType()) {
1294 QmlTypeNode *n = static_cast<QmlTypeNode *>(node);
1295 for (int i = 0; i < qmid.size(); ++i) {
1296 QString key = qmid[i] + "::" + node->name();
1297 insertQmlType(key, n);
1298 }
1299 // Also insert with unqualified name for context-aware disambiguation
1300 insertQmlType(node->name(), n);
1301 }
1302 return cn;
1303}
1304
1305/*!
1306 Inserts QML type node \a n with the specified \a key into the type map.
1307 Since the map is a QMultiMap, multiple types with the same name can coexist
1308 (e.g., Shape from different modules).
1309 */
1310void Tree::insertQmlType(const QString &key, QmlTypeNode *n)
1311{
1312 m_qmlTypeMap.insert(key, n);
1313}
1314
1315/*!
1316 Looks up and returns the QML type node identified by \a name. When multiple
1317 types with the same name exist (e.g., Shape from different modules), prefers
1318 the type from the same module as \a relative if provided. Returns the first
1319 match if no module relationship exists, or nullptr if not found.
1320 */
1321QmlTypeNode *Tree::lookupQmlType(const QString &name, const Node *relative) const
1322{
1323 auto values = m_qmlTypeMap.values(name);
1324 if (values.isEmpty())
1325 return nullptr;
1326
1327 // If no context or only one match, return first result
1328 if (!relative || values.size() == 1)
1329 return values.first();
1330
1331 // Prefer types from the same module as the relative context
1332 if (relative->isQmlType()) {
1333 const auto *relativeQmlType = static_cast<const QmlTypeNode *>(relative);
1334 const CollectionNode *relativeModule = relativeQmlType->logicalModule();
1335
1336 for (auto *candidate : values) {
1337 if (candidate->logicalModule() == relativeModule)
1338 return candidate;
1339 }
1340 }
1341
1342 // Fallback to first match
1343 return values.first();
1344}
1345
1346/*!
1347 Finds the function node with the specifried name \a path that
1348 also has the specified \a parameters and returns a pointer to
1349 the first matching function node if one is found.
1350
1351 This function begins searching the tree at \a relative for
1352 the \l {FunctionNode} {function node} identified by \a path
1353 that has the specified \a parameters. The \a flags are
1354 used to restrict the search. If a matching node is found, a
1355 pointer to it is returned. Otherwise, nullis returned. If
1356 \a relative is ull, the search begins at the tree root.
1357 */
1358const FunctionNode *Tree::findFunctionNode(const QStringList &path, const Parameters &parameters,
1359 const Node *relative, Genus genus) const
1360{
1361 if (path.size() == 3 && !path[0].isEmpty()
1362 && ((genus == Genus::QML) || (genus == Genus::DontCare))) {
1363 QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]), relative);
1364 if (qcn == nullptr) {
1365 QStringList p(path[1]);
1366 Node *n = findNodeByNameAndType(p, &Node::isQmlType);
1367 if ((n != nullptr) && n->isQmlType())
1368 qcn = static_cast<QmlTypeNode *>(n);
1369 }
1370 if (qcn != nullptr)
1371 return static_cast<const FunctionNode *>(qcn->findFunctionChild(path[2], parameters));
1372 }
1373
1374 if (relative == nullptr)
1375 relative = root();
1376 else if (genus != Genus::DontCare) {
1377 if (!(hasCommonGenusType(genus, relative->genus())))
1378 relative = root();
1379 }
1380
1381 do {
1382 Node *node = const_cast<Node *>(relative);
1383 int i;
1384
1385 for (i = 0; i < path.size(); ++i) {
1386 if (node == nullptr || !node->isAggregate())
1387 break;
1388
1389 Aggregate *aggregate = static_cast<Aggregate *>(node);
1390 Node *next = nullptr;
1391 if (i == path.size() - 1)
1392 next = aggregate->findFunctionChild(path.at(i), parameters);
1393 else
1394 next = aggregate->findChildNode(path.at(i), genus);
1395
1396 if ((next == nullptr) && aggregate->isClassNode()) {
1397 const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(aggregate));
1398 for (auto *base : bases) {
1399 if (i == path.size() - 1)
1400 next = base->findFunctionChild(path.at(i), parameters);
1401 else
1402 next = base->findChildNode(path.at(i), genus);
1403
1404 if (next != nullptr)
1405 break;
1406 }
1407 }
1408
1409 node = next;
1410 } // for (i = 0; i < path.size(); ++i)
1411
1412 if (node && i == path.size() && node->isFunction()) {
1413 // A function node was found at the end of the path.
1414 // If it is not marked private, return it. If it is
1415 // marked private, then if it overrides a function,
1416 // find that function instead because it might not
1417 // be marked private. If all the overloads are
1418 // marked private, return the original function node.
1419 // This should be replace with findOverriddenFunctionNode().
1420 const FunctionNode *fn = static_cast<const FunctionNode *>(node);
1421 const FunctionNode *FN = fn;
1422 while (FN->isPrivate() && !FN->overridesThis().isEmpty()) {
1423 QStringList path = FN->overridesThis().split("::");
1424 FN = m_qdb->findFunctionNode(path, parameters, relative, genus);
1425 if (FN == nullptr)
1426 break;
1427 if (!FN->isPrivate())
1428 return FN;
1429 }
1430 return fn;
1431 }
1432 relative = relative->parent();
1433 } while (relative);
1434 return nullptr;
1435}
1436
1437/*!
1438 Search this tree recursively from \a parent to find a function
1439 node with the specified \a tag. If no function node is found
1440 with the required \a tag, return 0.
1441 */
1442FunctionNode *Tree::findFunctionNodeForTag(const QString &tag, Aggregate *parent)
1443{
1444 if (parent == nullptr)
1445 parent = root();
1446 const NodeList &children = parent->childNodes();
1447 for (Node *n : children) {
1448 if (n != nullptr && n->isFunction() && n->hasTag(tag))
1449 return static_cast<FunctionNode *>(n);
1450 }
1451 for (Node *n : children) {
1452 if (n != nullptr && n->isAggregate()) {
1453 n = findFunctionNodeForTag(tag, static_cast<Aggregate *>(n));
1454 if (n != nullptr)
1455 return static_cast<FunctionNode *>(n);
1456 }
1457 }
1458 return nullptr;
1459}
1460
1461/*!
1462 There should only be one macro node for macro name \a t.
1463 The macro node is not built until the \macro command is seen.
1464 */
1465FunctionNode *Tree::findMacroNode(const QString &t, const Aggregate *parent)
1466{
1467 if (parent == nullptr)
1468 parent = root();
1469 const NodeList &children = parent->childNodes();
1470 for (Node *n : children) {
1471 if (n != nullptr && (n->isMacro() || n->isFunction()) && n->name() == t)
1472 return static_cast<FunctionNode *>(n);
1473 }
1474 for (Node *n : children) {
1475 if (n != nullptr && n->isAggregate()) {
1476 FunctionNode *fn = findMacroNode(t, static_cast<Aggregate *>(n));
1477 if (fn != nullptr)
1478 return fn;
1479 }
1480 }
1481 return nullptr;
1482}
1483
1484/*!
1485 Add the class and struct names in \a arg to the \e {don't document}
1486 map.
1487 */
1488void Tree::addToDontDocumentMap(QString &arg)
1489{
1490 arg.remove(QChar('('));
1491 arg.remove(QChar(')'));
1492 QString t = arg.simplified();
1493 QStringList sl = t.split(QChar(' '));
1494 if (sl.isEmpty())
1495 return;
1496 for (const QString &s : sl) {
1497 if (!m_dontDocumentMap.contains(s))
1498 m_dontDocumentMap.insert(s, nullptr);
1499 }
1500}
1501
1502/*!
1503 The \e {don't document} map has been loaded with the names
1504 of classes and structs in the current module that are not
1505 documented and should not be documented. Now traverse the
1506 map, and for each class or struct name, find the class node
1507 that represents that class or struct and mark it with the
1508 \C DontDocument status.
1509
1510 This results in a map of the class and struct nodes in the
1511 module that are in the public API but are not meant to be
1512 used by anyone. They are only used internally, but for one
1513 reason or another, they must have public visibility.
1514 */
1516{
1517 for (auto it = m_dontDocumentMap.begin(); it != m_dontDocumentMap.end(); ++it) {
1518 Aggregate *node = findAggregate(it.key());
1519 if (node != nullptr)
1521 }
1522}
1523
1524QT_END_NAMESPACE
FunctionNode * findFunctionChild(const FunctionNode *clone)
Returns the function node that is a child of this node, such that the function described has the same...
const NodeList & childNodes() const
Returns a const reference to the child list.
Definition aggregate.h:42
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
AtomType type() const
Return the type of this atom.
Definition atom.h:151
@ ListTagLeft
Definition atom.h:65
@ Target
Definition atom.h:104
@ SectionRight
Definition atom.h:82
@ SectionHeadingLeft
Definition atom.h:83
@ SinceTagLeft
Definition atom.h:88
@ SectionLeft
Definition atom.h:81
const Atom * next() const
Return the next atom in the atom list.
Definition atom.h:148
const Atom * next(AtomType t) const
Return the next Atom in the list if it is of AtomType t.
Definition atom.cpp:301
The ClassNode represents a C++ class.
Definition classnode.h:23
void removePrivateAndInternalBases()
Remove private and internal bases classes from this class's list of base classes.
A class for holding the members of a collection of doc pages.
void addMember(Node *node) override
Appends node to the collection node's member list, if and only if it isn't already in the member list...
bool hasTableOfContents() const
Definition doc.cpp:261
bool hasKeywords() const
Definition doc.cpp:266
bool hasTargets() const
Definition doc.cpp:271
bool isScoped() const
Definition enumnode.h:33
This node is used to represent any kind of function being documented.
This class represents a C++ namespace.
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:19
This class describes one instance of using the Q_PROPERTY macro.
const PropertyNode * overriddenFrom() const
void addFunction(FunctionNode *function, FunctionRole role)
void setOverriddenFrom(const PropertyNode *baseProperty)
Sets this property's {overridden from} property to baseProperty, which indicates that this property o...
void addSignal(FunctionNode *function, FunctionRole role)
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
QmlTypeNode * qmlBaseNode() const override
If this Aggregate is a QmlTypeNode, this function returns a pointer to the QmlTypeNode that is its ba...
Definition qmltypenode.h:47
CollectionNode * logicalModule() const override
If this is a QmlTypeNode, a pointer to its QML module is returned, which is a pointer to a Collection...
Definition qmltypenode.h:40
Definition text.h:12
static Text sectionHeading(const Atom *sectionBegin)
Definition text.cpp:176
This class constructs and maintains a tree of instances of the subclasses of Node.
Definition tree.h:58
void markDontDocumentNodes()
The {don't document} map has been loaded with the names of classes and structs in the current module ...
Definition tree.cpp:1515
void addToDontDocumentMap(QString &arg)
Add the class and struct names in arg to the {don't document} map.
Definition tree.cpp:1488
Node * findNodeByNameAndType(const QStringList &path, bool(Node::*isMatch)() const) const
Find the node with the specified path name that is of the specified type and subtype.
Definition tree.cpp:457
NodeType
Definition genustypes.h:150
Combined button and popup list for selecting options.
QList< Node * > NodeList
Definition node.h:44
QList< ClassNode * > ClassList
Definition node.h:45
QList< Node * > NodeVector
Definition node.h:46
QMap< QString, CollectionNode * > CNMap
Definition node.h:51
@ SearchBaseClasses
@ SearchEnumValues
@ IgnoreModules
@ TypesOnly
The Node class is the base class for all the nodes in QDoc's parse tree.
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:242
virtual bool isWrapper() const
Returns true if the node is a class node or a QML type node that is marked as being a wrapper class o...
Definition node.cpp:952
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:118
virtual void setQmlModule(CollectionNode *)
If this is a QmlTypeNode, this function sets the QML type's QML module pointer to the CollectionNode ...
Definition node.h:269
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:128
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition node.cpp:848
Genus genus() const override
Returns this node's Genus.
Definition node.h:92
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:100
virtual Status status() const
Returns the node's status value.
Definition node.h:249
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:160
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:215
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:143
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:235
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:107
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:119
NodeContext createContext() const
Definition node.cpp:162
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:905
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:150
void setStatus(Status t)
Sets the node's status to t.
Definition node.cpp:556
@ DontDocument
Definition node.h:62
A class for parsing and managing a function parameter list.
Definition main.cpp:28
A record of a linkable target within the documentation.
Definition tree.h:27
Node * m_node
Definition tree.h:46
Genus genus() const
Definition tree.h:44
int m_priority
Definition tree.h:49
TargetType
A type of a linkable target record.
Definition tree.h:29
@ Contents
Definition tree.h:29
TargetType m_type
Definition tree.h:48
static const Atom * nextSection(const Atom *a)
Definition tree.cpp:909
QMultiMap< QString, TargetRec * > TargetMap
Definition tree.h:52