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 return nullptr;
657
658 QString type{name};
659
660 // If the import is under a namespace (id) and the type name is not prefixed with that id,
661 // then we know the type is not available under this import.
662 if (!record.m_importId.isEmpty()) {
663 const QString namespacePrefix{"%1."_L1.arg(record.m_importId)};
664 if (!type.startsWith(namespacePrefix))
665 return nullptr;
666 type.remove(0, namespacePrefix.size());
667 }
668
669 const QString qmName = record.m_importUri.isEmpty() ? record.m_moduleName : record.m_importUri;
670 return m_forest.lookupQmlType(qmName + u"::"_s + type);
671}
672
673/*!
674 Returns the QML node identified by the QML module id \a qmid
675 and \a name, searching in the primary tree only. If \a qmid
676 is an empty string, searches for the node using name only.
677
678 Returns \c nullptr if no node was found.
679*/
680QmlTypeNode *QDocDatabase::findQmlTypeInPrimaryTree(const QString &qmid, const QString &name)
681{
682 if (!qmid.isEmpty())
683 return primaryTree()->lookupQmlType(qmid + u"::"_s + name);
684 return static_cast<QmlTypeNode *>(primaryTreeRoot()->findChildNode(name, Node::QML, TypesOnly));
685}
686
687/*!
688 This function calls a set of functions for each tree in the
689 forest that has not already been analyzed. In this way, when
690 running qdoc in \e singleExec mode, each tree is analyzed in
691 turn, and its classes and types are added to the appropriate
692 node maps.
693 */
695{
696 processForest(&QDocDatabase::findAllClasses);
697 processForest(&QDocDatabase::findAllFunctions);
698 processForest(&QDocDatabase::findAllObsoleteThings);
699 processForest(&QDocDatabase::findAllLegaleseTexts);
700 processForest(&QDocDatabase::findAllSince);
701 processForest(&QDocDatabase::findAllAttributions);
703}
704
705/*!
706 This function calls \a func for each tree in the forest,
707 ensuring that \a func is called only once per tree.
708
709 \sa processForest()
710 */
711void QDocDatabase::processForest(FindFunctionPtr func)
712{
713 Tree *t = m_forest.firstTree();
714 while (t) {
715 if (!m_completedFindFunctions.values(t).contains(func)) {
716 (this->*(func))(t->root());
717 m_completedFindFunctions.insert(t, func);
718 }
719 t = m_forest.nextTree();
720 }
721}
722
723/*!
724 Returns a reference to the collection of legalese texts.
725 */
727{
728 processForest(&QDocDatabase::findAllLegaleseTexts);
729 return m_legaleseTexts;
730}
731
732/*!
733 Returns a reference to the map of C++ classes with obsolete members.
734 */
736{
737 processForest(&QDocDatabase::findAllObsoleteThings);
738 return s_classesWithObsoleteMembers;
739}
740
741/*!
742 Returns a reference to the map of obsolete QML types.
743 */
745{
746 processForest(&QDocDatabase::findAllObsoleteThings);
747 return s_obsoleteQmlTypes;
748}
749
750/*!
751 Returns a reference to the map of QML types with obsolete members.
752 */
754{
755 processForest(&QDocDatabase::findAllObsoleteThings);
756 return s_qmlTypesWithObsoleteMembers;
757}
758
759/*!
760 Returns a reference to the map of QML basic types.
761 */
763{
764 processForest(&QDocDatabase::findAllClasses);
765 return s_qmlBasicTypes;
766}
767
768/*!
769 Returns a reference to the multimap of QML types.
770 */
772{
773 processForest(&QDocDatabase::findAllClasses);
774 return s_qmlTypes;
775}
776
777/*!
778 Returns a reference to the multimap of example nodes.
779 */
781{
782 processForest(&QDocDatabase::findAllClasses);
783 return s_examples;
784}
785
786/*!
787 Returns a reference to the multimap of attribution nodes.
788 */
790{
791 processForest(&QDocDatabase::findAllAttributions);
792 return m_attributions;
793}
794
795/*!
796 Returns a reference to the map of obsolete C++ clases.
797 */
799{
800 processForest(&QDocDatabase::findAllObsoleteThings);
801 return s_obsoleteClasses;
802}
803
804/*!
805 Returns a reference to the map of all C++ classes.
806 */
808{
809 processForest(&QDocDatabase::findAllClasses);
810 return s_cppClasses;
811}
812
813/*!
814 Returns the function index. This data structure is used to
815 output the function index page.
816 */
818{
819 processForest(&QDocDatabase::findAllFunctions);
820 return m_functionIndex;
821}
822
823/*!
824 Finds all the nodes containing legalese text and puts them
825 in a map.
826 */
827void QDocDatabase::findAllLegaleseTexts(Aggregate *node)
828{
829 for (const auto &childNode : node->childNodes()) {
830 if (childNode->isPrivate())
831 continue;
832 if (!childNode->doc().legaleseText().isEmpty())
833 m_legaleseTexts.insert(childNode->doc().legaleseText(), childNode);
834 if (childNode->isAggregate())
835 findAllLegaleseTexts(static_cast<Aggregate *>(childNode));
836 }
837}
838
839/*!
840 \fn void QDocDatabase::findAllObsoleteThings(Aggregate *node)
841
842 Finds all nodes with status = Deprecated and sorts them into
843 maps. They can be C++ classes, QML types, or they can be
844 functions, enum types, typedefs, methods, etc.
845 */
846
847/*!
848 \fn void QDocDatabase::findAllSince(Aggregate *node)
849
850 Finds all the nodes in \a node where a \e{since} command appeared
851 in the qdoc comment and sorts them into maps according to the kind
852 of node.
853
854 This function is used for generating the "New Classes... in x.y"
855 section on the \e{What's New in Qt x.y} page.
856 */
857
858/*!
859 Find the \a key in the map of new class maps, and return a
860 reference to the value, which is a NodeMap. If \a key is not
861 found, return a reference to an empty NodeMap.
862 */
863const NodeMultiMap &QDocDatabase::getClassMap(const QString &key)
864{
865 processForest(&QDocDatabase::findAllSince);
866 auto it = s_newClassMaps.constFind(key);
867 return (it != s_newClassMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
868}
869
870/*!
871 Find the \a key in the map of new QML type maps, and return a
872 reference to the value, which is a NodeMap. If the \a key is not
873 found, return a reference to an empty NodeMap.
874 */
875const NodeMultiMap &QDocDatabase::getQmlTypeMap(const QString &key)
876{
877 processForest(&QDocDatabase::findAllSince);
878 auto it = s_newQmlTypeMaps.constFind(key);
879 return (it != s_newQmlTypeMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
880}
881
882/*!
883 Find the \a key in the map of new \e {since} maps, and return
884 a reference to the value, which is a NodeMultiMap. If \a key
885 is not found, return a reference to an empty NodeMultiMap.
886 */
887const NodeMultiMap &QDocDatabase::getSinceMap(const QString &key)
888{
889 processForest(&QDocDatabase::findAllSince);
890 auto it = s_newSinceMaps.constFind(key);
891 return (it != s_newSinceMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
892}
893
894/*!
895 Performs several housekeeping tasks prior to generating the
896 documentation. These tasks create required data structures
897 and resolve links.
898 */
900{
901 const auto &config = Config::instance();
902 if (config.dualExec() || config.preparing()) {
903 // order matters
904 primaryTree()->resolveBaseClasses(primaryTreeRoot());
905 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
909 primaryTree()->removePrivateAndInternalBases(primaryTreeRoot());
910 primaryTree()->resolveProperties();
913 primaryTree()->resolveTargets(primaryTreeRoot());
914 primaryTree()->resolveCppToQmlLinks();
915 primaryTree()->resolveSince(*primaryTreeRoot());
916 }
917 if (config.singleExec() && config.generating()) {
918 primaryTree()->resolveBaseClasses(primaryTreeRoot());
919 primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
921 primaryTree()->resolveCppToQmlLinks();
922 primaryTree()->resolveSince(*primaryTreeRoot());
923 }
924 if (!config.preparing()) {
929 }
930 if (config.dualExec())
931 QDocIndexFiles::destroyQDocIndexFiles();
932}
933
935{
936 Tree *t = m_forest.firstTree();
937 while (t) {
938 t->resolveBaseClasses(t->root());
939 t = m_forest.nextTree();
940 }
941}
942
943/*!
944 Returns a reference to the namespace map. Constructs the
945 namespace map if it hasn't been constructed yet.
946
947 \note This function must not be called in the prepare phase.
948 */
950{
952 return m_namespaceIndex;
953}
954
955/*!
956 Multiple namespace nodes for namespace X can exist in the
957 qdoc database in different trees. This function first finds
958 all namespace nodes in all the trees and inserts them into
959 a multimap. Then it combines all the namespace nodes that
960 have the same name into a single namespace node of that
961 name and inserts that combined namespace node into an index.
962 */
964{
965 if (!m_namespaceIndex.isEmpty())
966 return;
967
968 bool linkErrors = !Config::instance().get(CONFIG_NOLINKERRORS).asBool();
969 NodeMultiMap namespaceMultimap;
970 Tree *t = m_forest.firstTree();
971 while (t) {
972 t->root()->findAllNamespaces(namespaceMultimap);
973 t = m_forest.nextTree();
974 }
975 const QList<QString> keys = namespaceMultimap.uniqueKeys();
976 for (const QString &key : keys) {
977 NamespaceNode *ns = nullptr;
978 NamespaceNode *indexNamespace = nullptr;
979 const NodeList namespaces = namespaceMultimap.values(key);
980 qsizetype count = namespaceMultimap.remove(key);
981 if (count > 0) {
982 for (auto *node : namespaces) {
983 ns = static_cast<NamespaceNode *>(node);
984 if (ns->isDocumentedHere())
985 break;
986 else if (ns->hadDoc())
987 indexNamespace = ns; // namespace was documented but in another tree
988 ns = nullptr;
989 }
990 if (ns) {
991 for (auto *node : namespaces) {
992 auto *nsNode = static_cast<NamespaceNode *>(node);
993 if (nsNode->hadDoc() && nsNode != ns) {
994 ns->doc().location().warning(
995 QStringLiteral("Namespace %1 documented more than once")
996 .arg(nsNode->name()), QStringLiteral("also seen here: %1")
997 .arg(nsNode->doc().location().toString()));
998 }
999 }
1000 } else if (!indexNamespace) {
1001 // Warn about documented children in undocumented namespaces.
1002 // As the namespace can be documented outside this project,
1003 // skip the warning if -no-link-errors is set
1004 if (linkErrors) {
1005 for (auto *node : namespaces) {
1006 if (!node->isIndexNode())
1007 static_cast<NamespaceNode *>(node)->reportDocumentedChildrenInUndocumentedNamespace();
1008 }
1009 }
1010 } else {
1011 for (auto *node : namespaces) {
1012 auto *nsNode = static_cast<NamespaceNode *>(node);
1013 if (nsNode != indexNamespace)
1014 nsNode->setDocNode(indexNamespace);
1015 }
1016 }
1017 }
1018 /*
1019 If there are multiple namespace nodes with the same
1020 name where one of them will be the main reference page
1021 for the namespace, include all nodes in the public
1022 API of the namespace.
1023 */
1024 if (ns && count > 1) {
1025 for (auto *node : namespaces) {
1026 auto *nameSpaceNode = static_cast<NamespaceNode *>(node);
1027 if (nameSpaceNode != ns) {
1028 for (auto it = nameSpaceNode->constBegin(); it != nameSpaceNode->constEnd();
1029 ++it) {
1030 Node *anotherNs = *it;
1031 if (anotherNs && anotherNs->isPublic() && !anotherNs->isInternal())
1032 ns->includeChild(anotherNs);
1033 }
1034 }
1035 }
1036 }
1037 /*
1038 Add the main namespace reference node to index, or the last seen
1039 namespace if the main one was not found.
1040 */
1041 if (!ns)
1042 ns = indexNamespace ? indexNamespace : static_cast<NamespaceNode *>(namespaces.last());
1043 m_namespaceIndex.insert(ns->name(), ns);
1044 }
1045}
1046
1047/*!
1048 Each instance of class Tree that represents an index file
1049 must be traversed to find all instances of class ProxyNode.
1050 For each ProxyNode found, look up the ProxyNode's name in
1051 the primary Tree. If it is found, it means that the proxy
1052 node contains elements (normally just functions) that are
1053 documented in the module represented by the Tree containing
1054 the proxy node but that are related to the node we found in
1055 the primary tree.
1056 */
1058{
1059 // The first tree is the primary tree.
1060 // Skip the primary tree.
1061 Tree *t = m_forest.firstTree();
1062 t = m_forest.nextTree();
1063 while (t) {
1064 const NodeList &proxies = t->proxies();
1065 if (!proxies.isEmpty()) {
1066 for (auto *node : proxies) {
1067 const auto *pn = static_cast<ProxyNode *>(node);
1068 if (pn->count() > 0) {
1069 Aggregate *aggregate = primaryTree()->findAggregate(pn->name());
1070 if (aggregate != nullptr)
1071 aggregate->appendToRelatedByProxy(pn->childNodes());
1072 }
1073 }
1074 }
1075 t = m_forest.nextTree();
1076 }
1077}
1078
1079/*!
1080 Finds the function node for the qualified function path in
1081 \a target and returns a pointer to it. The \a target is a
1082 function signature with or without parameters but without
1083 the return type.
1084
1085 \a relative is the node in the primary tree where the search
1086 begins. It is not used in the other trees, if the node is not
1087 found in the primary tree. \a genus can be used to force the
1088 search to find a C++ function or a QML function.
1089
1090 The entire forest is searched, but the first match is accepted.
1091 */
1092const FunctionNode *QDocDatabase::findFunctionNode(const QString &target, const Node *relative,
1093 Node::Genus genus)
1094{
1095 QString signature;
1096 QString function = target;
1097 qsizetype length = target.size();
1098 if (function.endsWith("()"))
1099 function.chop(2);
1100 if (function.endsWith(QChar(')'))) {
1101 qsizetype position = function.lastIndexOf(QChar('('));
1102 signature = function.mid(position + 1, length - position - 2);
1103 function = function.left(position);
1104 }
1105 QStringList path = function.split("::");
1106 return m_forest.findFunctionNode(path, Parameters(signature), relative, genus);
1107}
1108
1109/*!
1110 This function is called for autolinking to a \a type,
1111 which could be a function return type or a parameter
1112 type. The tree node that represents the \a type is
1113 returned. All the trees are searched until a match is
1114 found. When searching the primary tree, the search
1115 begins at \a relative and proceeds up the parent chain.
1116 When searching the index trees, the search begins at the
1117 root.
1118 */
1119const Node *QDocDatabase::findTypeNode(const QString &type, const Node *relative, Node::Genus genus)
1120{
1121 QStringList path = type.split("::");
1122 if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) {
1123 auto it = s_typeNodeMap.find(path.at(0));
1124 if (it != s_typeNodeMap.end())
1125 return it.value();
1126 }
1127 return m_forest.findTypeNode(path, relative, genus);
1128}
1129
1130/*!
1131 Finds the node that will generate the documentation that
1132 contains the \a target and returns a pointer to it.
1133
1134 Can this be improved by using the target map in Tree?
1135 */
1136const Node *QDocDatabase::findNodeForTarget(const QString &target, const Node *relative)
1137{
1138 const Node *node = nullptr;
1139 if (target.isEmpty())
1140 node = relative;
1141 else if (target.endsWith(".html"))
1142 node = findNodeByNameAndType(QStringList(target), &Node::isPageNode);
1143 else {
1144 QStringList path = target.split("::");
1145 int flags = SearchBaseClasses | SearchEnumValues;
1146 for (const auto *tree : searchOrder()) {
1147 const Node *n = tree->findNode(path, relative, flags, Node::DontCare);
1148 if (n)
1149 return n;
1150 relative = nullptr;
1151 }
1152 node = findPageNodeByTitle(target);
1153 }
1154 return node;
1155}
1156
1158{
1159 QStringList result;
1160 CNMap *m = primaryTree()->getCollectionMap(Node::Group);
1161
1162 if (!m)
1163 return result;
1164
1165 for (auto it = m->cbegin(); it != m->cend(); ++it)
1166 if (it.value()->members().contains(node))
1167 result << it.key();
1168
1169 return result;
1170}
1171
1172/*!
1173 Reads and parses the qdoc index files listed in \a indexFiles.
1174 */
1175void QDocDatabase::readIndexes(const QStringList &indexFiles)
1176{
1177 QStringList filesToRead;
1178 for (const QString &file : indexFiles) {
1179 QString fn = file.mid(file.lastIndexOf(QChar('/')) + 1);
1180 if (!isLoaded(fn))
1181 filesToRead << file;
1182 else
1183 qCCritical(lcQdoc) << "Index file" << file << "is already in memory.";
1184 }
1185 QDocIndexFiles::qdocIndexFiles()->readIndexes(filesToRead);
1186}
1187
1188/*!
1189 Generates a qdoc index file and write it to \a fileName. The
1190 index file is generated with the parameters \a url and \a title,
1191 using the generator \a g.
1192 */
1193void QDocDatabase::generateIndex(const QString &fileName, const QString &url, const QString &title,
1194 Generator *g)
1195{
1196 QString t = fileName.mid(fileName.lastIndexOf(QChar('/')) + 1);
1197 primaryTree()->setIndexFileName(t);
1198 QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g);
1199 QDocIndexFiles::destroyQDocIndexFiles();
1200}
1201
1202/*!
1203 Returns the collection node representing the module that \a relative
1204 node belongs to, or \c nullptr if there is no such module in the
1205 primary tree.
1206*/
1208{
1209 Node::NodeType moduleType{Node::Module};
1210 QString moduleName;
1211 switch (relative->genus())
1212 {
1213 case Node::CPP:
1214 moduleType = Node::Module;
1215 moduleName = relative->physicalModuleName();
1216 break;
1217 case Node::QML:
1218 moduleType = Node::QmlModule;
1219 moduleName = relative->logicalModuleName();
1220 break;
1221 default:
1222 return nullptr;
1223 }
1224 if (moduleName.isEmpty())
1225 return nullptr;
1226
1227 return primaryTree()->getCollection(moduleName, moduleType);
1228}
1229
1230/*!
1231 Finds all the collection nodes of the specified \a type
1232 and merges them into the collection node map \a cnm. Nodes
1233 that match the \a relative node are not included.
1234 */
1235void QDocDatabase::mergeCollections(Node::NodeType type, CNMap &cnm, const Node *relative)
1236{
1237 cnm.clear();
1238 CNMultiMap cnmm;
1239 for (auto *tree : searchOrder()) {
1240 CNMap *m = tree->getCollectionMap(type);
1241 if (m && !m->isEmpty()) {
1242 for (auto it = m->cbegin(); it != m->cend(); ++it) {
1243 if (!it.value()->isInternal())
1244 cnmm.insert(it.key(), it.value());
1245 }
1246 }
1247 }
1248 if (cnmm.isEmpty())
1249 return;
1250 static const QRegularExpression singleDigit("\\b([0-9])\\b");
1251 const QStringList keys = cnmm.uniqueKeys();
1252 for (const auto &key : keys) {
1253 const QList<CollectionNode *> values = cnmm.values(key);
1254 CollectionNode *n = nullptr;
1255 for (auto *value : values) {
1256 if (value && value->wasSeen() && value != relative) {
1257 n = value;
1258 break;
1259 }
1260 }
1261 if (n) {
1262 if (values.size() > 1) {
1263 for (CollectionNode *value : values) {
1264 if (value != n) {
1265 // Allow multiple (major) versions of QML modules
1266 if ((n->isQmlModule())
1267 && n->logicalModuleIdentifier() != value->logicalModuleIdentifier()) {
1268 if (value->wasSeen() && value != relative)
1269 cnm.insert(value->fullTitle().toLower(), value);
1270 continue;
1271 }
1272 for (Node *t : value->members())
1273 n->addMember(t);
1274 }
1275 }
1276 }
1277 QString sortKey = n->fullTitle().toLower();
1278 if (sortKey.startsWith("the "))
1279 sortKey.remove(0, 4);
1280 sortKey.replace(singleDigit, "0\\1");
1281 cnm.insert(sortKey, n);
1282 }
1283 }
1284}
1285
1286/*!
1287 Finds all the collection nodes with the same name
1288 and type as \a c and merges their members into the
1289 members list of \a c.
1290
1291 For QML modules, only nodes with matching
1292 module identifiers are merged to avoid merging
1293 modules with different (major) versions.
1294 */
1296{
1297 if (c == nullptr)
1298 return;
1299
1300 // REMARK: This form of merging is usually called during the
1301 // generation phase om-the-fly when a source-of-truth collection
1302 // is required.
1303 // In practice, this means a collection could be merged many, many
1304 // times during the lifetime of a generation.
1305 // To avoid repeating the merging process each time, which could
1306 // be time consuming, we use a small flag that is set directly on
1307 // the collection to bail-out early.
1308 //
1309 // The merging process is only meaningful for collections when the
1310 // collection references are spread troughout multiple projects.
1311 // The part of information that exists in other project is read
1312 // before the generation phase, such that when the generation
1313 // phase comes, we already have all the information we need for
1314 // merging such that we can consider all version of a certain
1315 // collection node immutable, making the caching inherently
1316 // correct at any point of the generation.
1317 //
1318 // This implies that this operation is unsafe if it is performed
1319 // before all the index files are loaded.
1320 // Indeed, this is a prerequisite, with the current structure, to
1321 // perform this optmization.
1322 //
1323 // At the current time, this is true and is expected not to
1324 // change.
1325 //
1326 // Do note that this is not applied to the other overload of
1327 // mergeCollections as we cannot as safely ensure its consistency
1328 // and, as the result of the merging depends on multiple
1329 // parameters, it would require an actual memoization of the call.
1330 //
1331 // Note that this is a defensive optimization and we are assuming
1332 // that it is effective based on heuristical data. As this is
1333 // expected to disappear, at least in its current form, in the
1334 // future, a more thorough analysis was not performed.
1335 if (c->isMerged()) {
1336 return;
1337 }
1338
1339 for (auto *tree : searchOrder()) {
1340 CollectionNode *cn = tree->getCollection(c->name(), c->nodeType());
1341 if (cn && cn != c) {
1342 if ((cn->isQmlModule())
1343 && cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
1344 continue;
1345
1346 for (auto *node : cn->members())
1347 c->addMember(node);
1348
1349 // REMARK: The merging process is performed to ensure that
1350 // references to the collection in external projects are
1351 // taken into account before consuming the collection.
1352 //
1353 // This works by having QDoc construct empty collections
1354 // as soon as a reference to a collection is encountered
1355 // and filling details later on when its definition is
1356 // found.
1357 //
1358 // This initially-empty collection is always saved to the
1359 // primaryTree and it is the collection that is directly
1360 // accessible to consumers during the generation process.
1361 //
1362 // Nonetheless, when the definition for the collection is
1363 // not in the same project as the one that is being
1364 // compiled, its details will never be filled in.
1365 //
1366 // Indeed, the details will live in the index file for the
1367 // project where the collection is defined, if any, and
1368 // the node for it, which has complete information, will
1369 // live in some non-primaryTree.
1370 //
1371 // The merging process itself is used by consumers during
1372 // the generation process because they access the
1373 // primaryTree version of the collection expecting a
1374 // source-of-truth.
1375 // To ensure that this is the case for usages that
1376 // requires linking, we need to merge not only the members
1377 // of the collection that reside in external versions of
1378 // the collection; but some of the data that reside in the
1379 // definition of the collection intself, namely the title
1380 // and the url.
1381 //
1382 // A collection that contains the data of a definition is
1383 // always marked as seen, hence we use that to discern
1384 // whether we are working with a placeholder node or not,
1385 // and fill in the data if we encounter a node that
1386 // represents a definition.
1387 //
1388 // The way in which QDoc works implies that collection are
1389 // globally scoped between projects.
1390 // The repetition of the definition for the same
1391 // collection is warned for as a duplicate documentation,
1392 // such that we can expect a single valid source of truth
1393 // for a given collection in each project.
1394 // It is currently unknown if this warning is applicable
1395 // when the repeated collection is defined in two
1396 // different projects.
1397 //
1398 // As QDoc implicitly would not correctly support this
1399 // case, we assume that only one declaration exists for
1400 // each collection, such that the first encoutered one
1401 // must be the source of truth and that there is no need
1402 // to copy any data after the first copy is performed.
1403 // KLUDGE: Note that this process is done as a hackish
1404 // solution to QTBUG-104237 and should not be considered
1405 // final or dependable.
1406 if (!c->wasSeen() && cn->wasSeen()) {
1407 c->markSeen();
1408 c->setTitle(cn->title());
1409 c->setUrl(cn->url());
1410 }
1411 }
1412 }
1413
1415}
1416
1417/*!
1418 Searches for the node that matches the path in \a atom and the
1419 specified \a genus. The \a relative node is used if the first
1420 leg of the path is empty, i.e. if the path begins with '#'.
1421 The function also sets \a ref if there remains an unused leg
1422 in the path after the node is found. The node is returned as
1423 well as the \a ref. If the returned node pointer is null,
1424 \a ref is also not valid.
1425 */
1426const Node *QDocDatabase::findNodeForAtom(const Atom *a, const Node *relative, QString &ref,
1427 Node::Genus genus)
1428{
1429 const Node *node = nullptr;
1430
1431 Atom *atom = const_cast<Atom *>(a);
1432 QStringList targetPath = atom->string().split(QLatin1Char('#'));
1433 QString first = targetPath.first().trimmed();
1434
1435 Tree *domain = nullptr;
1436
1437 if (atom->isLinkAtom()) {
1438 domain = atom->domain();
1439 genus = atom->genus();
1440 }
1441
1442 if (first.isEmpty())
1443 node = relative; // search for a target on the current page.
1444 else if (domain) {
1445 if (first.endsWith(".html"))
1446 node = domain->findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1447 else if (first.endsWith(QChar(')'))) {
1448 QString signature;
1449 QString function = first;
1450 qsizetype length = first.size();
1451 if (function.endsWith("()"))
1452 function.chop(2);
1453 if (function.endsWith(QChar(')'))) {
1454 qsizetype position = function.lastIndexOf(QChar('('));
1455 signature = function.mid(position + 1, length - position - 2);
1456 function = function.left(position);
1457 }
1458 QStringList path = function.split("::");
1459 node = domain->findFunctionNode(path, Parameters(signature), nullptr, genus);
1460 }
1461 if (node == nullptr) {
1462 int flags = SearchBaseClasses | SearchEnumValues;
1463 QStringList nodePath = first.split("::");
1464 QString target;
1465 targetPath.removeFirst();
1466 if (!targetPath.isEmpty())
1467 target = targetPath.takeFirst();
1468 if (relative && relative->tree()->physicalModuleName() != domain->physicalModuleName())
1469 relative = nullptr;
1470 return domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref);
1471 }
1472 } else {
1473 if (first.endsWith(".html"))
1474 node = findNodeByNameAndType(QStringList(first), &Node::isPageNode);
1475 else if (first.endsWith(QChar(')')))
1476 node = findFunctionNode(first, relative, genus);
1477 if (node == nullptr)
1478 return findNodeForTarget(targetPath, relative, genus, ref);
1479 }
1480
1481 if (node != nullptr && ref.isEmpty()) {
1482 if (!node->url().isEmpty())
1483 return node;
1484 targetPath.removeFirst();
1485 if (!targetPath.isEmpty()) {
1486 ref = node->root()->tree()->getRef(targetPath.first(), node);
1487 if (ref.isEmpty())
1488 node = nullptr;
1489 }
1490 }
1491 return node;
1492}
1493
1494/*!
1495 Updates navigation (previous/next page links and the navigation parent)
1496 for pages listed in the TOC, specified by the \c navigation.toctitles
1497 configuration variable.
1498
1499 if \c navigation.toctitles.inclusive is \c true, include also the TOC
1500 page(s) themselves as a 'root' item in the navigation bar (breadcrumbs)
1501 that are generated for HTML output.
1502*/
1504{
1505 // Restrict searching only to the local (primary) tree
1506 QList<Tree *> searchOrder = this->searchOrder();
1508
1509 const QString configVar = CONFIG_NAVIGATION +
1510 Config::dot +
1512
1513 // TODO: [direct-configuration-access]
1514 // The configuration is currently a singleton with some generally
1515 // global mutable state.
1516 //
1517 // Accessing the data in this form complicates testing and
1518 // requires tests that inhibit any test parallelization, as the
1519 // tests are not self contained.
1520 //
1521 // This should be generally avoived. Possibly, we should strive
1522 // for Config to be a POD type that generally is scoped to
1523 // main and whose data is destructured into dependencies when
1524 // the dependencies are constructed.
1525 bool inclusive{Config::instance().get(
1526 configVar + Config::dot + CONFIG_INCLUSIVE).asBool()};
1527
1528 // TODO: [direct-configuration-access]
1529 const auto tocTitles{Config::instance().get(configVar).asStringList()};
1530
1531 for (const auto &tocTitle : tocTitles) {
1532 if (const auto candidateTarget = findNodeForTarget(tocTitle, nullptr); candidateTarget && candidateTarget->isPageNode()) {
1533 auto tocPage{static_cast<const PageNode*>(candidateTarget)};
1534
1535 Text body = tocPage->doc().body();
1536
1537 auto *atom = body.firstAtom();
1538
1539 std::pair<PageNode *, Atom *> prev { nullptr, nullptr };
1540
1541 std::stack<const PageNode *> tocStack;
1542 tocStack.push(inclusive ? tocPage : nullptr);
1543
1544 bool inItem = false;
1545
1546 // TODO: Understand how much we use this form of looping over atoms.
1547 // If it is used a few times we might consider providing
1548 // an iterator for Text to make use of a simpler
1549 // range-for loop.
1550 while (atom) {
1551 switch (atom->type()) {
1552 case Atom::ListItemLeft:
1553 // Not known if we're going to have a link, push a temporary
1554 tocStack.push(nullptr);
1555 inItem = true;
1556 break;
1557 case Atom::ListItemRight:
1558 tocStack.pop();
1559 inItem = false;
1560 break;
1561 case Atom::Link: {
1562 if (!inItem)
1563 break;
1564
1565 // TODO: [unnecessary-output-parameter]
1566 // We currently need an lvalue string to
1567 // pass to findNodeForAtom, as the
1568 // outparameter ref.
1569 //
1570 // Apart from the general problems with output
1571 // parameters, we shouldn't be forced to
1572 // instanciate an unnecessary object at call
1573 // site.
1574 //
1575 // Understand what the correct way to avoid this is.
1576 // This requires changes to findNodeForAtom
1577 // and should be addressed in the context of
1578 // revising that method.
1579 QString unused{};
1580 // TODO: Having to const cast is really a code
1581 // smell and could result in undefined
1582 // behavior in some specific cases (e.g point
1583 // to something that is actually const).
1584 //
1585 // We should understand how to sequence the
1586 // code so that we have access to mutable data
1587 // when we need it and "freeze" the data
1588 // afterwards.
1589 //
1590 // If it we expect this form of mutability at
1591 // this point we should expose a non-const API
1592 // for the database, possibly limited to a
1593 // very specific scope of execution.
1594 //
1595 // Understand what the correct sequencing for
1596 // this processing is and revise this part.
1597 auto candidatePage = const_cast<Node *>(findNodeForAtom(atom, nullptr, unused));
1598 if (!candidatePage || !candidatePage->isPageNode()) break;
1599
1600 auto page{static_cast<PageNode*>(candidatePage)};
1601
1602 // ignore self-references
1603 if (page == prev.first) break;
1604
1605 if (prev.first) {
1606 prev.first->setLink(
1607 Node::NextLink,
1608 page->title(),
1609 // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
1610 // As with other structures in QDoc we
1611 // are able to call methods that are
1612 // valid only on very specific states.
1613 //
1614 // For some of those calls we have
1615 // some defensive programming measures
1616 // that allow us to at least identify
1617 // the error during debugging, while
1618 // for others this may currently hide
1619 // some logic error.
1620 //
1621 // To avoid those cases, we should
1622 // strive to move those cases to a
1623 // compilation error, requiring a
1624 // statically analyzable state that
1625 // represents the current model.
1626 //
1627 // This would ensure that those
1628 // lingering class of bugs are
1629 // eliminated completely, forces a
1630 // more explicit codebase where the
1631 // current capabilities do not depend
1632 // on runtime values might not be
1633 // generally visible, and does not
1634 // require us to incur into the
1635 // required state, which may be rare,
1636 // simplifying our abilities to
1637 // evaluate all possible states.
1638 //
1639 // For linking atoms, LinkAtom is
1640 // available and might be a good
1641 // enough solution to move linkText
1642 // to.
1643 atom->linkText()
1644 );
1645 page->setLink(
1646 Node::PreviousLink,
1647 prev.first->title(),
1648 // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
1649 prev.second->linkText()
1650 );
1651 }
1652
1653 if (page == tocPage)
1654 break;
1655
1656 // Find the navigation parent from the stack; we may have null pointers
1657 // for non-link list items, so skip those.
1658 qsizetype popped = 0;
1659 while (tocStack.size() > 1 && !tocStack.top()) {
1660 tocStack.pop();
1661 ++popped;
1662 }
1663
1664 page->setNavigationParent(tocStack.empty() ? nullptr : tocStack.top());
1665
1666 while (--popped > 0)
1667 tocStack.push(nullptr);
1668
1669 tocStack.push(page);
1670 prev = { page, atom };
1671 }
1672 break;
1673 default:
1674 break;
1675 }
1676
1677 atom = atom->next();
1678 }
1679 } else {
1680 Config::instance().get(configVar).location()
1681 .warning(QStringLiteral("Failed to find table of contents with title '%1'")
1682 .arg(tocTitle));
1683 }
1684 }
1685
1686 // Restore search order
1687 setSearchOrder(searchOrder);
1688}
1689
1690QT_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:391
#define CONFIG_NOLINKERRORS
Definition config.h:369
#define CONFIG_NAVIGATION
Definition config.h:368
#define CONFIG_INCLUSIVE
Definition config.h:357
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:30
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