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
918 The order of operations is important in some cases. For example,
919 the call to removePrivateAndInternalBases must follow the call to
920 markUndocumentedChildrenInternal to ensure correct behavior.
921 */
923{
924 const auto &config = Config::instance();
925 if (config.dualExec() || config.preparing()) {
926 // order matters
927 primaryTree()->resolveBaseClasses(primaryTreeRoot());
928 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
933 primaryTree()->validatePropertyDocumentation(primaryTreeRoot());
935 primaryTree()->removePrivateAndInternalBases(primaryTreeRoot());
937 primaryTree()->resolveTargets(primaryTreeRoot());
938 primaryTree()->resolveCppToQmlLinks();
939 primaryTree()->resolveSince(*primaryTreeRoot());
941 }
942 if (config.singleExec() && config.generating()) {
943 primaryTree()->resolveBaseClasses(primaryTreeRoot());
944 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
946 primaryTree()->resolveCppToQmlLinks();
947 primaryTree()->resolveSince(*primaryTreeRoot());
949 }
950 if (!config.preparing()) {
955 }
956 if (config.dualExec())
957 QDocIndexFiles::destroyQDocIndexFiles();
958}
959
961{
962 Tree *t = m_forest.firstTree();
963 while (t) {
964 t->resolveBaseClasses(t->root());
965 if (t != primaryTree())
966 t->root()->resolveQmlInheritance();
967 t = m_forest.nextTree();
968 }
969}
970
971/*!
972 Gathers the fully-qualified concept names referenced by \a node.
973 Template-head and direct-concept references live on the optional
974 RelaxedTemplateDeclaration carried by the node; trailing-requires
975 and constrained-auto references are accumulated on the FunctionNode.
976 */
977static void collectConceptReferences(const Node *node, QStringList &refs)
978{
979 if (const auto &td = node->templateDecl(); td.has_value()) {
980 for (const auto &name : td->referenced_concepts)
981 refs.append(QString::fromStdString(name));
982 }
983 if (node->isFunction()) {
984 const auto *fn = static_cast<const FunctionNode *>(node);
985 refs += fn->referencedConcepts();
986 }
987}
988
989/*!
990 Walks every documented node under \a parent and registers each constrained
991 item as a member of the concept's CollectionNode for each concept it
992 references.
993
994 Recurses into aggregates so the entire primary tree is covered.
995 */
997{
998 for (auto *child : std::as_const(parent->childNodes())) {
999 // A skipped node is not registered as a concept user, but its
1000 // documented descendants still are: a documented class nested in an
1001 // internal namespace, for example, should still appear in its concept's
1002 // "Used by" list. Visibility filtering and recursion are therefore
1003 // independent.
1004 const bool registerChild =
1005 !child->isPrivate() && !child->isInternal() && !child->isDontDocument();
1006
1007 if (registerChild) {
1008 QStringList refs;
1009 collectConceptReferences(child, refs);
1010 refs.sort();
1011 refs.removeDuplicates();
1012
1013 for (const QString &conceptName : std::as_const(refs)) {
1014 // Cross-tree lookup. The forest search order covers the primary
1015 // tree plus every dependency-module index tree. A concept
1016 // declared in module B and referenced from module A can be
1017 // found through the same cross-tree lookup machinery.
1018 // Concepts without a \\concept block anywhere in the corpus
1019 // return \c{nullptr} and are silently skipped; references to
1020 // undocumented concepts (such as std::integral) are common and
1021 // not actionable.
1022 //
1023 // addMember() deduplicates membership centrally, so re-running
1024 // this pass is safe and produces a stable "Used by" listing
1025 // without per-call guard logic.
1026 if (auto *cn = db.findMutableCollectionNode(conceptName, NodeType::Concept))
1027 cn->addMember(child);
1028 }
1029 }
1030
1031 if (child->isAggregate())
1032 registerConceptUsersUnder(static_cast<Aggregate *>(child), db);
1033 }
1034}
1035
1036/*!
1037 Builds the concept-to-users reverse index after parsing finishes
1038 and before any generator runs. The forward direction — which
1039 concepts a constrained declaration references — is captured
1040 during the libclang AST pass; this pass turns those isolated
1041 references into bidirectional collection membership so concept
1042 reference pages can render a \e {Used by} section through the
1043 same scaffolding group and module collection pages already use.
1044 */
1046{
1047 registerConceptUsersUnder(primaryTreeRoot(), *this);
1048}
1049
1050/*!
1051 Returns a reference to the namespace map. Constructs the
1052 namespace map if it hasn't been constructed yet.
1053
1054 \note This function must not be called in the prepare phase.
1055 */
1057{
1059 return m_namespaceIndex;
1060}
1061
1062/*!
1063 Multiple namespace nodes for namespace X can exist in the
1064 qdoc database in different trees. This function first finds
1065 all namespace nodes in all the trees and inserts them into
1066 a multimap. Then it combines all the namespace nodes that
1067 have the same name into a single namespace node of that
1068 name and inserts that combined namespace node into an index.
1069 */
1071{
1072 if (!m_namespaceIndex.isEmpty())
1073 return;
1074
1075 bool linkErrors = !Config::instance().get(CONFIG_NOLINKERRORS).asBool();
1076 NodeMultiMap namespaceMultimap;
1077 Tree *t = m_forest.firstTree();
1078 while (t) {
1079 t->root()->findAllNamespaces(namespaceMultimap);
1080 t = m_forest.nextTree();
1081 }
1082 const QList<QString> keys = namespaceMultimap.uniqueKeys();
1083 for (const QString &key : keys) {
1084 NamespaceNode *ns = nullptr;
1085 NamespaceNode *indexNamespace = nullptr;
1086 const NodeList namespaces = namespaceMultimap.values(key);
1087 qsizetype count = namespaceMultimap.remove(key);
1088 if (count > 0) {
1089 for (auto *node : namespaces) {
1090 ns = static_cast<NamespaceNode *>(node);
1091 if (ns->isDocumentedHere())
1092 break;
1093 else if (ns->hadDoc())
1094 indexNamespace = ns; // namespace was documented but in another tree
1095 ns = nullptr;
1096 }
1097 if (ns) {
1098 for (auto *node : namespaces) {
1099 auto *nsNode = static_cast<NamespaceNode *>(node);
1100 if (nsNode->hadDoc() && nsNode != ns) {
1101 ns->doc().location().warning(
1102 QStringLiteral("Namespace %1 documented more than once")
1103 .arg(nsNode->name()), QStringLiteral("also seen here: %1")
1104 .arg(nsNode->doc().location().toString()));
1105 }
1106 }
1107 } else if (!indexNamespace) {
1108 // Warn about documented children in undocumented namespaces.
1109 // As the namespace can be documented outside this project,
1110 // skip the warning if --no-link-errors is set
1111 if (linkErrors) {
1112 for (auto *node : namespaces) {
1113 if (!node->isIndexNode())
1114 static_cast<NamespaceNode *>(node)->reportDocumentedChildrenInUndocumentedNamespace();
1115 }
1116 }
1117 } else {
1118 for (auto *node : namespaces) {
1119 auto *nsNode = static_cast<NamespaceNode *>(node);
1120 if (nsNode != indexNamespace)
1121 nsNode->setDocNode(indexNamespace);
1122 }
1123 }
1124 }
1125 /*
1126 If there are multiple namespace nodes with the same
1127 name where one of them will be the main reference page
1128 for the namespace, include all nodes in the public
1129 API of the namespace.
1130 */
1131 if (ns && count > 1) {
1132 for (auto *node : namespaces) {
1133 auto *nameSpaceNode = static_cast<NamespaceNode *>(node);
1134 if (nameSpaceNode != ns) {
1135 for (auto it = nameSpaceNode->constBegin(); it != nameSpaceNode->constEnd();
1136 ++it) {
1137 Node *anotherNs = *it;
1138 if (anotherNs && anotherNs->isPublic() && !anotherNs->isInternal())
1139 ns->includeChild(anotherNs);
1140 }
1141 }
1142 }
1143 }
1144 /*
1145 Add the main namespace reference node to index, or the last seen
1146 namespace if the main one was not found.
1147 */
1148 if (!ns)
1149 ns = indexNamespace ? indexNamespace : static_cast<NamespaceNode *>(namespaces.last());
1150 m_namespaceIndex.insert(ns->name(), ns);
1151 }
1152}
1153
1154/*!
1155 Each instance of class Tree that represents an index file
1156 must be traversed to find all instances of class ProxyNode.
1157 For each ProxyNode found, look up the ProxyNode's name in
1158 the primary Tree. If it is found, it means that the proxy
1159 node contains elements (normally just functions) that are
1160 documented in the module represented by the Tree containing
1161 the proxy node but that are related to the node we found in
1162 the primary tree.
1163 */
1165{
1166 // The first tree is the primary tree.
1167 // Skip the primary tree.
1168 Tree *t = m_forest.firstTree();
1169 t = m_forest.nextTree();
1170 while (t) {
1171 const NodeList &proxies = t->proxies();
1172 if (!proxies.isEmpty()) {
1173 for (auto *node : proxies) {
1174 const auto *pn = static_cast<ProxyNode *>(node);
1175 if (pn->count() > 0) {
1176 Aggregate *aggregate = primaryTree()->findAggregate(pn->name());
1177 if (aggregate != nullptr)
1178 aggregate->appendToRelatedByProxy(pn->childNodes());
1179 }
1180 }
1181 }
1182 t = m_forest.nextTree();
1183 }
1184}
1185
1186/*!
1187 Finds the function node for the qualified function path in
1188 \a target and returns a pointer to it. The \a target is a
1189 function signature with or without parameters but without
1190 the return type.
1191
1192 \a relative is the node in the primary tree where the search
1193 begins. It is not used in the other trees, if the node is not
1194 found in the primary tree. \a genus can be used to force the
1195 search to find a C++ function or a QML function.
1196
1197 The entire forest is searched, but the first match is accepted.
1198 */
1199const FunctionNode *QDocDatabase::findFunctionNode(const QString &target, const Node *relative,
1200 Genus genus)
1201{
1202 QString signature;
1203 QString function = target;
1204 qsizetype length = target.size();
1205 if (function.endsWith("()"))
1206 function.chop(2);
1207 if (function.endsWith(QChar(')'))) {
1208 qsizetype position = function.lastIndexOf(QChar('('));
1209 signature = function.mid(position + 1, length - position - 2);
1210 function = function.left(position);
1211 }
1212 QStringList path = function.split("::");
1213 return m_forest.findFunctionNode(path, Parameters(signature), relative, genus);
1214}
1215
1216/*!
1217 This function is called for autolinking to a \a type,
1218 which could be a function return type or a parameter
1219 type. The tree node that represents the \a type is
1220 returned. All the trees are searched until a match is
1221 found. When searching the primary tree, the search
1222 begins at \a relative and proceeds up the parent chain.
1223 When searching the index trees, the search begins at the
1224 root.
1225 */
1226const Node *QDocDatabase::findTypeNode(const QString &type, const Node *relative, Genus genus)
1227{
1228 // For QML contexts with qualified names containing ".", try import-aware lookup first
1229 if ((genus == Genus::QML || (relative && relative->genus() == Genus::QML)) &&
1230 type.contains('.') && !type.contains("::")) {
1231 if (relative && relative->isQmlType()) {
1232 const QmlTypeNode *qmlType = static_cast<const QmlTypeNode*>(relative);
1233 const ImportList &imports = qmlType->importList();
1234
1235 for (const auto &import : imports) {
1236 if (QmlTypeNode *found = findQmlType(import, type)) {
1237 return found;
1238 }
1239 }
1240 }
1241
1242 // Fall back to regular path-based lookup for QML qualified names
1243 QStringList path = type.split(".");
1244 if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
1245 auto it = s_typeNodeMap.find(path.at(0));
1246 if (it != s_typeNodeMap.end())
1247 return it.value();
1248 }
1249
1250 // Try the full qualified path first
1251 const Node *node = m_forest.findTypeNode(path, relative, genus);
1252 if (node)
1253 return node;
1254
1255 // If the full path fails and we have multiple segments, try just the last segment
1256 // This handles cases like "TM.BaseType" where "TM" is an alias we can't resolve
1257 // but "BaseType" might be findable as a QML type
1258 if (path.size() > 1) {
1259 const Node *lastSegmentNode = m_forest.findTypeNode(QStringList{path.last()}, relative, genus);
1260 if (lastSegmentNode && lastSegmentNode->isQmlType())
1261 return lastSegmentNode;
1262 }
1263
1264 return nullptr;
1265 }
1266
1267 // For C++ contexts or QML types with "::" notation, use C++ path splitting
1268 QStringList path = type.split("::");
1269 if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
1270 auto it = s_typeNodeMap.find(path.at(0));
1271 if (it != s_typeNodeMap.end())
1272 return it.value();
1273 }
1274 return m_forest.findTypeNode(path, relative, genus);
1275}
1276
1277/*!
1278 Finds the node that will generate the documentation that
1279 contains the \a target and returns a pointer to it.
1280
1281 Can this be improved by using the target map in Tree?
1282 */
1283const Node *QDocDatabase::findNodeForTarget(const QString &target, const Node *relative)
1284{
1285 const Node *node = nullptr;
1286 if (target.isEmpty())
1287 node = relative;
1288 else if (target.endsWith(".html"))
1289 node = findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1290 else {
1291 QStringList path = target.split("::");
1292 int flags = SearchBaseClasses | SearchEnumValues;
1293 for (const auto *tree : searchOrder()) {
1294 const Node *n = tree->findNode(path, relative, flags, Genus::DontCare);
1295 if (n)
1296 return n;
1297 relative = nullptr;
1298 }
1299 node = findPageNodeByTitle(target);
1300 }
1301 return node;
1302}
1303
1304/*!
1305 Finds the node for \a target with genus and module scoping.
1306
1307 When \a moduleName is non-empty, the search is scoped to that
1308 module's tree. Function signatures (targets ending with \c{()})
1309 are parsed and dispatched to function-specific lookup. QML
1310 dot-path targets are resolved via import-aware type lookup when
1311 the \a genus is QML.
1312
1313 This overload extracts the dispatch logic from findNodeForAtom()
1314 into a method that takes plain parameters instead of an Atom
1315 pointer, enabling callers without Atom access (such as
1316 LinkResolver) to use the same scoped lookup.
1317*/
1318const Node *QDocDatabase::findNodeForTarget(const QString &target, const Node *relative,
1319 Genus genus, const QString &moduleName,
1320 QString *ref)
1321{
1322 if (target.isEmpty())
1323 return relative;
1324
1325 Tree *domain = moduleName.isEmpty() ? nullptr : findTree(moduleName);
1326
1327 if (domain) {
1328 const Node *node = nullptr;
1329 if (target.endsWith(".html"_L1)) {
1330 node = domain->findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1331 } else if (target.endsWith(')'_L1)) {
1332 QString function = target;
1333 QString signature;
1334 if (function.endsWith("()"_L1))
1335 function.chop(2);
1336 if (function.endsWith(')'_L1)) {
1337 qsizetype position = function.lastIndexOf('('_L1);
1338 signature = function.mid(position + 1, function.size() - position - 2);
1339 function = function.left(position);
1340 }
1341 QStringList path = function.split("::"_L1);
1342 node = domain->findFunctionNode(path, Parameters(signature), nullptr, genus);
1343 }
1344 if (node)
1345 return node;
1346
1347 if (genus == Genus::QML && target.contains('.'_L1) && !target.contains("::"_L1)) {
1348 int typeFlags = SearchBaseClasses | SearchEnumValues | TypesOnly;
1349 QStringList path = target.split('.'_L1);
1350 node = domain->findNode(path, relative, typeFlags, genus);
1351 if (node)
1352 return node;
1353 if (path.size() > 1) {
1354 node = domain->findNode(QStringList{path.last()}, relative, typeFlags, genus);
1355 if (node && node->isQmlType())
1356 return node;
1357 }
1358 }
1359
1360 int flags = SearchBaseClasses | SearchEnumValues;
1361 QStringList nodePath = target.split("::"_L1);
1362 if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
1363 relative = nullptr;
1364 QString localRef;
1365 const Node *result =
1366 domain->findNodeForTarget(nodePath, {}, relative, flags, genus, localRef);
1367 if (result && ref)
1368 *ref = localRef;
1369 return result;
1370 }
1371
1372 // Forest-wide search: function signatures, QML dot-paths, then general.
1373 if (target.endsWith(".html"_L1))
1374 return findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1375
1376 if (target.endsWith(')'_L1)) {
1377 const Node *node = findFunctionNode(target, relative, genus);
1378 if (node)
1379 return node;
1380 }
1381
1382 if (genus == Genus::QML && target.contains('.'_L1) && !target.contains("::"_L1)) {
1383 const Node *node = findTypeNode(target, relative, genus);
1384 if (node)
1385 return node;
1386 }
1387
1388 QStringList targetPath = Utilities::pathAndFragment(target);
1389 int flags = SearchBaseClasses | SearchEnumValues;
1390 QString localRef;
1391 const Node *node = findNodeForTarget(targetPath, relative, genus, localRef, flags);
1392 if (node) {
1393 if (ref)
1394 *ref = localRef;
1395 return node;
1396 }
1397
1398 return findPageNodeByTitle(target);
1399}
1400
1402{
1403 QStringList result;
1404 CNMap *m = primaryTree()->getCollectionMap(NodeType::Group);
1405
1406 if (!m)
1407 return result;
1408
1409 for (auto it = m->cbegin(); it != m->cend(); ++it)
1410 if (it.value()->members().contains(node))
1411 result << it.key();
1412
1413 return result;
1414}
1415
1416/*!
1417 Reads and parses the qdoc index files listed in \a indexFiles.
1418 */
1419void QDocDatabase::readIndexes(const QStringList &indexFiles)
1420{
1421 QStringList filesToRead;
1422 for (const QString &file : indexFiles) {
1423 QString fn = file.mid(file.lastIndexOf(QChar('/')) + 1);
1424 if (!isLoaded(fn))
1425 filesToRead << file;
1426 else
1427 qCCritical(lcQdoc) << "Index file" << file << "is already in memory.";
1428 }
1429 QDocIndexFiles::qdocIndexFiles()->readIndexes(filesToRead);
1430}
1431
1432/*!
1433 Generates a qdoc index file and writes it to \a fileName. The
1434 index file is generated with the parameters \a url, \a title,
1435 and \a context, which supplies publication locations and serialization
1436 policy independently of the rendering implementation.
1437 */
1438void QDocDatabase::generateIndex(const QString &fileName, const QString &url, const QString &title,
1439 const IndexSerializationContext &context)
1440{
1441 QString t = fileName.mid(fileName.lastIndexOf(QChar('/')) + 1);
1442 primaryTree()->setIndexFileName(t);
1443 QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, context);
1444 QDocIndexFiles::destroyQDocIndexFiles();
1445}
1446
1447/*!
1448 Returns the collection node representing the module that \a relative
1449 node belongs to, or \c nullptr if there is no such module in the
1450 primary tree.
1451*/
1453{
1454 NodeType moduleType{NodeType::Module};
1455 QString moduleName;
1456 switch (relative->genus())
1457 {
1458 case Genus::CPP:
1459 moduleType = NodeType::Module;
1460 moduleName = relative->physicalModuleName();
1461 break;
1462 case Genus::QML:
1463 moduleType = NodeType::QmlModule;
1464 moduleName = relative->logicalModuleName();
1465 break;
1466 default:
1467 return nullptr;
1468 }
1469 if (moduleName.isEmpty())
1470 return nullptr;
1471
1472 return primaryTree()->getCollection(moduleName, moduleType);
1473}
1474
1475/*!
1476 Finds all the collection nodes of the specified \a type
1477 and merges them into the collection node map \a cnm. Nodes
1478 that match the \a relative node are not included.
1479 */
1480void QDocDatabase::mergeCollections(NodeType type, CNMap &cnm, const Node *relative)
1481{
1482 cnm.clear();
1483 CNMultiMap cnmm;
1484 for (auto *tree : searchOrder()) {
1485 CNMap *m = tree->getCollectionMap(type);
1486 if (m && !m->isEmpty()) {
1487 for (auto it = m->cbegin(); it != m->cend(); ++it) {
1488 if (!it.value()->isInternal())
1489 cnmm.insert(it.key(), it.value());
1490 }
1491 }
1492 }
1493 if (cnmm.isEmpty())
1494 return;
1495 static const QRegularExpression singleDigit("\\b([0-9])\\b");
1496 const QStringList keys = cnmm.uniqueKeys();
1497 for (const auto &key : keys) {
1498 const QList<CollectionNode *> values = cnmm.values(key);
1499 CollectionNode *n = nullptr;
1500 for (auto *value : values) {
1501 if (value && value->wasSeen() && value != relative) {
1502 n = value;
1503 break;
1504 }
1505 }
1506 if (n) {
1507 if (values.size() > 1) {
1508 for (CollectionNode *value : values) {
1509 if (value != n) {
1510 // Allow multiple (major) versions of QML modules
1511 if ((n->isQmlModule())
1512 && n->logicalModuleIdentifier() != value->logicalModuleIdentifier()) {
1513 if (value->wasSeen() && value != relative)
1514 cnm.insert(value->fullTitle().toLower(), value);
1515 continue;
1516 }
1517 for (Node *t : value->members())
1518 n->addMember(t);
1519 }
1520 }
1521 }
1522 QString sortKey = n->fullTitle().toLower();
1523 if (sortKey.startsWith("the "))
1524 sortKey.remove(0, 4);
1525 sortKey.replace(singleDigit, "0\\1");
1526 cnm.insert(sortKey, n);
1527 }
1528 }
1529}
1530
1531/*!
1532 Finds all the collection nodes with the same name
1533 and type as \a c and merges their members into the
1534 members list of \a c.
1535
1536 For QML modules, only nodes with matching
1537 module identifiers are merged to avoid merging
1538 modules with different (major) versions.
1539 */
1541{
1542 if (c == nullptr)
1543 return;
1544
1545 // REMARK: This form of merging is usually called during the
1546 // generation phase om-the-fly when a source-of-truth collection
1547 // is required.
1548 // In practice, this means a collection could be merged many, many
1549 // times during the lifetime of a generation.
1550 // To avoid repeating the merging process each time, which could
1551 // be time consuming, we use a small flag that is set directly on
1552 // the collection to bail-out early.
1553 //
1554 // The merging process is only meaningful for collections when the
1555 // collection references are spread troughout multiple projects.
1556 // The part of information that exists in other project is read
1557 // before the generation phase, such that when the generation
1558 // phase comes, we already have all the information we need for
1559 // merging such that we can consider all version of a certain
1560 // collection node immutable, making the caching inherently
1561 // correct at any point of the generation.
1562 //
1563 // This implies that this operation is unsafe if it is performed
1564 // before all the index files are loaded.
1565 // Indeed, this is a prerequisite, with the current structure, to
1566 // perform this optmization.
1567 //
1568 // At the current time, this is true and is expected not to
1569 // change.
1570 //
1571 // Do note that this is not applied to the other overload of
1572 // mergeCollections as we cannot as safely ensure its consistency
1573 // and, as the result of the merging depends on multiple
1574 // parameters, it would require an actual memoization of the call.
1575 //
1576 // Note that this is a defensive optimization and we are assuming
1577 // that it is effective based on heuristical data. As this is
1578 // expected to disappear, at least in its current form, in the
1579 // future, a more thorough analysis was not performed.
1580 if (c->isMerged()) {
1581 return;
1582 }
1583
1584 for (auto *tree : searchOrder()) {
1585 CollectionNode *cn = tree->getCollection(c->name(), c->nodeType());
1586 if (cn && cn != c) {
1587 if ((cn->isQmlModule())
1588 && cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
1589 continue;
1590
1591 for (auto *node : cn->members())
1592 c->addMember(node);
1593
1594 // REMARK: The merging process is performed to ensure that
1595 // references to the collection in external projects are
1596 // taken into account before consuming the collection.
1597 //
1598 // This works by having QDoc construct empty collections
1599 // as soon as a reference to a collection is encountered
1600 // and filling details later on when its definition is
1601 // found.
1602 //
1603 // This initially-empty collection is always saved to the
1604 // primaryTree and it is the collection that is directly
1605 // accessible to consumers during the generation process.
1606 //
1607 // Nonetheless, when the definition for the collection is
1608 // not in the same project as the one that is being
1609 // compiled, its details will never be filled in.
1610 //
1611 // Indeed, the details will live in the index file for the
1612 // project where the collection is defined, if any, and
1613 // the node for it, which has complete information, will
1614 // live in some non-primaryTree.
1615 //
1616 // The merging process itself is used by consumers during
1617 // the generation process because they access the
1618 // primaryTree version of the collection expecting a
1619 // source-of-truth.
1620 // To ensure that this is the case for usages that
1621 // requires linking, we need to merge not only the members
1622 // of the collection that reside in external versions of
1623 // the collection; but some of the data that reside in the
1624 // definition of the collection intself, namely the title
1625 // and the url.
1626 //
1627 // A collection that contains the data of a definition is
1628 // always marked as seen, hence we use that to discern
1629 // whether we are working with a placeholder node or not,
1630 // and fill in the data if we encounter a node that
1631 // represents a definition.
1632 //
1633 // The way in which QDoc works implies that collection are
1634 // globally scoped between projects.
1635 // The repetition of the definition for the same
1636 // collection is warned for as a duplicate documentation,
1637 // such that we can expect a single valid source of truth
1638 // for a given collection in each project.
1639 // It is currently unknown if this warning is applicable
1640 // when the repeated collection is defined in two
1641 // different projects.
1642 //
1643 // As QDoc implicitly would not correctly support this
1644 // case, we assume that only one declaration exists for
1645 // each collection, such that the first encoutered one
1646 // must be the source of truth and that there is no need
1647 // to copy any data after the first copy is performed.
1648 // KLUDGE: Note that this process is done as a hackish
1649 // solution to QTBUG-104237 and should not be considered
1650 // final or dependable.
1651 if (!c->wasSeen() && cn->wasSeen()) {
1652 c->markSeen();
1653 c->setTitle(cn->title());
1654 c->setUrl(cn->url());
1655 c->setResolvedPhysicalModuleName(cn->tree()->physicalModuleName());
1656 }
1657 }
1658 }
1659
1661}
1662
1663/*!
1664 Returns the node to a piece of QML API documentation that corresponds to
1665 the given \a path, or \c nullptr if the path cannot be resolved.
1666 The \a relative node specifies where the link or reference was used.
1667
1668 This doesn't handle the use of fragments to refer to elements in the
1669 target documentation.
1670*/
1671const Node *QDocDatabase::findQmlNode(const QString &path, const Node *relative)
1672{
1673 // Since path contains a dot, assume that it is either a QML module
1674 // in its own right or a QML path. Break the path where path separators
1675 // for C++ or QML occur.
1676 QStringList pieces;
1677 for (auto &piece : path.split("."_L1)) {
1678 for (auto &inner : piece.split("::"_L1)) {
1679 pieces.append(inner);
1680 }
1681 }
1682
1683 // Try to resolve a leading QML module name.
1684 QString modName{""};
1685 QString tryName{""};
1686 const Node *modNode{nullptr};
1687 int i;
1688
1689 for (i = 0; i < pieces.count(); i++) {
1690 tryName += pieces.at(i);
1691 const Node *node = findNodeByNameAndType(QStringList(tryName), &Node::isQmlModule);
1692
1693 if (node) {
1694 modName = tryName;
1695 modNode = node;
1696 } else
1697 break;
1698
1699 tryName += "."_L1;
1700 }
1701
1702 // No more pieces to check, so return what we have.
1703 if (i == pieces.count())
1704 return modNode;
1705
1706 QmlTypeNode *qtn{nullptr};
1707
1708 if (modNode) {
1709 pieces = pieces.mid(i);
1710 qtn = findQmlType(modName, pieces.takeFirst(), relative);
1711 } else
1712 qtn = findQmlType(pieces.takeFirst(), relative);
1713
1714 // Return a null pointer or the leaf node.
1715 if (!qtn || pieces.isEmpty())
1716 return qtn;
1717
1718 auto propertyName = pieces.join("."_L1);
1719 QmlPropertyNode *qmlProperty = qtn->hasQmlProperty(propertyName);
1720
1721 if (qmlProperty)
1722 return qmlProperty;
1723
1724 // Fall back on a general search for a member with the name.
1725 // This will intentionally fail for functions if there is a dot
1726 // in the name.
1727 return qtn->findChildNode(propertyName, Genus::QML);
1728}
1729
1730/*!
1731 Searches for the node that matches the path in \a atom and the
1732 specified \a genus. The \a relative node is used if the first
1733 leg of the path is empty, i.e. if the path begins with '#'.
1734 The function also sets \a ref if there remains an unused leg
1735 in the path after the node is found. The node is returned as
1736 well as the \a ref. If the returned node pointer is null,
1737 \a ref is also not valid.
1738 */
1739const Node *QDocDatabase::findNodeForAtom(const Atom *a, const Node *relative, QString &ref,
1740 Genus genus)
1741{
1742 const Node *node = nullptr;
1743
1744 Atom *atom = const_cast<Atom *>(a);
1745 QStringList targetPath = Utilities::pathAndFragment(atom->string());
1746 QString first = targetPath.first().trimmed();
1747
1748 Tree *domain = nullptr;
1749
1750 if (atom->isLinkAtom()) {
1751 if (auto *atomDomain = atom->domain())
1752 domain = atomDomain;
1753 if (atom->genus() != Genus::DontCare)
1754 genus = atom->genus();
1755
1756 if (!first.contains("://"_L1) && !first.contains(" "_L1) && !first.endsWith(".html"_L1) && first.contains("."_L1)) {
1757 const Node *found = findQmlNode(first, relative);
1758 if (found) return found;
1759 }
1760 }
1761
1762 if (first.isEmpty())
1763 node = relative; // search for a target on the current page.
1764 else if (domain) {
1765 if (first.endsWith(".html"))
1766 node = domain->findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1767 else if (first.endsWith(QChar(')'))) {
1768 QString signature;
1769 QString function = first;
1770 qsizetype length = first.size();
1771 if (function.endsWith("()"))
1772 function.chop(2);
1773 if (function.endsWith(QChar(')'))) {
1774 qsizetype position = function.lastIndexOf(QChar('('));
1775 signature = function.mid(position + 1, length - position - 2);
1776 function = function.left(position);
1777 }
1778 QStringList path = function.split("::");
1779 node = domain->findFunctionNode(path, Parameters(signature), nullptr, genus);
1780 }
1781 if (node == nullptr) {
1782 int flags = SearchBaseClasses | SearchEnumValues;
1783 QStringList nodePath = first.split("::");
1784 QString target;
1785 targetPath.removeFirst();
1786 if (!targetPath.isEmpty())
1787 target = targetPath.takeFirst();
1788 if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
1789 relative = nullptr;
1790 return domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
1791 }
1792 } else {
1793 if (first.endsWith(".html"))
1794 node = findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1795 else if (first.endsWith(QChar(')')))
1796 node = findFunctionNode(first, relative, genus);
1797 if (node == nullptr) {
1798 // For QML contexts with qualified names containing ".", use the same logic as findTypeNode
1799 if (genus == Genus::QML && first.contains('.') && !first.contains("::")) {
1800 // Try import-aware lookup using findTypeNode logic
1801 node = findTypeNode(first, relative, genus);
1802 if (node) {
1803 // Handle any fragment reference
1804 targetPath.removeFirst();
1805 if (!targetPath.isEmpty()) {
1806 ref = node->root()->tree()->getRef(targetPath.first(), node);
1807 if (ref.isEmpty())
1808 node = nullptr;
1809 }
1810 return node;
1811 }
1812 }
1813 return findNodeForTarget(targetPath, relative, genus, ref, atom->flags());
1814 }
1815 }
1816
1817 if (node != nullptr && ref.isEmpty()) {
1818 if (!node->url().isEmpty())
1819 return node;
1820 targetPath.removeFirst();
1821 if (!targetPath.isEmpty()) {
1822 ref = node->root()->tree()->getRef(targetPath.first(), node);
1823 if (ref.isEmpty())
1824 node = nullptr;
1825 }
1826 }
1827 return node;
1828}
1829
1830/*!
1831 Updates navigation (previous/next page links and the navigation parent)
1832 for pages listed in the TOC, specified by the \c navigation.toctitles
1833 configuration variable.
1834
1835 if \c navigation.toctitles.inclusive is \c true, include also the TOC
1836 page(s) themselves as a 'root' item in the navigation bar (breadcrumbs)
1837 that are generated for HTML output.
1838*/
1840{
1841 // Restrict searching only to the local (primary) tree
1842 QList<Tree *> searchOrder = this->searchOrder();
1844
1845 const QString configVar = CONFIG_NAVIGATION +
1846 Config::dot +
1848
1849 // TODO: [direct-configuration-access]
1850 // The configuration is currently a singleton with some generally
1851 // global mutable state.
1852 //
1853 // Accessing the data in this form complicates testing and
1854 // requires tests that inhibit any test parallelization, as the
1855 // tests are not self contained.
1856 //
1857 // This should be generally avoived. Possibly, we should strive
1858 // for Config to be a POD type that generally is scoped to
1859 // main and whose data is destructured into dependencies when
1860 // the dependencies are constructed.
1861 bool inclusive{Config::instance().get(
1862 configVar + Config::dot + CONFIG_INCLUSIVE).asBool()};
1863
1864 // TODO: [direct-configuration-access]
1865 const auto tocTitles{Config::instance().get(configVar).asStringList()};
1866
1867 for (const auto &tocTitle : tocTitles) {
1868 if (const auto candidateTarget = findNodeForTarget(tocTitle, nullptr); candidateTarget && candidateTarget->isPageNode()) {
1869 auto tocPage{static_cast<const PageNode*>(candidateTarget)};
1870
1871 Text body = tocPage->doc().body();
1872
1873 auto *atom = body.firstAtom();
1874
1875 std::pair<PageNode *, QString> prev { nullptr, QString() };
1876
1877 std::stack<const PageNode *> tocStack;
1878 tocStack.push(inclusive ? tocPage : nullptr);
1879
1880 bool inItem = false;
1881
1882 // TODO: Understand how much we use this form of looping over atoms.
1883 // If it is used a few times we might consider providing
1884 // an iterator for Text to make use of a simpler
1885 // range-for loop.
1886 while (atom) {
1887 switch (atom->type()) {
1888 case Atom::ListItemLeft:
1889 // Not known if we're going to have a link, push a temporary
1890 tocStack.push(nullptr);
1891 inItem = true;
1892 break;
1893 case Atom::ListItemRight:
1894 tocStack.pop();
1895 inItem = false;
1896 break;
1897 case Atom::Link: {
1898 if (!inItem)
1899 break;
1900
1901 // TODO: [unnecessary-output-parameter]
1902 // We currently need an lvalue string to
1903 // pass to findNodeForAtom, as the
1904 // outparameter ref.
1905 //
1906 // Apart from the general problems with output
1907 // parameters, we shouldn't be forced to
1908 // instanciate an unnecessary object at call
1909 // site.
1910 //
1911 // Understand what the correct way to avoid this is.
1912 // This requires changes to findNodeForAtom
1913 // and should be addressed in the context of
1914 // revising that method.
1915 QString unused{};
1916 // TODO: Having to const cast is really a code
1917 // smell and could result in undefined
1918 // behavior in some specific cases (e.g point
1919 // to something that is actually const).
1920 //
1921 // We should understand how to sequence the
1922 // code so that we have access to mutable data
1923 // when we need it and "freeze" the data
1924 // afterwards.
1925 //
1926 // If it we expect this form of mutability at
1927 // this point we should expose a non-const API
1928 // for the database, possibly limited to a
1929 // very specific scope of execution.
1930 //
1931 // Understand what the correct sequencing for
1932 // this processing is and revise this part.
1933 auto candidatePage = const_cast<Node *>(findNodeForAtom(atom, nullptr, unused));
1934 if (!candidatePage || !candidatePage->isPageNode()) break;
1935
1936 auto page{static_cast<PageNode*>(candidatePage)};
1937
1938 // ignore self-references
1939 if (page == prev.first) break;
1940
1941 if (prev.first) {
1942 prev.first->setLink(
1943 Node::NextLink,
1944 page->title(),
1945 // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
1946 // As with other structures in QDoc we
1947 // are able to call methods that are
1948 // valid only on very specific states.
1949 //
1950 // For some of those calls we have
1951 // some defensive programming measures
1952 // that allow us to at least identify
1953 // the error during debugging, while
1954 // for others this may currently hide
1955 // some logic error.
1956 //
1957 // To avoid those cases, we should
1958 // strive to move those cases to a
1959 // compilation error, requiring a
1960 // statically analyzable state that
1961 // represents the current model.
1962 //
1963 // This would ensure that those
1964 // lingering class of bugs are
1965 // eliminated completely, forces a
1966 // more explicit codebase where the
1967 // current capabilities do not depend
1968 // on runtime values might not be
1969 // generally visible, and does not
1970 // require us to incur into the
1971 // required state, which may be rare,
1972 // simplifying our abilities to
1973 // evaluate all possible states.
1974 //
1975 // For linking atoms, LinkAtom is
1976 // available and might be a good
1977 // enough solution to move linkText
1978 // to.
1979 atom->linkText()
1980 );
1981 page->setLink(
1982 Node::PreviousLink,
1983 prev.first->title(),
1984 prev.second
1985 );
1986 }
1987
1988 if (page == tocPage)
1989 break;
1990
1991 // Find the navigation parent from the stack; we may have null pointers
1992 // for non-link list items, so skip those.
1993 qsizetype popped = 0;
1994 while (tocStack.size() > 1 && !tocStack.top()) {
1995 tocStack.pop();
1996 ++popped;
1997 }
1998
1999 page->setNavigationParent(tocStack.empty() ? nullptr : tocStack.top());
2000
2001 while (--popped > 0)
2002 tocStack.push(nullptr);
2003
2004 tocStack.push(page);
2005 // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
2006 prev = { page, atom->linkText() };
2007 }
2008 break;
2009
2010 case Atom::AnnotatedList:
2011 case Atom::GeneratedList: {
2012 if (const auto *cn = getCollectionNode(atom->string(), NodeType::Group)) {
2013 const auto sortOrder{Generator::sortOrder(atom->strings().last())};
2014 NodeList members{cn->members()};
2015 // Drop non-page nodes and index nodes so that we do not generate navigational
2016 // links pointing outside of this documentation set.
2017 members.erase(std::remove_if(members.begin(), members.end(),
2018 [](const Node *n) {
2019 return n->isIndexNode() || !n->isPageNode() || n->isExternalPage();
2020 }), members.end());
2021 if (members.isEmpty())
2022 break;
2023
2024 if (sortOrder == Qt::DescendingOrder)
2025 std::sort(members.rbegin(), members.rend(), Node::nodeSortKeyOrNameLessThan);
2026 else
2027 std::sort(members.begin(), members.end(), Node::nodeSortKeyOrNameLessThan);
2028
2029 // `members` now has local PageNode pointers, adjust prev/next links for each.
2030 // Do not set a navigation parent node as group members use the group node as
2031 // their nav. parent.
2032 for (auto *m : members) {
2033 auto *page = static_cast<PageNode *>(m);
2034 if (prev.first) {
2035 prev.first->setLink(Node::NextLink, page->title(), page->fullName());
2036 page->setLink(Node::PreviousLink, prev.first->title(), prev.second);
2037 }
2038 prev = { page, page->fullName() };
2039 }
2040 }
2041 }
2042 break;
2043
2044 default:
2045 break;
2046 }
2047
2048 atom = atom->next();
2049 }
2050 } else {
2051 Config::instance().get(configVar).location()
2052 .warning(QStringLiteral("Failed to find table of contents with title '%1'")
2053 .arg(tocTitle));
2054 }
2055 }
2056
2057 // Restore search order
2058 setSearchOrder(searchOrder);
2059}
2060
2061QT_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:152
virtual Tree * domain()
Definition atom.h:154
virtual Genus genus()
Definition atom.h:153
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 generateIndex(const QString &fileName, const QString &url, const QString &title, const IndexSerializationContext &context)
Generates a qdoc index file and writes it to fileName.
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()
A class representing a forest of Tree objects.
This class imports QDoc index files and serializes index sections for module indexes and WebXML outpu...
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:1650
NodeList & proxies()
Definition tree.h:78
void resolveProperties()
Resolves access functions associated with each PropertyNode stored in m_unresolvedPropertyMap,...
Definition tree.cpp:261
#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:165
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:247
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