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