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