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