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
qdocdatabase.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 "qdocdatabase.h"
5
6#include "atom.h"
8#include "functionnode.h"
9#include "generator.h"
10#include "genustypes.h"
11#include "qdocindexfiles.h"
12#include "qdoclogging.h"
14#include "qmltypenode.h"
15#include "tree.h"
16#include "utilities.h"
17
18#include <QtCore/qregularexpression.h>
19#include <stack>
20
21QT_BEGIN_NAMESPACE
22
23using namespace Qt::StringLiterals;
25
26/*!
27 \class QDocForest
28
29 A class representing a forest of Tree objects.
30
31 This private class manages a collection of Tree objects (a
32 forest) for the singleton QDocDatabase object. It is only
33 accessed by that singleton QDocDatabase object, which is a
34 friend. Each tree in the forest is an instance of class
35 Tree, which is a mostly private class. Both QDocForest and
36 QDocDatabase are friends of Tree and have full access.
37
38 There are two kinds of trees in the forest, differing not
39 in structure but in use. One Tree is the primary tree. It
40 is the tree representing the module being documented. All
41 the other trees in the forest are called index trees. Each
42 one represents the contents of the index file for one of
43 the modules the current module must be able to link to.
44
45 The instances of subclasses of Node in the primary tree
46 will contain documentation in an instance of Doc. The
47 index trees contain no documentation, and each Node in
48 an index tree is marked as an index node.
49
50 Each tree is named with the name of its module.
51
52 The search order is created by searchOrder(), if it has
53 not already been created. The search order and module
54 names arrays have parallel structure, i.e. modulNames_[i]
55 is the module name of the Tree at searchOrder_[i].
56
57 The primary tree is always the first tree in the search
58 order. i.e., when the database is searched, the primary
59 tree is always searched first, unless a specific tree is
60 being searched.
61 */
62
63/*!
64 Destroys the qdoc forest. This requires deleting
65 each Tree in the forest. Note that the forest has
66 been transferred into the search order array, so
67 what is really being used to destroy the forest
68 is the search order array.
69 */
70QDocForest::~QDocForest()
71{
72 for (auto *entry : m_searchOrder)
73 delete entry;
74 m_forest.clear();
75 m_searchOrder.clear();
76 m_indexSearchOrder.clear();
77 m_moduleNames.clear();
78 m_primaryTree = nullptr;
79}
80
81/*!
82 Initializes the forest prior to a traversal and
83 returns a pointer to the primary tree. If the
84 forest is empty, it returns \nullptr.
85 */
86Tree *QDocForest::firstTree()
87{
88 m_currentIndex = 0;
89 return (!searchOrder().isEmpty() ? searchOrder()[0] : nullptr);
90}
91
92/*!
93 Increments the forest's current tree index. If the current
94 tree index is still within the forest, the function returns
95 the pointer to the current tree. Otherwise it returns \nullptr.
96 */
97Tree *QDocForest::nextTree()
98{
99 ++m_currentIndex;
100 return (m_currentIndex < searchOrder().size() ? searchOrder()[m_currentIndex] : nullptr);
101}
102
103/*!
104 \fn Tree *QDocForest::primaryTree()
105
106 Returns the pointer to the primary tree.
107 */
108
109/*!
110 Finds the tree for module \a t in the forest and
111 sets the primary tree to be that tree. After the
112 primary tree is set, that tree is removed from the
113 forest.
114
115 \node It gets re-inserted into the forest after the
116 search order is built.
117 */
118void QDocForest::setPrimaryTree(const QString &t)
119{
120 QString T = t.toLower();
121 m_primaryTree = findTree(T);
122 m_forest.remove(T);
123 if (m_primaryTree == nullptr)
124 qCCritical(lcQdoc) << "Error: Could not set primary tree to" << t;
125}
126
127/*!
128 If the search order array is empty, create the search order.
129 If the search order array is not empty, do nothing.
130 */
131void QDocForest::setSearchOrder(const QStringList &t)
132{
133 if (!m_searchOrder.isEmpty())
134 return;
135
136 /* Allocate space for the search order. */
137 m_searchOrder.reserve(m_forest.size() + 1);
138 m_searchOrder.clear();
139 m_moduleNames.reserve(m_forest.size() + 1);
140 m_moduleNames.clear();
141
142 /* The primary tree is always first in the search order. */
143 QString primaryName = primaryTree()->physicalModuleName();
144 m_searchOrder.append(m_primaryTree);
145 m_moduleNames.append(primaryName);
146 m_forest.remove(primaryName);
147
148 for (const QString &m : t) {
149 if (primaryName != m) {
150 auto it = m_forest.find(m);
151 if (it != m_forest.end()) {
152 m_searchOrder.append(it.value());
153 m_moduleNames.append(m);
154 m_forest.remove(m);
155 }
156 }
157 }
158 /*
159 If any trees remain in the forest, just add them
160 to the search order sequentially, because we don't
161 know any better at this point.
162 */
163 if (!m_forest.isEmpty()) {
164 for (auto it = m_forest.begin(); it != m_forest.end(); ++it) {
165 m_searchOrder.append(it.value());
166 m_moduleNames.append(it.key());
167 }
168 m_forest.clear();
169 }
170
171 /*
172 Rebuild the forest after constructing the search order.
173 It was destroyed during construction of the search order,
174 but it is needed for module-specific searches.
175
176 Note that this loop also inserts the primary tree into the
177 forrest. That is a requirement.
178 */
179 for (int i = 0; i < m_searchOrder.size(); ++i) {
180 if (!m_forest.contains(m_moduleNames.at(i))) {
181 m_forest.insert(m_moduleNames.at(i), m_searchOrder.at(i));
182 }
183 }
184}
185
186/*!
187 Returns an ordered array of Tree pointers that represents
188 the order in which the trees should be searched. The first
189 Tree in the array is the tree for the current module, i.e.
190 the module for which qdoc is generating documentation.
191
192 The other Tree pointers in the array represent the index
193 files that were loaded in preparation for generating this
194 module's documentation. Each Tree pointer represents one
195 index file. The index file Tree points have been ordered
196 heuristically to, hopefully, minimize searching. Thr order
197 will probably be changed.
198
199 If the search order array is empty, this function calls
200 indexSearchOrder(). The search order array is empty while
201 the index files are being loaded, but some searches must
202 be performed during this time, notably searches for base
203 class nodes. These searches require a temporary search
204 order. The temporary order changes throughout the loading
205 of the index files, but it is always the tree for the
206 current index file first, followed by the trees for the
207 index files that have already been loaded. The only
208 ordering required in this temporary search order is that
209 the current tree must be searched first.
210 */
211const QList<Tree *> &QDocForest::searchOrder()
212{
213 if (m_searchOrder.isEmpty())
214 return indexSearchOrder();
215 return m_searchOrder;
216}
217
218/*!
219 There are two search orders used by qdoc when searching for
220 things. The normal search order is returned by searchOrder(),
221 but this normal search order is not known until all the index
222 files have been read. At that point, setSearchOrder() is
223 called.
224
225 During the reading of the index files, the vector holding
226 the normal search order remains empty. Whenever the search
227 order is requested, if that vector is empty, this function
228 is called to return a temporary search order, which includes
229 all the index files that have been read so far, plus the
230 one being read now. That one is prepended to the front of
231 the vector.
232 */
233const QList<Tree *> &QDocForest::indexSearchOrder()
234{
235 if (m_forest.size() > m_indexSearchOrder.size())
236 m_indexSearchOrder.prepend(m_primaryTree);
237 return m_indexSearchOrder;
238}
239
240/*!
241 Create a new Tree for the index file for the specified
242 \a module and add it to the forest. Return the pointer
243 to its root.
244 */
245NamespaceNode *QDocForest::newIndexTree(const QString &module)
246{
247 m_primaryTree = new Tree(module, m_qdb);
248 m_forest.insert(module.toLower(), m_primaryTree);
249 return m_primaryTree->root();
250}
251
252/*!
253 Create a new Tree for use as the primary tree. This tree
254 will represent the primary module. \a module is camel case.
255 */
256void QDocForest::newPrimaryTree(const QString &module)
257{
258 m_primaryTree = new Tree(module, m_qdb);
259}
260
261/*!
262 Searches through the forest for a node named \a targetPath
263 and returns a pointer to it if found. The \a relative node
264 is the starting point. It only makes sense for the primary
265 tree, which is searched first. After the primary tree has
266 been searched, \a relative is set to 0 for searching the
267 other trees, which are all index trees. With relative set
268 to 0, the starting point for each index tree is the root
269 of the index tree.
270
271 If \a targetPath is resolved successfully but it refers to
272 a \\section title, continue the search, keeping the section
273 title as a fallback if no higher-priority targets are found.
274 */
275const Node *QDocForest::findNodeForTarget(QStringList &targetPath, const Node *relative,
276 Genus genus, QString &ref, int findFlags)
277{
278 int flags = SearchBaseClasses | SearchEnumValues | findFlags;
279
280 QString entity = targetPath.takeFirst();
281 QStringList entityPath = entity.split("::");
282
283 QString target;
284 if (!targetPath.isEmpty())
285 target = targetPath.takeFirst();
286
288 const Node *tocNode = nullptr;
289 for (const auto *tree : searchOrder()) {
290 const Node *n = tree->findNodeForTarget(entityPath, target, relative, flags, genus, ref, &type);
291 if (n) {
292 // Targets referring to non-section titles are returned immediately
293 if (type != TargetRec::Contents)
294 return n;
295 if (!tocNode)
296 tocNode = n;
297 }
298 relative = nullptr;
299 }
300 return tocNode;
301}
302
303/*!
304 Finds the FunctionNode for the qualified function name
305 in \a path, that also has the specified \a parameters.
306 Returns a pointer to the first matching function.
307
308 \a relative is a node in the primary tree where the search
309 should begin. It is only used when searching the primary
310 tree. \a genus can be used to force the search to find a
311 C++ function or a QML function.
312 */
313const FunctionNode *QDocForest::findFunctionNode(const QStringList &path,
314 const Parameters &parameters, const Node *relative,
315 Genus genus)
316{
317 for (const auto *tree : searchOrder()) {
318 const FunctionNode *fn = tree->findFunctionNode(path, parameters, relative, genus);
319 if (fn)
320 return fn;
321 relative = nullptr;
322 }
323 return nullptr;
324}
325
326/*! \class QDocDatabase
327 This class provides exclusive access to the qdoc database,
328 which consists of a forrest of trees and a lot of maps and
329 other useful data structures.
330 */
331
332QDocDatabase *QDocDatabase::s_qdocDB = nullptr;
333NodeMap QDocDatabase::s_typeNodeMap;
334NodeMultiMap QDocDatabase::s_obsoleteClasses;
335NodeMultiMap QDocDatabase::s_classesWithObsoleteMembers;
336NodeMultiMap QDocDatabase::s_obsoleteQmlTypes;
337NodeMultiMap QDocDatabase::s_qmlTypesWithObsoleteMembers;
338NodeMultiMap QDocDatabase::s_cppClasses;
339NodeMultiMap QDocDatabase::s_qmlBasicTypes;
340NodeMultiMap QDocDatabase::s_qmlTypes;
341NodeMultiMap QDocDatabase::s_examples;
342NodeMultiMapMap QDocDatabase::s_newClassMaps;
343NodeMultiMapMap QDocDatabase::s_newQmlTypeMaps;
344NodeMultiMapMap QDocDatabase::s_newEnumValueMaps;
345NodeMultiMapMap QDocDatabase::s_newSinceMaps;
346
347/*!
348 Constructs the singleton qdoc database object. The singleton
349 constructs the \a forest_ object, which is also a singleton.
350 \a m_showInternal is normally false. If it is true, qdoc will
351 write documentation for nodes marked \c internal.
352
353 \a singleExec_ is false when qdoc is being used in the standard
354 way of running qdoc twices for each module, first with --prepare
355 and then with --generate. First the --prepare phase is run for
356 each module, then the --generate phase is run for each module.
357
358 When \a singleExec_ is true, qdoc is run only once. During the
359 single execution, qdoc processes the qdocconf files for all the
360 modules sequentially in a loop. Each source file for each module
361 is read exactly once.
362 */
363QDocDatabase::QDocDatabase() : m_forest(this)
364{
365 // nothing
366}
367
368/*!
369 Creates the singleton. Allows only one instance of the class
370 to be created. Returns a pointer to the singleton.
371*/
373{
374 if (s_qdocDB == nullptr) {
375 s_qdocDB = new QDocDatabase;
376 initializeDB();
377 }
378 return s_qdocDB;
379}
380
381/*!
382 Destroys the singleton.
383 */
385{
386 if (s_qdocDB != nullptr) {
387 delete s_qdocDB;
388 s_qdocDB = nullptr;
389 }
390}
391
392/*!
393 Initialize data structures in the singleton qdoc database.
394
395 In particular, the type node map is initialized with a lot
396 type names that don't refer to documented types. For example,
397 many C++ standard types are included. These might be documented
398 here at some point, but for now they are not. Other examples
399 include \c array and \c data, which are just generic names
400 used as place holders in function signatures that appear in
401 the documentation.
402
403 \note Do not add QML basic types into this list as it will
404 break linking to those types.
405 */
406void QDocDatabase::initializeDB()
407{
408 s_typeNodeMap.insert("accepted", nullptr);
409 s_typeNodeMap.insert("actionPerformed", nullptr);
410 s_typeNodeMap.insert("activated", nullptr);
411 s_typeNodeMap.insert("alias", nullptr);
412 s_typeNodeMap.insert("anchors", nullptr);
413 s_typeNodeMap.insert("any", nullptr);
414 s_typeNodeMap.insert("array", nullptr);
415 s_typeNodeMap.insert("autoSearch", nullptr);
416 s_typeNodeMap.insert("axis", nullptr);
417 s_typeNodeMap.insert("backClicked", nullptr);
418 s_typeNodeMap.insert("boomTime", nullptr);
419 s_typeNodeMap.insert("border", nullptr);
420 s_typeNodeMap.insert("buttonClicked", nullptr);
421 s_typeNodeMap.insert("callback", nullptr);
422 s_typeNodeMap.insert("char", nullptr);
423 s_typeNodeMap.insert("clicked", nullptr);
424 s_typeNodeMap.insert("close", nullptr);
425 s_typeNodeMap.insert("closed", nullptr);
426 s_typeNodeMap.insert("cond", nullptr);
427 s_typeNodeMap.insert("data", nullptr);
428 s_typeNodeMap.insert("dataReady", nullptr);
429 s_typeNodeMap.insert("dateString", nullptr);
430 s_typeNodeMap.insert("dateTimeString", nullptr);
431 s_typeNodeMap.insert("datetime", nullptr);
432 s_typeNodeMap.insert("day", nullptr);
433 s_typeNodeMap.insert("deactivated", nullptr);
434 s_typeNodeMap.insert("drag", nullptr);
435 s_typeNodeMap.insert("easing", nullptr);
436 s_typeNodeMap.insert("error", nullptr);
437 s_typeNodeMap.insert("exposure", nullptr);
438 s_typeNodeMap.insert("fatalError", nullptr);
439 s_typeNodeMap.insert("fileSelected", nullptr);
440 s_typeNodeMap.insert("flags", nullptr);
441 s_typeNodeMap.insert("float", nullptr);
442 s_typeNodeMap.insert("focus", nullptr);
443 s_typeNodeMap.insert("focusZone", nullptr);
444 s_typeNodeMap.insert("format", nullptr);
445 s_typeNodeMap.insert("framePainted", nullptr);
446 s_typeNodeMap.insert("from", nullptr);
447 s_typeNodeMap.insert("frontClicked", nullptr);
448 s_typeNodeMap.insert("function", nullptr);
449 s_typeNodeMap.insert("hasOpened", nullptr);
450 s_typeNodeMap.insert("hovered", nullptr);
451 s_typeNodeMap.insert("hoveredTitle", nullptr);
452 s_typeNodeMap.insert("hoveredUrl", nullptr);
453 s_typeNodeMap.insert("imageCapture", nullptr);
454 s_typeNodeMap.insert("imageProcessing", nullptr);
455 s_typeNodeMap.insert("index", nullptr);
456 s_typeNodeMap.insert("initialized", nullptr);
457 s_typeNodeMap.insert("isLoaded", nullptr);
458 s_typeNodeMap.insert("item", nullptr);
459 s_typeNodeMap.insert("key", nullptr);
460 s_typeNodeMap.insert("keysequence", nullptr);
461 s_typeNodeMap.insert("listViewClicked", nullptr);
462 s_typeNodeMap.insert("loadRequest", nullptr);
463 s_typeNodeMap.insert("locale", nullptr);
464 s_typeNodeMap.insert("location", nullptr);
465 s_typeNodeMap.insert("long", nullptr);
466 s_typeNodeMap.insert("message", nullptr);
467 s_typeNodeMap.insert("messageReceived", nullptr);
468 s_typeNodeMap.insert("mode", nullptr);
469 s_typeNodeMap.insert("month", nullptr);
470 s_typeNodeMap.insert("name", nullptr);
471 s_typeNodeMap.insert("number", nullptr);
472 s_typeNodeMap.insert("object", nullptr);
473 s_typeNodeMap.insert("offset", nullptr);
474 s_typeNodeMap.insert("ok", nullptr);
475 s_typeNodeMap.insert("openCamera", nullptr);
476 s_typeNodeMap.insert("openImage", nullptr);
477 s_typeNodeMap.insert("openVideo", nullptr);
478 s_typeNodeMap.insert("padding", nullptr);
479 s_typeNodeMap.insert("parent", nullptr);
480 s_typeNodeMap.insert("path", nullptr);
481 s_typeNodeMap.insert("photoModeSelected", nullptr);
482 s_typeNodeMap.insert("position", nullptr);
483 s_typeNodeMap.insert("precision", nullptr);
484 s_typeNodeMap.insert("presetClicked", nullptr);
485 s_typeNodeMap.insert("preview", nullptr);
486 s_typeNodeMap.insert("previewSelected", nullptr);
487 s_typeNodeMap.insert("progress", nullptr);
488 s_typeNodeMap.insert("puzzleLost", nullptr);
489 s_typeNodeMap.insert("qmlSignal", nullptr);
490 s_typeNodeMap.insert("rectangle", nullptr);
491 s_typeNodeMap.insert("request", nullptr);
492 s_typeNodeMap.insert("requestId", nullptr);
493 s_typeNodeMap.insert("section", nullptr);
494 s_typeNodeMap.insert("selected", nullptr);
495 s_typeNodeMap.insert("send", nullptr);
496 s_typeNodeMap.insert("settingsClicked", nullptr);
497 s_typeNodeMap.insert("shoe", nullptr);
498 s_typeNodeMap.insert("short", nullptr);
499 s_typeNodeMap.insert("signed", nullptr);
500 s_typeNodeMap.insert("sizeChanged", nullptr);
501 s_typeNodeMap.insert("size_t", nullptr);
502 s_typeNodeMap.insert("sockaddr", nullptr);
503 s_typeNodeMap.insert("someOtherSignal", nullptr);
504 s_typeNodeMap.insert("sourceSize", nullptr);
505 s_typeNodeMap.insert("startButtonClicked", nullptr);
506 s_typeNodeMap.insert("state", nullptr);
507 s_typeNodeMap.insert("std::initializer_list", nullptr);
508 s_typeNodeMap.insert("std::list", nullptr);
509 s_typeNodeMap.insert("std::map", nullptr);
510 s_typeNodeMap.insert("std::pair", nullptr);
511 s_typeNodeMap.insert("std::string", nullptr);
512 s_typeNodeMap.insert("std::vector", nullptr);
513 s_typeNodeMap.insert("stringlist", nullptr);
514 s_typeNodeMap.insert("swapPlayers", nullptr);
515 s_typeNodeMap.insert("symbol", nullptr);
516 s_typeNodeMap.insert("t", nullptr);
517 s_typeNodeMap.insert("T", nullptr);
518 s_typeNodeMap.insert("tagChanged", nullptr);
519 s_typeNodeMap.insert("timeString", nullptr);
520 s_typeNodeMap.insert("timeout", nullptr);
521 s_typeNodeMap.insert("to", nullptr);
522 s_typeNodeMap.insert("toggled", nullptr);
523 s_typeNodeMap.insert("type", nullptr);
524 s_typeNodeMap.insert("unsigned", nullptr);
525 s_typeNodeMap.insert("urllist", nullptr);
526 s_typeNodeMap.insert("va_list", nullptr);
527 s_typeNodeMap.insert("value", nullptr);
528 s_typeNodeMap.insert("valueEmitted", nullptr);
529 s_typeNodeMap.insert("videoFramePainted", nullptr);
530 s_typeNodeMap.insert("videoModeSelected", nullptr);
531 s_typeNodeMap.insert("videoRecorder", nullptr);
532 s_typeNodeMap.insert("void", nullptr);
533 s_typeNodeMap.insert("volatile", nullptr);
534 s_typeNodeMap.insert("wchar_t", nullptr);
535 s_typeNodeMap.insert("x", nullptr);
536 s_typeNodeMap.insert("y", nullptr);
537 s_typeNodeMap.insert("zoom", nullptr);
538 s_typeNodeMap.insert("zoomTo", nullptr);
539}
540
541/*! \fn NamespaceNode *QDocDatabase::primaryTreeRoot()
542 Returns a pointer to the root node of the primary tree.
543 */
544
545/*!
546 \fn const CNMap &QDocDatabase::groups()
547 Returns a const reference to the collection of all
548 group nodes in the primary tree.
549*/
550
551/*!
552 \fn const CNMap &QDocDatabase::modules()
553 Returns a const reference to the collection of all
554 module nodes in the primary tree.
555*/
556
557/*!
558 \fn const CNMap &QDocDatabase::qmlModules()
559 Returns a const reference to the collection of all
560 QML module nodes in the primary tree.
561*/
562
563/*! \fn CollectionNode *QDocDatabase::findGroup(const QString &name)
564 Find the group node named \a name and return a pointer
565 to it. If a matching node is not found, add a new group
566 node named \a name and return a pointer to that one.
567
568 If a new group node is added, its parent is the tree root,
569 and the new group node is marked \e{not seen}.
570 */
571
572/*! \fn CollectionNode *QDocDatabase::findModule(const QString &name)
573 Find the module node named \a name and return a pointer
574 to it. If a matching node is not found, add a new module
575 node named \a name and return a pointer to that one.
576
577 If a new module node is added, its parent is the tree root,
578 and the new module node is marked \e{not seen}.
579 */
580
581/*! \fn CollectionNode *QDocDatabase::addGroup(const QString &name)
582 Looks up the group named \a name in the primary tree. If
583 a match is found, a pointer to the node is returned.
584 Otherwise, a new group node named \a name is created and
585 inserted into the collection, and the pointer to that node
586 is returned.
587 */
588
589/*! \fn CollectionNode *QDocDatabase::addModule(const QString &name)
590 Looks up the module named \a name in the primary tree. If
591 a match is found, a pointer to the node is returned.
592 Otherwise, a new module node named \a name is created and
593 inserted into the collection, and the pointer to that node
594 is returned.
595 */
596
597/*! \fn CollectionNode *QDocDatabase::addQmlModule(const QString &name)
598 Looks up the QML module named \a name in the primary tree.
599 If a match is found, a pointer to the node is returned.
600 Otherwise, a new QML module node named \a name is created
601 and inserted into the collection, and the pointer to that
602 node is returned.
603 */
604
605/*! \fn CollectionNode *QDocDatabase::addToGroup(const QString &name, Node *node)
606 Looks up the group node named \a name in the collection
607 of all group nodes. If a match is not found, a new group
608 node named \a name is created and inserted into the collection.
609 Then append \a node to the group's members list, and append the
610 group node to the member list of the \a node. The parent of the
611 \a node is not changed by this function. Returns a pointer to
612 the group node.
613 */
614
615/*! \fn CollectionNode *QDocDatabase::addToModule(const QString &name, Node *node)
616 Looks up the module node named \a name in the collection
617 of all module nodes. If a match is not found, a new module
618 node named \a name is created and inserted into the collection.
619 Then append \a node to the module's members list. The parent of
620 \a node is not changed by this function. Returns the module node.
621 */
622
623/*! \fn Collection *QDocDatabase::addToQmlModule(const QString &name, Node *node)
624 Looks up the QML module named \a name. If it isn't there,
625 create it. Then append \a node to the QML module's member
626 list. The parent of \a node is not changed by this function.
627 */
628
629/*! \fn QmlTypeNode *QDocDatabase::findQmlType(const QString &name)
630 Returns the QML type node identified by the qualified
631 QML type \a name, or \c nullptr if no type was found.
632 */
633
634/*!
635 Returns the QML type node identified by the QML module id
636 \a qmid and QML type \a name, or \c nullptr if no type
637 was found.
638
639 If the QML module id is empty, looks up the QML type by
640 \a name only.
641 */
642QmlTypeNode *QDocDatabase::findQmlType(const QString &qmid, const QString &name, const Node *relative)
643{
644 if (!qmid.isEmpty()) {
645 if (auto *qcn = m_forest.lookupQmlType(qmid + u"::"_s + name, relative); qcn)
646 return qcn;
647 }
648
649 // Try unqualified lookup first (uses context-aware disambiguation)
650 if (auto *qcn = m_forest.lookupQmlType(name, relative); qcn)
651 return qcn;
652
653 // Fallback to path-based search
654 QStringList path(name);
655 return static_cast<QmlTypeNode *>(m_forest.findNodeByNameAndType(path, &Node::isQmlType));
656}
657
658/*!
659 Returns the QML type node identified by the QML module id
660 constructed from the strings in the import \a record and the
661 QML type \a name. Returns \c nullptr if no type was not found.
662 */
663QmlTypeNode *QDocDatabase::findQmlType(const ImportRec &record, const QString &name, const Node *relative)
664{
665 if (record.isEmpty())
666 return nullptr;
667
668 QString type{name};
669
670 // If the import is under a namespace (id) and the type name is not prefixed with that id,
671 // then we know the type is not available under this import.
672 if (!record.m_importId.isEmpty()) {
673 const QString namespacePrefix{"%1."_L1.arg(record.m_importId)};
674 if (!type.startsWith(namespacePrefix))
675 return nullptr;
676 type.remove(0, namespacePrefix.size());
677 }
678
679 const QString qmName = record.m_importUri.isEmpty() ? record.m_moduleName : record.m_importUri;
680 return m_forest.lookupQmlType(qmName + u"::"_s + type, relative);
681}
682
683/*!
684 Returns the QML node identified by the QML module id \a qmid
685 and \a name, searching in the primary tree only. If \a qmid
686 is an empty string, searches for the node using name only.
687
688 Returns \c nullptr if no node was found.
689*/
690QmlTypeNode *QDocDatabase::findQmlTypeInPrimaryTree(const QString &qmid, const QString &name)
691{
692 if (!qmid.isEmpty())
693 return primaryTree()->lookupQmlType(qmid + u"::"_s + name);
694 return static_cast<QmlTypeNode *>(primaryTreeRoot()->findChildNode(name, Genus::QML, TypesOnly));
695}
696
697/*!
698 This function calls a set of functions for each tree in the
699 forest that has not already been analyzed. In this way, when
700 running qdoc in \e singleExec mode, each tree is analyzed in
701 turn, and its classes and types are added to the appropriate
702 node maps.
703 */
705{
706 processForest(&QDocDatabase::findAllClasses);
707 processForest(&QDocDatabase::findAllFunctions);
708 processForest(&QDocDatabase::findAllObsoleteThings);
709 processForest(&QDocDatabase::findAllLegaleseTexts);
710 processForest(&QDocDatabase::findAllSince);
711 processForest(&QDocDatabase::findAllAttributions);
713}
714
715/*!
716 This function calls \a func for each tree in the forest,
717 ensuring that \a func is called only once per tree.
718
719 \sa processForest()
720 */
721void QDocDatabase::processForest(FindFunctionPtr func)
722{
723 Tree *t = m_forest.firstTree();
724 while (t) {
725 if (!m_completedFindFunctions.values(t).contains(func)) {
726 (this->*(func))(t->root());
727 m_completedFindFunctions.insert(t, func);
728 }
729 t = m_forest.nextTree();
730 }
731}
732
733/*!
734 Returns a reference to the collection of legalese texts.
735 */
737{
738 processForest(&QDocDatabase::findAllLegaleseTexts);
739 return m_legaleseTexts;
740}
741
742/*!
743 Returns a reference to the map of C++ classes with obsolete members.
744 */
746{
747 processForest(&QDocDatabase::findAllObsoleteThings);
748 return s_classesWithObsoleteMembers;
749}
750
751/*!
752 Returns a reference to the map of obsolete QML types.
753 */
755{
756 processForest(&QDocDatabase::findAllObsoleteThings);
757 return s_obsoleteQmlTypes;
758}
759
760/*!
761 Returns a reference to the map of QML types with obsolete members.
762 */
764{
765 processForest(&QDocDatabase::findAllObsoleteThings);
766 return s_qmlTypesWithObsoleteMembers;
767}
768
769/*!
770 Returns a reference to the map of QML basic types.
771 */
773{
774 processForest(&QDocDatabase::findAllClasses);
775 return s_qmlBasicTypes;
776}
777
778/*!
779 Returns a reference to the multimap of QML types.
780 */
782{
783 processForest(&QDocDatabase::findAllClasses);
784 return s_qmlTypes;
785}
786
787/*!
788 Returns a reference to the multimap of example nodes.
789 */
791{
792 processForest(&QDocDatabase::findAllClasses);
793 return s_examples;
794}
795
796/*!
797 Returns a reference to the multimap of attribution nodes.
798 */
800{
801 processForest(&QDocDatabase::findAllAttributions);
802 return m_attributions;
803}
804
805/*!
806 Returns a reference to the map of obsolete C++ clases.
807 */
809{
810 processForest(&QDocDatabase::findAllObsoleteThings);
811 return s_obsoleteClasses;
812}
813
814/*!
815 Returns a reference to the map of all C++ classes.
816 */
818{
819 processForest(&QDocDatabase::findAllClasses);
820 return s_cppClasses;
821}
822
823/*!
824 Returns the function index. This data structure is used to
825 output the function index page.
826 */
828{
829 processForest(&QDocDatabase::findAllFunctions);
830 return m_functionIndex;
831}
832
833/*!
834 Finds all the nodes containing legalese text and puts them
835 in a map.
836 */
837void QDocDatabase::findAllLegaleseTexts(Aggregate *node)
838{
839 for (const auto &childNode : node->childNodes()) {
840 if (childNode->isPrivate())
841 continue;
842 if (!childNode->doc().legaleseText().isEmpty())
843 m_legaleseTexts.insert(childNode->doc().legaleseText(), childNode);
844 if (childNode->isAggregate())
845 findAllLegaleseTexts(static_cast<Aggregate *>(childNode));
846 }
847}
848
849/*!
850 \fn void QDocDatabase::findAllObsoleteThings(Aggregate *node)
851
852 Finds all nodes with status = Deprecated and sorts them into
853 maps. They can be C++ classes, QML types, or they can be
854 functions, enum types, typedefs, methods, etc.
855 */
856
857/*!
858 \fn void QDocDatabase::findAllSince(Aggregate *node)
859
860 Finds all the nodes in \a node where a \e{since} command appeared
861 in the qdoc comment and sorts them into maps according to the kind
862 of node.
863
864 This function is used for generating the "New Classes... in x.y"
865 section on the \e{What's New in Qt x.y} page.
866 */
867
868/*!
869 \fn const CollectionNode *QDocDatabase::findConceptNode(const QString &name)
870
871 Non-creating, cross-tree lookup. findConcept()/findCollection() fabricate a
872 placeholder on a miss, which would shadow a dependency-module concept with an
873 empty primary-tree node and emit a dangling local href. getCollectionNode()
874 searches the same forest order without that side effect.
875 */
876
877/*!
878 Find the \a key in the map of new class maps, and return a
879 reference to the value, which is a NodeMap. If \a key is not
880 found, return a reference to an empty NodeMap.
881 */
882const NodeMultiMap &QDocDatabase::getClassMap(const QString &key)
883{
884 processForest(&QDocDatabase::findAllSince);
885 auto it = s_newClassMaps.constFind(key);
886 return (it != s_newClassMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
887}
888
889/*!
890 Find the \a key in the map of new QML type maps, and return a
891 reference to the value, which is a NodeMap. If the \a key is not
892 found, return a reference to an empty NodeMap.
893 */
894const NodeMultiMap &QDocDatabase::getQmlTypeMap(const QString &key)
895{
896 processForest(&QDocDatabase::findAllSince);
897 auto it = s_newQmlTypeMaps.constFind(key);
898 return (it != s_newQmlTypeMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
899}
900
901/*!
902 Find the \a key in the map of new \e {since} maps, and return
903 a reference to the value, which is a NodeMultiMap. If \a key
904 is not found, return a reference to an empty NodeMultiMap.
905 */
906const NodeMultiMap &QDocDatabase::getSinceMap(const QString &key)
907{
908 processForest(&QDocDatabase::findAllSince);
909 auto it = s_newSinceMaps.constFind(key);
910 return (it != s_newSinceMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
911}
912
913/*!
914 Performs several housekeeping tasks prior to generating the
915 documentation. These tasks create required data structures
916 and resolve links.
917 */
919{
920 const auto &config = Config::instance();
921 if (config.dualExec() || config.preparing()) {
922 // order matters
923 primaryTree()->resolveBaseClasses(primaryTreeRoot());
924 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
928 primaryTree()->removePrivateAndInternalBases(primaryTreeRoot());
930 primaryTree()->validatePropertyDocumentation(primaryTreeRoot());
933 primaryTree()->resolveTargets(primaryTreeRoot());
934 primaryTree()->resolveCppToQmlLinks();
935 primaryTree()->resolveSince(*primaryTreeRoot());
937 }
938 if (config.singleExec() && config.generating()) {
939 primaryTree()->resolveBaseClasses(primaryTreeRoot());
940 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
942 primaryTree()->resolveCppToQmlLinks();
943 primaryTree()->resolveSince(*primaryTreeRoot());
945 }
946 if (!config.preparing()) {
951 }
952 if (config.dualExec())
953 QDocIndexFiles::destroyQDocIndexFiles();
954}
955
957{
958 Tree *t = m_forest.firstTree();
959 while (t) {
960 t->resolveBaseClasses(t->root());
961 if (t != primaryTree())
962 t->root()->resolveQmlInheritance();
963 t = m_forest.nextTree();
964 }
965}
966
967/*!
968 Gathers the fully-qualified concept names referenced by \a node.
969 Template-head and direct-concept references live on the optional
970 RelaxedTemplateDeclaration carried by the node; trailing-requires
971 and constrained-auto references are accumulated on the FunctionNode.
972 */
973static void collectConceptReferences(const Node *node, QStringList &refs)
974{
975 if (const auto &td = node->templateDecl(); td.has_value()) {
976 for (const auto &name : td->referenced_concepts)
977 refs.append(QString::fromStdString(name));
978 }
979 if (node->isFunction()) {
980 const auto *fn = static_cast<const FunctionNode *>(node);
981 refs += fn->referencedConcepts();
982 }
983}
984
985/*!
986 Walks every documented node under \a parent and registers each constrained
987 item as a member of the concept's CollectionNode for each concept it
988 references.
989
990 Recurses into aggregates so the entire primary tree is covered.
991 */
993{
994 for (auto *child : std::as_const(parent->childNodes())) {
995 // A skipped node is not registered as a concept user, but its
996 // documented descendants still are: a documented class nested in an
997 // internal namespace, for example, should still appear in its concept's
998 // "Used by" list. Visibility filtering and recursion are therefore
999 // independent.
1000 const bool registerChild =
1001 !child->isPrivate() && !child->isInternal() && !child->isDontDocument();
1002
1003 if (registerChild) {
1004 QStringList refs;
1005 collectConceptReferences(child, refs);
1006 refs.sort();
1007 refs.removeDuplicates();
1008
1009 for (const QString &conceptName : std::as_const(refs)) {
1010 // Cross-tree lookup. The forest search order covers the primary
1011 // tree plus every dependency-module index tree. A concept
1012 // declared in module B and referenced from module A can be
1013 // found through the same cross-tree lookup machinery.
1014 // Concepts without a \\concept block anywhere in the corpus
1015 // return \c{nullptr} and are silently skipped; references to
1016 // undocumented concepts (such as std::integral) are common and
1017 // not actionable.
1018 //
1019 // addMember() deduplicates membership centrally, so re-running
1020 // this pass is safe and produces a stable "Used by" listing
1021 // without per-call guard logic.
1022 if (auto *cn = db.findMutableCollectionNode(conceptName, NodeType::Concept))
1023 cn->addMember(child);
1024 }
1025 }
1026
1027 if (child->isAggregate())
1028 registerConceptUsersUnder(static_cast<Aggregate *>(child), db);
1029 }
1030}
1031
1032/*!
1033 Builds the concept-to-users reverse index after parsing finishes
1034 and before any generator runs. The forward direction — which
1035 concepts a constrained declaration references — is captured
1036 during the libclang AST pass; this pass turns those isolated
1037 references into bidirectional collection membership so concept
1038 reference pages can render a \e {Used by} section through the
1039 same scaffolding group and module collection pages already use.
1040 */
1042{
1043 registerConceptUsersUnder(primaryTreeRoot(), *this);
1044}
1045
1046/*!
1047 Returns a reference to the namespace map. Constructs the
1048 namespace map if it hasn't been constructed yet.
1049
1050 \note This function must not be called in the prepare phase.
1051 */
1053{
1055 return m_namespaceIndex;
1056}
1057
1058/*!
1059 Multiple namespace nodes for namespace X can exist in the
1060 qdoc database in different trees. This function first finds
1061 all namespace nodes in all the trees and inserts them into
1062 a multimap. Then it combines all the namespace nodes that
1063 have the same name into a single namespace node of that
1064 name and inserts that combined namespace node into an index.
1065 */
1067{
1068 if (!m_namespaceIndex.isEmpty())
1069 return;
1070
1071 bool linkErrors = !Config::instance().get(CONFIG_NOLINKERRORS).asBool();
1072 NodeMultiMap namespaceMultimap;
1073 Tree *t = m_forest.firstTree();
1074 while (t) {
1075 t->root()->findAllNamespaces(namespaceMultimap);
1076 t = m_forest.nextTree();
1077 }
1078 const QList<QString> keys = namespaceMultimap.uniqueKeys();
1079 for (const QString &key : keys) {
1080 NamespaceNode *ns = nullptr;
1081 NamespaceNode *indexNamespace = nullptr;
1082 const NodeList namespaces = namespaceMultimap.values(key);
1083 qsizetype count = namespaceMultimap.remove(key);
1084 if (count > 0) {
1085 for (auto *node : namespaces) {
1086 ns = static_cast<NamespaceNode *>(node);
1087 if (ns->isDocumentedHere())
1088 break;
1089 else if (ns->hadDoc())
1090 indexNamespace = ns; // namespace was documented but in another tree
1091 ns = nullptr;
1092 }
1093 if (ns) {
1094 for (auto *node : namespaces) {
1095 auto *nsNode = static_cast<NamespaceNode *>(node);
1096 if (nsNode->hadDoc() && nsNode != ns) {
1097 ns->doc().location().warning(
1098 QStringLiteral("Namespace %1 documented more than once")
1099 .arg(nsNode->name()), QStringLiteral("also seen here: %1")
1100 .arg(nsNode->doc().location().toString()));
1101 }
1102 }
1103 } else if (!indexNamespace) {
1104 // Warn about documented children in undocumented namespaces.
1105 // As the namespace can be documented outside this project,
1106 // skip the warning if --no-link-errors is set
1107 if (linkErrors) {
1108 for (auto *node : namespaces) {
1109 if (!node->isIndexNode())
1110 static_cast<NamespaceNode *>(node)->reportDocumentedChildrenInUndocumentedNamespace();
1111 }
1112 }
1113 } else {
1114 for (auto *node : namespaces) {
1115 auto *nsNode = static_cast<NamespaceNode *>(node);
1116 if (nsNode != indexNamespace)
1117 nsNode->setDocNode(indexNamespace);
1118 }
1119 }
1120 }
1121 /*
1122 If there are multiple namespace nodes with the same
1123 name where one of them will be the main reference page
1124 for the namespace, include all nodes in the public
1125 API of the namespace.
1126 */
1127 if (ns && count > 1) {
1128 for (auto *node : namespaces) {
1129 auto *nameSpaceNode = static_cast<NamespaceNode *>(node);
1130 if (nameSpaceNode != ns) {
1131 for (auto it = nameSpaceNode->constBegin(); it != nameSpaceNode->constEnd();
1132 ++it) {
1133 Node *anotherNs = *it;
1134 if (anotherNs && anotherNs->isPublic() && !anotherNs->isInternal())
1135 ns->includeChild(anotherNs);
1136 }
1137 }
1138 }
1139 }
1140 /*
1141 Add the main namespace reference node to index, or the last seen
1142 namespace if the main one was not found.
1143 */
1144 if (!ns)
1145 ns = indexNamespace ? indexNamespace : static_cast<NamespaceNode *>(namespaces.last());
1146 m_namespaceIndex.insert(ns->name(), ns);
1147 }
1148}
1149
1150/*!
1151 Each instance of class Tree that represents an index file
1152 must be traversed to find all instances of class ProxyNode.
1153 For each ProxyNode found, look up the ProxyNode's name in
1154 the primary Tree. If it is found, it means that the proxy
1155 node contains elements (normally just functions) that are
1156 documented in the module represented by the Tree containing
1157 the proxy node but that are related to the node we found in
1158 the primary tree.
1159 */
1161{
1162 // The first tree is the primary tree.
1163 // Skip the primary tree.
1164 Tree *t = m_forest.firstTree();
1165 t = m_forest.nextTree();
1166 while (t) {
1167 const NodeList &proxies = t->proxies();
1168 if (!proxies.isEmpty()) {
1169 for (auto *node : proxies) {
1170 const auto *pn = static_cast<ProxyNode *>(node);
1171 if (pn->count() > 0) {
1172 Aggregate *aggregate = primaryTree()->findAggregate(pn->name());
1173 if (aggregate != nullptr)
1174 aggregate->appendToRelatedByProxy(pn->childNodes());
1175 }
1176 }
1177 }
1178 t = m_forest.nextTree();
1179 }
1180}
1181
1182/*!
1183 Finds the function node for the qualified function path in
1184 \a target and returns a pointer to it. The \a target is a
1185 function signature with or without parameters but without
1186 the return type.
1187
1188 \a relative is the node in the primary tree where the search
1189 begins. It is not used in the other trees, if the node is not
1190 found in the primary tree. \a genus can be used to force the
1191 search to find a C++ function or a QML function.
1192
1193 The entire forest is searched, but the first match is accepted.
1194 */
1195const FunctionNode *QDocDatabase::findFunctionNode(const QString &target, const Node *relative,
1196 Genus genus)
1197{
1198 QString signature;
1199 QString function = target;
1200 qsizetype length = target.size();
1201 if (function.endsWith("()"))
1202 function.chop(2);
1203 if (function.endsWith(QChar(')'))) {
1204 qsizetype position = function.lastIndexOf(QChar('('));
1205 signature = function.mid(position + 1, length - position - 2);
1206 function = function.left(position);
1207 }
1208 QStringList path = function.split("::");
1209 return m_forest.findFunctionNode(path, Parameters(signature), relative, genus);
1210}
1211
1212/*!
1213 This function is called for autolinking to a \a type,
1214 which could be a function return type or a parameter
1215 type. The tree node that represents the \a type is
1216 returned. All the trees are searched until a match is
1217 found. When searching the primary tree, the search
1218 begins at \a relative and proceeds up the parent chain.
1219 When searching the index trees, the search begins at the
1220 root.
1221 */
1222const Node *QDocDatabase::findTypeNode(const QString &type, const Node *relative, Genus genus)
1223{
1224 // For QML contexts with qualified names containing ".", try import-aware lookup first
1225 if ((genus == Genus::QML || (relative && relative->genus() == Genus::QML)) &&
1226 type.contains('.') && !type.contains("::")) {
1227 if (relative && relative->isQmlType()) {
1228 const QmlTypeNode *qmlType = static_cast<const QmlTypeNode*>(relative);
1229 const ImportList &imports = qmlType->importList();
1230
1231 for (const auto &import : imports) {
1232 if (QmlTypeNode *found = findQmlType(import, type)) {
1233 return found;
1234 }
1235 }
1236 }
1237
1238 // Fall back to regular path-based lookup for QML qualified names
1239 QStringList path = type.split(".");
1240 if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
1241 auto it = s_typeNodeMap.find(path.at(0));
1242 if (it != s_typeNodeMap.end())
1243 return it.value();
1244 }
1245
1246 // Try the full qualified path first
1247 const Node *node = m_forest.findTypeNode(path, relative, genus);
1248 if (node)
1249 return node;
1250
1251 // If the full path fails and we have multiple segments, try just the last segment
1252 // This handles cases like "TM.BaseType" where "TM" is an alias we can't resolve
1253 // but "BaseType" might be findable as a QML type
1254 if (path.size() > 1) {
1255 const Node *lastSegmentNode = m_forest.findTypeNode(QStringList{path.last()}, relative, genus);
1256 if (lastSegmentNode && lastSegmentNode->isQmlType())
1257 return lastSegmentNode;
1258 }
1259
1260 return nullptr;
1261 }
1262
1263 // For C++ contexts or QML types with "::" notation, use C++ path splitting
1264 QStringList path = type.split("::");
1265 if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
1266 auto it = s_typeNodeMap.find(path.at(0));
1267 if (it != s_typeNodeMap.end())
1268 return it.value();
1269 }
1270 return m_forest.findTypeNode(path, relative, genus);
1271}
1272
1273/*!
1274 Finds the node that will generate the documentation that
1275 contains the \a target and returns a pointer to it.
1276
1277 Can this be improved by using the target map in Tree?
1278 */
1279const Node *QDocDatabase::findNodeForTarget(const QString &target, const Node *relative)
1280{
1281 const Node *node = nullptr;
1282 if (target.isEmpty())
1283 node = relative;
1284 else if (target.endsWith(".html"))
1285 node = findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1286 else {
1287 QStringList path = target.split("::");
1288 int flags = SearchBaseClasses | SearchEnumValues;
1289 for (const auto *tree : searchOrder()) {
1290 const Node *n = tree->findNode(path, relative, flags, Genus::DontCare);
1291 if (n)
1292 return n;
1293 relative = nullptr;
1294 }
1295 node = findPageNodeByTitle(target);
1296 }
1297 return node;
1298}
1299
1300/*!
1301 Finds the node for \a target with genus and module scoping.
1302
1303 When \a moduleName is non-empty, the search is scoped to that
1304 module's tree. Function signatures (targets ending with \c{()})
1305 are parsed and dispatched to function-specific lookup. QML
1306 dot-path targets are resolved via import-aware type lookup when
1307 the \a genus is QML.
1308
1309 This overload extracts the dispatch logic from findNodeForAtom()
1310 into a method that takes plain parameters instead of an Atom
1311 pointer, enabling callers without Atom access (such as
1312 LinkResolver) to use the same scoped lookup.
1313*/
1314const Node *QDocDatabase::findNodeForTarget(const QString &target, const Node *relative,
1315 Genus genus, const QString &moduleName,
1316 QString *ref)
1317{
1318 if (target.isEmpty())
1319 return relative;
1320
1321 Tree *domain = moduleName.isEmpty() ? nullptr : findTree(moduleName);
1322
1323 if (domain) {
1324 const Node *node = nullptr;
1325 if (target.endsWith(".html"_L1)) {
1326 node = domain->findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1327 } else if (target.endsWith(')'_L1)) {
1328 QString function = target;
1329 QString signature;
1330 if (function.endsWith("()"_L1))
1331 function.chop(2);
1332 if (function.endsWith(')'_L1)) {
1333 qsizetype position = function.lastIndexOf('('_L1);
1334 signature = function.mid(position + 1, function.size() - position - 2);
1335 function = function.left(position);
1336 }
1337 QStringList path = function.split("::"_L1);
1338 node = domain->findFunctionNode(path, Parameters(signature), nullptr, genus);
1339 }
1340 if (node)
1341 return node;
1342
1343 if (genus == Genus::QML && target.contains('.'_L1) && !target.contains("::"_L1)) {
1344 int typeFlags = SearchBaseClasses | SearchEnumValues | TypesOnly;
1345 QStringList path = target.split('.'_L1);
1346 node = domain->findNode(path, relative, typeFlags, genus);
1347 if (node)
1348 return node;
1349 if (path.size() > 1) {
1350 node = domain->findNode(QStringList{path.last()}, relative, typeFlags, genus);
1351 if (node && node->isQmlType())
1352 return node;
1353 }
1354 }
1355
1356 int flags = SearchBaseClasses | SearchEnumValues;
1357 QStringList nodePath = target.split("::"_L1);
1358 if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
1359 relative = nullptr;
1360 QString localRef;
1361 const Node *result =
1362 domain->findNodeForTarget(nodePath, {}, relative, flags, genus, localRef);
1363 if (result && ref)
1364 *ref = localRef;
1365 return result;
1366 }
1367
1368 // Forest-wide search: function signatures, QML dot-paths, then general.
1369 if (target.endsWith(".html"_L1))
1370 return findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1371
1372 if (target.endsWith(')'_L1)) {
1373 const Node *node = findFunctionNode(target, relative, genus);
1374 if (node)
1375 return node;
1376 }
1377
1378 if (genus == Genus::QML && target.contains('.'_L1) && !target.contains("::"_L1)) {
1379 const Node *node = findTypeNode(target, relative, genus);
1380 if (node)
1381 return node;
1382 }
1383
1384 QStringList targetPath = Utilities::pathAndFragment(target);
1385 int flags = SearchBaseClasses | SearchEnumValues;
1386 QString localRef;
1387 const Node *node = findNodeForTarget(targetPath, relative, genus, localRef, flags);
1388 if (node) {
1389 if (ref)
1390 *ref = localRef;
1391 return node;
1392 }
1393
1394 return findPageNodeByTitle(target);
1395}
1396
1398{
1399 QStringList result;
1400 CNMap *m = primaryTree()->getCollectionMap(NodeType::Group);
1401
1402 if (!m)
1403 return result;
1404
1405 for (auto it = m->cbegin(); it != m->cend(); ++it)
1406 if (it.value()->members().contains(node))
1407 result << it.key();
1408
1409 return result;
1410}
1411
1412/*!
1413 Reads and parses the qdoc index files listed in \a indexFiles.
1414 */
1415void QDocDatabase::readIndexes(const QStringList &indexFiles)
1416{
1417 QStringList filesToRead;
1418 for (const QString &file : indexFiles) {
1419 QString fn = file.mid(file.lastIndexOf(QChar('/')) + 1);
1420 if (!isLoaded(fn))
1421 filesToRead << file;
1422 else
1423 qCCritical(lcQdoc) << "Index file" << file << "is already in memory.";
1424 }
1425 QDocIndexFiles::qdocIndexFiles()->readIndexes(filesToRead);
1426}
1427
1428/*!
1429 Generates a qdoc index file and writes it to \a fileName. The
1430 index file is generated with the parameters \a url, \a title,
1431 and \a hrefGenerator.
1432
1433 The \a hrefGenerator is used to compute document locations (hrefs)
1434 for nodes. For index files, this should be the HTML generator
1435 to ensure correct .html file extensions in the generated hrefs.
1436 If null, defaults to the HTML generator.
1437 */
1438void QDocDatabase::generateIndex(const QString &fileName, const QString &url, const QString &title,
1439 const Generator *hrefGenerator)
1440{
1441 // Resolve generator before modifying any state
1442 const Generator *generator = hrefGenerator;
1443 if (!generator)
1444 generator = Generator::generatorForFormat(u"HTML"_s);
1445 if (!generator) {
1446 qCWarning(lcQdoc) << "Cannot generate index file: no href generator available"
1447 " (HTML generator missing)";
1448 return;
1449 }
1450
1451 QString t = fileName.mid(fileName.lastIndexOf(QChar('/')) + 1);
1452 primaryTree()->setIndexFileName(t);
1453 QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, generator);
1454 QDocIndexFiles::destroyQDocIndexFiles();
1455}
1456
1457/*!
1458 Returns the collection node representing the module that \a relative
1459 node belongs to, or \c nullptr if there is no such module in the
1460 primary tree.
1461*/
1463{
1464 NodeType moduleType{NodeType::Module};
1465 QString moduleName;
1466 switch (relative->genus())
1467 {
1468 case Genus::CPP:
1469 moduleType = NodeType::Module;
1470 moduleName = relative->physicalModuleName();
1471 break;
1472 case Genus::QML:
1473 moduleType = NodeType::QmlModule;
1474 moduleName = relative->logicalModuleName();
1475 break;
1476 default:
1477 return nullptr;
1478 }
1479 if (moduleName.isEmpty())
1480 return nullptr;
1481
1482 return primaryTree()->getCollection(moduleName, moduleType);
1483}
1484
1485/*!
1486 Finds all the collection nodes of the specified \a type
1487 and merges them into the collection node map \a cnm. Nodes
1488 that match the \a relative node are not included.
1489 */
1490void QDocDatabase::mergeCollections(NodeType type, CNMap &cnm, const Node *relative)
1491{
1492 cnm.clear();
1493 CNMultiMap cnmm;
1494 for (auto *tree : searchOrder()) {
1495 CNMap *m = tree->getCollectionMap(type);
1496 if (m && !m->isEmpty()) {
1497 for (auto it = m->cbegin(); it != m->cend(); ++it) {
1498 if (!it.value()->isInternal())
1499 cnmm.insert(it.key(), it.value());
1500 }
1501 }
1502 }
1503 if (cnmm.isEmpty())
1504 return;
1505 static const QRegularExpression singleDigit("\\b([0-9])\\b");
1506 const QStringList keys = cnmm.uniqueKeys();
1507 for (const auto &key : keys) {
1508 const QList<CollectionNode *> values = cnmm.values(key);
1509 CollectionNode *n = nullptr;
1510 for (auto *value : values) {
1511 if (value && value->wasSeen() && value != relative) {
1512 n = value;
1513 break;
1514 }
1515 }
1516 if (n) {
1517 if (values.size() > 1) {
1518 for (CollectionNode *value : values) {
1519 if (value != n) {
1520 // Allow multiple (major) versions of QML modules
1521 if ((n->isQmlModule())
1522 && n->logicalModuleIdentifier() != value->logicalModuleIdentifier()) {
1523 if (value->wasSeen() && value != relative)
1524 cnm.insert(value->fullTitle().toLower(), value);
1525 continue;
1526 }
1527 for (Node *t : value->members())
1528 n->addMember(t);
1529 }
1530 }
1531 }
1532 QString sortKey = n->fullTitle().toLower();
1533 if (sortKey.startsWith("the "))
1534 sortKey.remove(0, 4);
1535 sortKey.replace(singleDigit, "0\\1");
1536 cnm.insert(sortKey, n);
1537 }
1538 }
1539}
1540
1541/*!
1542 Finds all the collection nodes with the same name
1543 and type as \a c and merges their members into the
1544 members list of \a c.
1545
1546 For QML modules, only nodes with matching
1547 module identifiers are merged to avoid merging
1548 modules with different (major) versions.
1549 */
1551{
1552 if (c == nullptr)
1553 return;
1554
1555 // REMARK: This form of merging is usually called during the
1556 // generation phase om-the-fly when a source-of-truth collection
1557 // is required.
1558 // In practice, this means a collection could be merged many, many
1559 // times during the lifetime of a generation.
1560 // To avoid repeating the merging process each time, which could
1561 // be time consuming, we use a small flag that is set directly on
1562 // the collection to bail-out early.
1563 //
1564 // The merging process is only meaningful for collections when the
1565 // collection references are spread troughout multiple projects.
1566 // The part of information that exists in other project is read
1567 // before the generation phase, such that when the generation
1568 // phase comes, we already have all the information we need for
1569 // merging such that we can consider all version of a certain
1570 // collection node immutable, making the caching inherently
1571 // correct at any point of the generation.
1572 //
1573 // This implies that this operation is unsafe if it is performed
1574 // before all the index files are loaded.
1575 // Indeed, this is a prerequisite, with the current structure, to
1576 // perform this optmization.
1577 //
1578 // At the current time, this is true and is expected not to
1579 // change.
1580 //
1581 // Do note that this is not applied to the other overload of
1582 // mergeCollections as we cannot as safely ensure its consistency
1583 // and, as the result of the merging depends on multiple
1584 // parameters, it would require an actual memoization of the call.
1585 //
1586 // Note that this is a defensive optimization and we are assuming
1587 // that it is effective based on heuristical data. As this is
1588 // expected to disappear, at least in its current form, in the
1589 // future, a more thorough analysis was not performed.
1590 if (c->isMerged()) {
1591 return;
1592 }
1593
1594 for (auto *tree : searchOrder()) {
1595 CollectionNode *cn = tree->getCollection(c->name(), c->nodeType());
1596 if (cn && cn != c) {
1597 if ((cn->isQmlModule())
1598 && cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
1599 continue;
1600
1601 for (auto *node : cn->members())
1602 c->addMember(node);
1603
1604 // REMARK: The merging process is performed to ensure that
1605 // references to the collection in external projects are
1606 // taken into account before consuming the collection.
1607 //
1608 // This works by having QDoc construct empty collections
1609 // as soon as a reference to a collection is encountered
1610 // and filling details later on when its definition is
1611 // found.
1612 //
1613 // This initially-empty collection is always saved to the
1614 // primaryTree and it is the collection that is directly
1615 // accessible to consumers during the generation process.
1616 //
1617 // Nonetheless, when the definition for the collection is
1618 // not in the same project as the one that is being
1619 // compiled, its details will never be filled in.
1620 //
1621 // Indeed, the details will live in the index file for the
1622 // project where the collection is defined, if any, and
1623 // the node for it, which has complete information, will
1624 // live in some non-primaryTree.
1625 //
1626 // The merging process itself is used by consumers during
1627 // the generation process because they access the
1628 // primaryTree version of the collection expecting a
1629 // source-of-truth.
1630 // To ensure that this is the case for usages that
1631 // requires linking, we need to merge not only the members
1632 // of the collection that reside in external versions of
1633 // the collection; but some of the data that reside in the
1634 // definition of the collection intself, namely the title
1635 // and the url.
1636 //
1637 // A collection that contains the data of a definition is
1638 // always marked as seen, hence we use that to discern
1639 // whether we are working with a placeholder node or not,
1640 // and fill in the data if we encounter a node that
1641 // represents a definition.
1642 //
1643 // The way in which QDoc works implies that collection are
1644 // globally scoped between projects.
1645 // The repetition of the definition for the same
1646 // collection is warned for as a duplicate documentation,
1647 // such that we can expect a single valid source of truth
1648 // for a given collection in each project.
1649 // It is currently unknown if this warning is applicable
1650 // when the repeated collection is defined in two
1651 // different projects.
1652 //
1653 // As QDoc implicitly would not correctly support this
1654 // case, we assume that only one declaration exists for
1655 // each collection, such that the first encoutered one
1656 // must be the source of truth and that there is no need
1657 // to copy any data after the first copy is performed.
1658 // KLUDGE: Note that this process is done as a hackish
1659 // solution to QTBUG-104237 and should not be considered
1660 // final or dependable.
1661 if (!c->wasSeen() && cn->wasSeen()) {
1662 c->markSeen();
1663 c->setTitle(cn->title());
1664 c->setUrl(cn->url());
1665 c->setResolvedPhysicalModuleName(cn->tree()->physicalModuleName());
1666 }
1667 }
1668 }
1669
1671}
1672
1673/*!
1674 Returns the node to a piece of QML API documentation that corresponds to
1675 the given \a path, or \c nullptr if the path cannot be resolved.
1676 The \a relative node specifies where the link or reference was used.
1677
1678 This doesn't handle the use of fragments to refer to elements in the
1679 target documentation.
1680*/
1681const Node *QDocDatabase::findQmlNode(const QString &path, const Node *relative)
1682{
1683 // Since path contains a dot, assume that it is either a QML module
1684 // in its own right or a QML path. Break the path where path separators
1685 // for C++ or QML occur.
1686 QStringList pieces;
1687 for (auto piece : path.split("."_L1)) {
1688 for (auto inner : piece.split("::"_L1)) {
1689 pieces.append(inner);
1690 }
1691 }
1692
1693 // Try to resolve a leading QML module name.
1694 QString modName{""};
1695 QString tryName{""};
1696 const Node *modNode{nullptr};
1697 int i;
1698
1699 for (i = 0; i < pieces.count(); i++) {
1700 tryName += pieces.at(i);
1701 const Node *node = findNodeByNameAndType(QStringList(tryName), &Node::isQmlModule);
1702
1703 if (node) {
1704 modName = tryName;
1705 modNode = node;
1706 } else
1707 break;
1708
1709 tryName += "."_L1;
1710 }
1711
1712 // No more pieces to check, so return what we have.
1713 if (i == pieces.count())
1714 return modNode;
1715
1716 QmlTypeNode *qtn{nullptr};
1717
1718 if (modNode) {
1719 pieces = pieces.mid(i);
1720 qtn = findQmlType(modName, pieces.takeFirst(), relative);
1721 } else
1722 qtn = findQmlType(pieces.takeFirst(), relative);
1723
1724 // Return a null pointer or the leaf node.
1725 if (!qtn || pieces.isEmpty())
1726 return qtn;
1727
1728 auto propertyName = pieces.join("."_L1);
1729 QmlPropertyNode *qmlProperty = qtn->hasQmlProperty(propertyName);
1730
1731 if (qmlProperty)
1732 return qmlProperty;
1733
1734 // Fall back on a general search for a member with the name.
1735 // This will intentionally fail for functions if there is a dot
1736 // in the name.
1737 return qtn->findChildNode(propertyName, Genus::QML);
1738}
1739
1740/*!
1741 Searches for the node that matches the path in \a atom and the
1742 specified \a genus. The \a relative node is used if the first
1743 leg of the path is empty, i.e. if the path begins with '#'.
1744 The function also sets \a ref if there remains an unused leg
1745 in the path after the node is found. The node is returned as
1746 well as the \a ref. If the returned node pointer is null,
1747 \a ref is also not valid.
1748 */
1749const Node *QDocDatabase::findNodeForAtom(const Atom *a, const Node *relative, QString &ref,
1750 Genus genus)
1751{
1752 const Node *node = nullptr;
1753
1754 Atom *atom = const_cast<Atom *>(a);
1755 QStringList targetPath = Utilities::pathAndFragment(atom->string());
1756 QString first = targetPath.first().trimmed();
1757
1758 Tree *domain = nullptr;
1759
1760 if (atom->isLinkAtom()) {
1761 if (auto *atomDomain = atom->domain())
1762 domain = atomDomain;
1763 if (atom->genus() != Genus::DontCare)
1764 genus = atom->genus();
1765
1766 if (!first.contains("://"_L1) && !first.contains(" "_L1) && !first.endsWith(".html"_L1) && first.contains("."_L1)) {
1767 const Node *found = findQmlNode(first, relative);
1768 if (found) return found;
1769 }
1770 }
1771
1772 if (first.isEmpty())
1773 node = relative; // search for a target on the current page.
1774 else if (domain) {
1775 if (first.endsWith(".html"))
1776 node = domain->findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1777 else if (first.endsWith(QChar(')'))) {
1778 QString signature;
1779 QString function = first;
1780 qsizetype length = first.size();
1781 if (function.endsWith("()"))
1782 function.chop(2);
1783 if (function.endsWith(QChar(')'))) {
1784 qsizetype position = function.lastIndexOf(QChar('('));
1785 signature = function.mid(position + 1, length - position - 2);
1786 function = function.left(position);
1787 }
1788 QStringList path = function.split("::");
1789 node = domain->findFunctionNode(path, Parameters(signature), nullptr, genus);
1790 }
1791 if (node == nullptr) {
1792 int flags = SearchBaseClasses | SearchEnumValues;
1793 QStringList nodePath = first.split("::");
1794 QString target;
1795 targetPath.removeFirst();
1796 if (!targetPath.isEmpty())
1797 target = targetPath.takeFirst();
1798 if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
1799 relative = nullptr;
1800 return domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
1801 }
1802 } else {
1803 if (first.endsWith(".html"))
1804 node = findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1805 else if (first.endsWith(QChar(')')))
1806 node = findFunctionNode(first, relative, genus);
1807 if (node == nullptr) {
1808 // For QML contexts with qualified names containing ".", use the same logic as findTypeNode
1809 if (genus == Genus::QML && first.contains('.') && !first.contains("::")) {
1810 // Try import-aware lookup using findTypeNode logic
1811 node = findTypeNode(first, relative, genus);
1812 if (node) {
1813 // Handle any fragment reference
1814 targetPath.removeFirst();
1815 if (!targetPath.isEmpty()) {
1816 ref = node->root()->tree()->getRef(targetPath.first(), node);
1817 if (ref.isEmpty())
1818 node = nullptr;
1819 }
1820 return node;
1821 }
1822 }
1823 return findNodeForTarget(targetPath, relative, genus, ref, atom->flags());
1824 }
1825 }
1826
1827 if (node != nullptr && ref.isEmpty()) {
1828 if (!node->url().isEmpty())
1829 return node;
1830 targetPath.removeFirst();
1831 if (!targetPath.isEmpty()) {
1832 ref = node->root()->tree()->getRef(targetPath.first(), node);
1833 if (ref.isEmpty())
1834 node = nullptr;
1835 }
1836 }
1837 return node;
1838}
1839
1840/*!
1841 Updates navigation (previous/next page links and the navigation parent)
1842 for pages listed in the TOC, specified by the \c navigation.toctitles
1843 configuration variable.
1844
1845 if \c navigation.toctitles.inclusive is \c true, include also the TOC
1846 page(s) themselves as a 'root' item in the navigation bar (breadcrumbs)
1847 that are generated for HTML output.
1848*/
1850{
1851 // Restrict searching only to the local (primary) tree
1852 QList<Tree *> searchOrder = this->searchOrder();
1854
1855 const QString configVar = CONFIG_NAVIGATION +
1856 Config::dot +
1858
1859 // TODO: [direct-configuration-access]
1860 // The configuration is currently a singleton with some generally
1861 // global mutable state.
1862 //
1863 // Accessing the data in this form complicates testing and
1864 // requires tests that inhibit any test parallelization, as the
1865 // tests are not self contained.
1866 //
1867 // This should be generally avoived. Possibly, we should strive
1868 // for Config to be a POD type that generally is scoped to
1869 // main and whose data is destructured into dependencies when
1870 // the dependencies are constructed.
1871 bool inclusive{Config::instance().get(
1872 configVar + Config::dot + CONFIG_INCLUSIVE).asBool()};
1873
1874 // TODO: [direct-configuration-access]
1875 const auto tocTitles{Config::instance().get(configVar).asStringList()};
1876
1877 for (const auto &tocTitle : tocTitles) {
1878 if (const auto candidateTarget = findNodeForTarget(tocTitle, nullptr); candidateTarget && candidateTarget->isPageNode()) {
1879 auto tocPage{static_cast<const PageNode*>(candidateTarget)};
1880
1881 Text body = tocPage->doc().body();
1882
1883 auto *atom = body.firstAtom();
1884
1885 std::pair<PageNode *, QString> prev { nullptr, QString() };
1886
1887 std::stack<const PageNode *> tocStack;
1888 tocStack.push(inclusive ? tocPage : nullptr);
1889
1890 bool inItem = false;
1891
1892 // TODO: Understand how much we use this form of looping over atoms.
1893 // If it is used a few times we might consider providing
1894 // an iterator for Text to make use of a simpler
1895 // range-for loop.
1896 while (atom) {
1897 switch (atom->type()) {
1898 case Atom::ListItemLeft:
1899 // Not known if we're going to have a link, push a temporary
1900 tocStack.push(nullptr);
1901 inItem = true;
1902 break;
1903 case Atom::ListItemRight:
1904 tocStack.pop();
1905 inItem = false;
1906 break;
1907 case Atom::Link: {
1908 if (!inItem)
1909 break;
1910
1911 // TODO: [unnecessary-output-parameter]
1912 // We currently need an lvalue string to
1913 // pass to findNodeForAtom, as the
1914 // outparameter ref.
1915 //
1916 // Apart from the general problems with output
1917 // parameters, we shouldn't be forced to
1918 // instanciate an unnecessary object at call
1919 // site.
1920 //
1921 // Understand what the correct way to avoid this is.
1922 // This requires changes to findNodeForAtom
1923 // and should be addressed in the context of
1924 // revising that method.
1925 QString unused{};
1926 // TODO: Having to const cast is really a code
1927 // smell and could result in undefined
1928 // behavior in some specific cases (e.g point
1929 // to something that is actually const).
1930 //
1931 // We should understand how to sequence the
1932 // code so that we have access to mutable data
1933 // when we need it and "freeze" the data
1934 // afterwards.
1935 //
1936 // If it we expect this form of mutability at
1937 // this point we should expose a non-const API
1938 // for the database, possibly limited to a
1939 // very specific scope of execution.
1940 //
1941 // Understand what the correct sequencing for
1942 // this processing is and revise this part.
1943 auto candidatePage = const_cast<Node *>(findNodeForAtom(atom, nullptr, unused));
1944 if (!candidatePage || !candidatePage->isPageNode()) break;
1945
1946 auto page{static_cast<PageNode*>(candidatePage)};
1947
1948 // ignore self-references
1949 if (page == prev.first) break;
1950
1951 if (prev.first) {
1952 prev.first->setLink(
1953 Node::NextLink,
1954 page->title(),
1955 // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
1956 // As with other structures in QDoc we
1957 // are able to call methods that are
1958 // valid only on very specific states.
1959 //
1960 // For some of those calls we have
1961 // some defensive programming measures
1962 // that allow us to at least identify
1963 // the error during debugging, while
1964 // for others this may currently hide
1965 // some logic error.
1966 //
1967 // To avoid those cases, we should
1968 // strive to move those cases to a
1969 // compilation error, requiring a
1970 // statically analyzable state that
1971 // represents the current model.
1972 //
1973 // This would ensure that those
1974 // lingering class of bugs are
1975 // eliminated completely, forces a
1976 // more explicit codebase where the
1977 // current capabilities do not depend
1978 // on runtime values might not be
1979 // generally visible, and does not
1980 // require us to incur into the
1981 // required state, which may be rare,
1982 // simplifying our abilities to
1983 // evaluate all possible states.
1984 //
1985 // For linking atoms, LinkAtom is
1986 // available and might be a good
1987 // enough solution to move linkText
1988 // to.
1989 atom->linkText()
1990 );
1991 page->setLink(
1992 Node::PreviousLink,
1993 prev.first->title(),
1994 prev.second
1995 );
1996 }
1997
1998 if (page == tocPage)
1999 break;
2000
2001 // Find the navigation parent from the stack; we may have null pointers
2002 // for non-link list items, so skip those.
2003 qsizetype popped = 0;
2004 while (tocStack.size() > 1 && !tocStack.top()) {
2005 tocStack.pop();
2006 ++popped;
2007 }
2008
2009 page->setNavigationParent(tocStack.empty() ? nullptr : tocStack.top());
2010
2011 while (--popped > 0)
2012 tocStack.push(nullptr);
2013
2014 tocStack.push(page);
2015 // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
2016 prev = { page, atom->linkText() };
2017 }
2018 break;
2019
2020 case Atom::AnnotatedList:
2021 case Atom::GeneratedList: {
2022 if (const auto *cn = getCollectionNode(atom->string(), NodeType::Group)) {
2023 const auto sortOrder{Generator::sortOrder(atom->strings().last())};
2024 NodeList members{cn->members()};
2025 // Drop non-page nodes and index nodes so that we do not generate navigational
2026 // links pointing outside of this documentation set.
2027 members.erase(std::remove_if(members.begin(), members.end(),
2028 [](const Node *n) {
2029 return n->isIndexNode() || !n->isPageNode() || n->isExternalPage();
2030 }), members.end());
2031 if (members.isEmpty())
2032 break;
2033
2034 if (sortOrder == Qt::DescendingOrder)
2035 std::sort(members.rbegin(), members.rend(), Node::nodeSortKeyOrNameLessThan);
2036 else
2037 std::sort(members.begin(), members.end(), Node::nodeSortKeyOrNameLessThan);
2038
2039 // `members` now has local PageNode pointers, adjust prev/next links for each.
2040 // Do not set a navigation parent node as group members use the group node as
2041 // their nav. parent.
2042 for (auto *m : members) {
2043 auto *page = static_cast<PageNode *>(m);
2044 if (prev.first) {
2045 prev.first->setLink(Node::NextLink, page->title(), page->fullName());
2046 page->setLink(Node::PreviousLink, prev.first->title(), prev.second);
2047 }
2048 prev = { page, page->fullName() };
2049 }
2050 }
2051 }
2052 break;
2053
2054 default:
2055 break;
2056 }
2057
2058 atom = atom->next();
2059 }
2060 } else {
2061 Config::instance().get(configVar).location()
2062 .warning(QStringLiteral("Failed to find table of contents with title '%1'")
2063 .arg(tocTitle));
2064 }
2065 }
2066
2067 // Restore search order
2068 setSearchOrder(searchOrder);
2069}
2070
2071QT_END_NAMESPACE
void resolveRelates()
Adopts each non-aggregate C++ node (function/macro, typedef, enum, variable, or a shared comment node...
void resolveQmlInheritance()
Resolves the inheritance information for all QML type children of this aggregate.
void normalizeOverloads()
Sorts the lists of overloads in the function map and assigns overload numbers.
void findAllNamespaces(NodeMultiMap &namespaces)
For each child of this node, if the child is a namespace node, insert the child into the namespaces m...
void markUndocumentedChildrenInternal()
Mark all child nodes that have no documentation as having internal status.
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
virtual bool isLinkAtom() const
Definition atom.h:163
virtual Tree * domain()
Definition atom.h:165
virtual Genus genus()
Definition atom.h:164
A class for holding the members of a collection of doc pages.
This node is used to represent any kind of function being documented.
This class represents a C++ namespace.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
const Node * findTypeNode(const QString &type, const Node *relative, Genus genus)
This function is called for autolinking to a type, which could be a function return type or a paramet...
NodeMapMap & getFunctionIndex()
Returns the function index.
const NodeMultiMap & getClassMap(const QString &key)
Find the key in the map of new class maps, and return a reference to the value, which is a NodeMap.
const NodeMultiMap & getQmlTypeMap(const QString &key)
Find the key in the map of new QML type maps, and return a reference to the value,...
void resolveNamespaces()
Multiple namespace nodes for namespace X can exist in the qdoc database in different trees.
TextToNodeMap & getLegaleseTexts()
Returns a reference to the collection of legalese texts.
NodeMultiMap & getAttributions()
Returns a reference to the multimap of attribution nodes.
static void destroyQdocDB()
Destroys the singleton.
NodeMultiMap & getQmlTypesWithObsoleteMembers()
Returns a reference to the map of QML types with obsolete members.
NodeMultiMap & getObsoleteQmlTypes()
Returns a reference to the map of obsolete QML types.
QmlTypeNode * findQmlType(const QString &qmid, const QString &name, const Node *relative=nullptr)
Returns the QML type node identified by the QML module id qmid and QML type name, or nullptr if no ty...
QmlTypeNode * findQmlType(const ImportRec &import, const QString &name, const Node *relative=nullptr)
const FunctionNode * findFunctionNode(const QString &target, const Node *relative, Genus genus)
Finds the function node for the qualified function path in target and returns a pointer to it.
static QDocDatabase * qdocDB()
Creates the singleton.
void resolveBaseClasses()
NodeMultiMap & getQmlTypes()
Returns a reference to the multimap of QML types.
NodeMultiMap & getClassesWithObsoleteMembers()
Returns a reference to the map of C++ classes with obsolete members.
NodeMultiMap & getQmlValueTypes()
Returns a reference to the map of QML basic types.
NodeMultiMap & getCppClasses()
Returns a reference to the map of all C++ classes.
QStringList groupNamesForNode(Node *node)
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
void readIndexes(const QStringList &indexFiles)
Reads and parses the qdoc index files listed in indexFiles.
void resolveConceptUsers()
Builds the concept-to-users reverse index after parsing finishes and before any generator runs.
const Node * findNodeForTarget(const QString &target, const Node *relative)
Finds the node that will generate the documentation that contains the target and returns a pointer to...
const Node * findNodeForAtom(const Atom *atom, const Node *relative, QString &ref, Genus genus=Genus::DontCare)
Searches for the node that matches the path in atom and the specified genus.
void mergeCollections(NodeType type, CNMap &cnm, const Node *relative)
Finds all the collection nodes of the specified type and merges them into the collection node map cnm...
NodeMultiMap & getNamespaces()
Returns a reference to the namespace map.
QmlTypeNode * findQmlTypeInPrimaryTree(const QString &qmid, const QString &name)
Returns the QML node identified by the QML module id qmid and name, searching in the primary tree onl...
const NodeMultiMap & getSinceMap(const QString &key)
Find the key in the map of new {since} maps, and return a reference to the value, which is a NodeMult...
NodeMultiMap & getExamples()
Returns a reference to the multimap of example nodes.
void processForest()
This function calls a set of functions for each tree in the forest that has not already been analyzed...
const Node * findNodeForTarget(const QString &target, const Node *relative, Genus genus, const QString &moduleName, QString *ref=nullptr)
Finds the node for target with genus and module scoping.
void(QDocDatabase::*)(Aggregate *) FindFunctionPtr
void updateNavigation()
Updates navigation (previous/next page links and the navigation parent) for pages listed in the TOC,...
void resolveStuff()
Performs several housekeeping tasks prior to generating the documentation.
Tree * primaryTree()
NodeMultiMap & getObsoleteClasses()
Returns a reference to the map of obsolete C++ clases.
const CollectionNode * getModuleNode(const Node *relative)
Returns the collection node representing the module that relative node belongs to,...
void resolveProxies()
Each instance of class Tree that represents an index file must be traversed to find all instances of ...
const Node * findQmlNode(const QString &path, const Node *relative)
Returns the node to a piece of QML API documentation that corresponds to the given path,...
void mergeCollections(CollectionNode *c)
Finds all the collection nodes with the same name and type as c and merges their members into the mem...
void setLocalSearch()
void generateIndex(const QString &fileName, const QString &url, const QString &title, const Generator *hrefGenerator)
Generates a qdoc index file and writes it to fileName.
A class representing a forest of Tree objects.
This class handles qdoc index files.
const ImportList & importList() const
Definition qmltypenode.h:51
This class constructs and maintains a tree of instances of the subclasses of Node.
Definition tree.h:58
void markDontDocumentNodes()
The {don't document} map has been loaded with the names of classes and structs in the current module ...
Definition tree.cpp:1535
NodeList & proxies()
Definition tree.h:78
void resolveProperties()
Resolves access functions associated with each PropertyNode stored in m_unresolvedPropertyMap,...
Definition tree.cpp:257
#define CONFIG_TOCTITLES
Definition config.h:458
#define CONFIG_NOLINKERRORS
Definition config.h:430
#define CONFIG_NAVIGATION
Definition config.h:429
#define CONFIG_INCLUSIVE
Definition config.h:416
NodeType
Definition genustypes.h:154
QMultiMap< QString, CollectionNode * > CNMultiMap
Definition node.h:53
QList< Node * > NodeList
Definition node.h:45
QMap< QString, NodeMultiMap > NodeMultiMapMap
Definition node.h:51
QMap< QString, Node * > NodeMap
Definition node.h:48
QMap< QString, NodeMap > NodeMapMap
Definition node.h:49
QMap< QString, CollectionNode * > CNMap
Definition node.h:52
static void collectConceptReferences(const Node *node, QStringList &refs)
Gathers the fully-qualified concept names referenced by node.
static void registerConceptUsersUnder(Aggregate *parent, QDocDatabase &db)
Walks every documented node under parent and registers each constrained item as a member of the conce...
static NodeMultiMap emptyNodeMultiMap_
QT_BEGIN_NAMESPACE typedef QMultiMap< Text, const Node * > TextToNodeMap
@ SearchBaseClasses
@ SearchEnumValues
@ TypesOnly
QList< ImportRec > ImportList
Definition qmltypenode.h:20
QMultiMap< QString, Node * > NodeMultiMap
Definition generator.h:36
bool isEmpty() const
Definition importrec.h:30
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:123
Genus genus() const override
Returns this node's Genus.
Definition node.h:85
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:150
const std::optional< RelaxedTemplateDeclaration > & templateDecl() const
Definition node.h:245
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
Aggregate * root() const
virtual Tree * tree() const
Returns a pointer to the Tree this node is in.
Definition node.cpp:899
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
TargetType
A type of a linkable target record.
Definition tree.h:29
@ Unknown
Definition tree.h:29