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