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