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 default:
1155 break;
1156 }
1157 return nullptr;
1158}
1159
1160/*!
1161 Searches this tree for a collection named \a name with the
1162 specified \a type. If the collection is found, a pointer
1163 to it is returned. If a collection is not found, null is
1164 returned.
1165 */
1166CollectionNode *Tree::getCollection(const QString &name, NodeType type)
1167{
1168 CNMap *map = getCollectionMap(type);
1169 if (map) {
1170 auto it = map->constFind(name);
1171 if (it != map->cend())
1172 return it.value();
1173 }
1174 return nullptr;
1175}
1176
1177/*!
1178 Find the group, module, or QML module named \a name and return a
1179 pointer to that collection node. \a type specifies which kind of
1180 collection node you want. If a collection node with the specified \a
1181 name and \a type is not found, a new one is created, and the pointer
1182 to the new one is returned.
1183
1184 If a new collection node is created, its parent is the tree
1185 root, and the new collection node is marked \e{not seen}.
1186
1187 \a genus must be specified, i.e. it must not be \c{DontCare}.
1188 If it is \c{DontCare}, 0 is returned, which is a programming
1189 error.
1190 */
1191CollectionNode *Tree::findCollection(const QString &name, NodeType type)
1192{
1193 CNMap *m = getCollectionMap(type);
1194 if (!m) // error
1195 return nullptr;
1196 auto it = m->constFind(name);
1197 if (it != m->cend())
1198 return it.value();
1199 CollectionNode *cn = new CollectionNode(type, root(), name);
1201 m->insert(name, cn);
1202 return cn;
1203}
1204
1205/*! \fn CollectionNode *Tree::findGroup(const QString &name)
1206 Find the group node named \a name and return a pointer
1207 to it. If the group node is not found, add a new group
1208 node named \a name and return a pointer to the new one.
1209
1210 If a new group node is added, its parent is the tree root,
1211 and the new group node is marked \e{not seen}.
1212 */
1213
1214/*! \fn CollectionNode *Tree::findModule(const QString &name)
1215 Find the module node named \a name and return a pointer
1216 to it. If a matching node is not found, add a new module
1217 node named \a name and return a pointer to that one.
1218
1219 If a new module node is added, its parent is the tree root,
1220 and the new module node is marked \e{not seen}.
1221 */
1222
1223/*! \fn CollectionNode *Tree::findQmlModule(const QString &name)
1224 Find the QML module node named \a name and return a pointer
1225 to it. If a matching node is not found, add a new QML module
1226 node named \a name and return a pointer to that one.
1227
1228 If a new QML module node is added, its parent is the tree root,
1229 and the new node is marked \e{not seen}.
1230 */
1231
1232/*! \fn CollectionNode *Tree::addGroup(const QString &name)
1233 Looks up the group node named \a name in the collection
1234 of all group nodes. If a match is found, a pointer to the
1235 node is returned. Otherwise, a new group node named \a name
1236 is created and inserted into the collection, and the pointer
1237 to that node is returned.
1238 */
1239
1240/*! \fn CollectionNode *Tree::addModule(const QString &name)
1241 Looks up the module node named \a name in the collection
1242 of all module nodes. If a match is found, a pointer to the
1243 node is returned. Otherwise, a new module node named \a name
1244 is created and inserted into the collection, and the pointer
1245 to that node is returned.
1246 */
1247
1248/*! \fn CollectionNode *Tree::addQmlModule(const QString &name)
1249 Looks up the QML module node named \a name in the collection
1250 of all QML module nodes. If a match is found, a pointer to the
1251 node is returned. Otherwise, a new QML module node named \a name
1252 is created and inserted into the collection, and the pointer
1253 to that node is returned.
1254 */
1255
1256/*!
1257 Looks up the group node named \a name in the collection
1258 of all group nodes. If a match is not found, a new group
1259 node named \a name is created and inserted into the collection.
1260 Then append \a node to the group's members list, and append the
1261 group name to the list of group names in \a node. The parent of
1262 \a node is not changed by this function. Returns a pointer to
1263 the group node.
1264 */
1265CollectionNode *Tree::addToGroup(const QString &name, Node *node)
1266{
1267 CollectionNode *cn = findGroup(name);
1268 if (!node->isInternal()) {
1269 cn->addMember(node);
1270 node->appendGroupName(name);
1271 }
1272 return cn;
1273}
1274
1275/*!
1276 Looks up the module node named \a name in the collection
1277 of all module nodes. If a match is not found, a new module
1278 node named \a name is created and inserted into the collection.
1279 Then append \a node to the module's members list. The parent of
1280 \a node is not changed by this function. Returns the module node.
1281 */
1282CollectionNode *Tree::addToModule(const QString &name, Node *node)
1283{
1284 CollectionNode *cn = findModule(name);
1285 cn->addMember(node);
1286 node->setPhysicalModuleName(name);
1287 return cn;
1288}
1289
1290/*!
1291 Looks up the QML module named \a name. If it isn't there,
1292 create it. Then append \a node to the QML module's member
1293 list. The parent of \a node is not changed by this function.
1294 Returns the pointer to the QML module node.
1295 */
1296CollectionNode *Tree::addToQmlModule(const QString &name, Node *node)
1297{
1298 QStringList qmid;
1299 QStringList dotSplit;
1300 QStringList blankSplit = name.split(QLatin1Char(' '));
1301 qmid.append(blankSplit[0]);
1302 if (blankSplit.size() > 1) {
1303 qmid.append(blankSplit[0] + blankSplit[1]);
1304 dotSplit = blankSplit[1].split(QLatin1Char('.'));
1305 qmid.append(blankSplit[0] + dotSplit[0]);
1306 }
1307
1308 CollectionNode *cn = findQmlModule(blankSplit[0]);
1309 cn->addMember(node);
1310 node->setQmlModule(cn);
1311 if (node->isQmlType()) {
1312 QmlTypeNode *n = static_cast<QmlTypeNode *>(node);
1313 for (int i = 0; i < qmid.size(); ++i) {
1314 QString key = qmid[i] + "::" + node->name();
1315 insertQmlType(key, n);
1316 }
1317 // Also insert with unqualified name for context-aware disambiguation
1318 insertQmlType(node->name(), n);
1319 }
1320 return cn;
1321}
1322
1323/*!
1324 Inserts QML type node \a n with the specified \a key into the type map.
1325 Since the map is a QMultiMap, multiple types with the same name can coexist
1326 (e.g., Shape from different modules).
1327 */
1328void Tree::insertQmlType(const QString &key, QmlTypeNode *n)
1329{
1330 m_qmlTypeMap.insert(key, n);
1331}
1332
1333/*!
1334 Looks up and returns the QML type node identified by \a name. When multiple
1335 types with the same name exist (e.g., Shape from different modules), prefers
1336 the type from the same module as \a relative if provided. Returns the first
1337 match if no module relationship exists, or nullptr if not found.
1338 */
1339QmlTypeNode *Tree::lookupQmlType(const QString &name, const Node *relative) const
1340{
1341 auto values = m_qmlTypeMap.values(name);
1342 if (values.isEmpty())
1343 return nullptr;
1344
1345 // If no context or only one match, return first result
1346 if (!relative || values.size() == 1)
1347 return values.first();
1348
1349 // Prefer types from the same module as the relative context
1350 if (relative->isQmlType()) {
1351 const auto *relativeQmlType = static_cast<const QmlTypeNode *>(relative);
1352 const CollectionNode *relativeModule = relativeQmlType->logicalModule();
1353
1354 for (auto *candidate : values) {
1355 if (candidate->logicalModule() == relativeModule)
1356 return candidate;
1357 }
1358 }
1359
1360 // Fallback to first match
1361 return values.first();
1362}
1363
1364/*!
1365 Finds the function node with the specifried name \a path that
1366 also has the specified \a parameters and returns a pointer to
1367 the first matching function node if one is found.
1368
1369 This function begins searching the tree at \a relative for
1370 the \l {FunctionNode} {function node} identified by \a path
1371 that has the specified \a parameters. The \a flags are
1372 used to restrict the search. If a matching node is found, a
1373 pointer to it is returned. Otherwise, nullis returned. If
1374 \a relative is ull, the search begins at the tree root.
1375 */
1376const FunctionNode *Tree::findFunctionNode(const QStringList &path, const Parameters &parameters,
1377 const Node *relative, Genus genus) const
1378{
1379 if (path.size() == 3 && !path[0].isEmpty()
1380 && ((genus == Genus::QML) || (genus == Genus::DontCare))) {
1381 QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]), relative);
1382 if (qcn == nullptr) {
1383 QStringList p(path[1]);
1384 Node *n = findNodeByNameAndType(p, &Node::isQmlType);
1385 if ((n != nullptr) && n->isQmlType())
1386 qcn = static_cast<QmlTypeNode *>(n);
1387 }
1388 if (qcn != nullptr)
1389 return static_cast<const FunctionNode *>(qcn->findFunctionChild(path[2], parameters));
1390 }
1391
1392 if (relative == nullptr)
1393 relative = root();
1394 else if (genus != Genus::DontCare) {
1395 if (!(hasCommonGenusType(genus, relative->genus())))
1396 relative = root();
1397 }
1398
1399 do {
1400 Node *node = const_cast<Node *>(relative);
1401 int i;
1402
1403 for (i = 0; i < path.size(); ++i) {
1404 if (node == nullptr || !node->isAggregate())
1405 break;
1406
1407 Aggregate *aggregate = static_cast<Aggregate *>(node);
1408 Node *next = nullptr;
1409 if (i == path.size() - 1)
1410 next = aggregate->findFunctionChild(path.at(i), parameters);
1411 else
1412 next = aggregate->findChildNode(path.at(i), genus);
1413
1414 if ((next == nullptr) && aggregate->isClassNode()) {
1415 const ClassList bases = allBaseClasses(static_cast<const ClassNode *>(aggregate));
1416 for (auto *base : bases) {
1417 if (i == path.size() - 1)
1418 next = base->findFunctionChild(path.at(i), parameters);
1419 else
1420 next = base->findChildNode(path.at(i), genus);
1421
1422 if (next != nullptr)
1423 break;
1424 }
1425 }
1426
1427 node = next;
1428 } // for (i = 0; i < path.size(); ++i)
1429
1430 if (node && i == path.size() && node->isFunction()) {
1431 // A function node was found at the end of the path.
1432 // If it is not marked private, return it. If it is
1433 // marked private, then if it overrides a function,
1434 // find that function instead because it might not
1435 // be marked private. If all the overloads are
1436 // marked private, return the original function node.
1437 // This should be replace with findOverriddenFunctionNode().
1438 const FunctionNode *fn = static_cast<const FunctionNode *>(node);
1439 const FunctionNode *FN = fn;
1440 while (FN->isPrivate() && !FN->overridesThis().isEmpty()) {
1441 QStringList path = FN->overridesThis().split("::");
1442 FN = m_qdb->findFunctionNode(path, parameters, relative, genus);
1443 if (FN == nullptr)
1444 break;
1445 if (!FN->isPrivate())
1446 return FN;
1447 }
1448 return fn;
1449 }
1450 relative = relative->parent();
1451 } while (relative);
1452 return nullptr;
1453}
1454
1455/*!
1456 Search this tree recursively from \a parent to find a function
1457 node with the specified \a tag. If no function node is found
1458 with the required \a tag, return 0.
1459 */
1460FunctionNode *Tree::findFunctionNodeForTag(const QString &tag, Aggregate *parent)
1461{
1462 if (parent == nullptr)
1463 parent = root();
1464 const NodeList &children = parent->childNodes();
1465 for (Node *n : children) {
1466 if (n != nullptr && n->isFunction() && n->hasTag(tag))
1467 return static_cast<FunctionNode *>(n);
1468 }
1469 for (Node *n : children) {
1470 if (n != nullptr && n->isAggregate()) {
1471 n = findFunctionNodeForTag(tag, static_cast<Aggregate *>(n));
1472 if (n != nullptr)
1473 return static_cast<FunctionNode *>(n);
1474 }
1475 }
1476 return nullptr;
1477}
1478
1479/*!
1480 There should only be one macro node for macro name \a t.
1481 The macro node is not built until the \macro command is seen.
1482 */
1483FunctionNode *Tree::findMacroNode(const QString &t, const Aggregate *parent)
1484{
1485 if (parent == nullptr)
1486 parent = root();
1487 const NodeList &children = parent->childNodes();
1488 for (Node *n : children) {
1489 if (n != nullptr && (n->isMacro() || n->isFunction()) && n->name() == t)
1490 return static_cast<FunctionNode *>(n);
1491 }
1492 for (Node *n : children) {
1493 if (n != nullptr && n->isAggregate()) {
1494 FunctionNode *fn = findMacroNode(t, static_cast<Aggregate *>(n));
1495 if (fn != nullptr)
1496 return fn;
1497 }
1498 }
1499 return nullptr;
1500}
1501
1502/*!
1503 Add the class and struct names in \a arg to the \e {don't document}
1504 map.
1505 */
1506void Tree::addToDontDocumentMap(QString &arg)
1507{
1508 arg.remove(QChar('('));
1509 arg.remove(QChar(')'));
1510 QString t = arg.simplified();
1511 QStringList sl = t.split(QChar(' '));
1512 if (sl.isEmpty())
1513 return;
1514 for (const QString &s : sl) {
1515 if (!m_dontDocumentMap.contains(s))
1516 m_dontDocumentMap.insert(s, nullptr);
1517 }
1518}
1519
1520/*!
1521 The \e {don't document} map has been loaded with the names
1522 of classes and structs in the current module that are not
1523 documented and should not be documented. Now traverse the
1524 map, and for each class or struct name, find the class node
1525 that represents that class or struct and mark it with the
1526 \C DontDocument status.
1527
1528 This results in a map of the class and struct nodes in the
1529 module that are in the public API but are not meant to be
1530 used by anyone. They are only used internally, but for one
1531 reason or another, they must have public visibility.
1532 */
1534{
1535 for (auto it = m_dontDocumentMap.begin(); it != m_dontDocumentMap.end(); ++it) {
1536 Aggregate *node = findAggregate(it.key());
1537 if (node != nullptr)
1539 }
1540}
1541
1542QT_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:1533
void addToDontDocumentMap(QString &arg)
Add the class and struct names in arg to the {don't document} map.
Definition tree.cpp:1506
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:150
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:236
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:989
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:112
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:260
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:122
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:868
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:240
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:154
virtual bool isAttached() const
Returns true if the QML property or QML method node is marked as attached.
Definition node.h:143
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:209
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:137
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:232
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:229
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:113
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:942
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:144
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:121
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