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