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