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
cppcodeparser.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
5
6#include "access.h"
7#include "qmlenumnode.h"
8#include "classnode.h"
10#include "collectionnode.h"
12#include "config.h"
13#include "examplenode.h"
15#include "functionnode.h"
16#include "generator.h"
17#include "genustypes.h"
18#include "headernode.h"
21#include "namespacenode.h"
22#include "qdocdatabase.h"
23#include "qmltypenode.h"
27#include "utilities.h"
28
29#include <QtCore/qdebug.h>
30#include <QtCore/qmap.h>
31
32#include <algorithm>
33
34using namespace Qt::Literals::StringLiterals;
35
36QT_BEGIN_NAMESPACE
37
38/*
39 All these can appear in a C++ namespace. Don't add
40 anything that can't be in a C++ namespace.
41 */
42static const QMap<QString, NodeType> s_nodeTypeMap{
43 { COMMAND_NAMESPACE, NodeType::Namespace }, { COMMAND_NAMESPACE, NodeType::Namespace },
44 { COMMAND_CLASS, NodeType::Class }, { COMMAND_STRUCT, NodeType::Struct },
45 { COMMAND_UNION, NodeType::Union }, { COMMAND_ENUM, NodeType::Enum },
46 { COMMAND_TYPEALIAS, NodeType::TypeAlias }, { COMMAND_TYPEDEF, NodeType::Typedef },
47 { COMMAND_PROPERTY, NodeType::Property }, { COMMAND_VARIABLE, NodeType::Variable }
48};
49
50typedef bool (Node::*NodeTypeTestFunc)() const;
58
61{
62 Config &config = Config::instance();
63 QStringList exampleFilePatterns{config.get(CONFIG_EXAMPLES
65 + CONFIG_FILEEXTENSIONS).asStringList()};
66
67 if (!exampleFilePatterns.isEmpty())
68 m_exampleNameFilter = exampleFilePatterns.join(' ');
69 else
70 m_exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
71
72 QStringList exampleImagePatterns{config.get(CONFIG_EXAMPLES
74 + CONFIG_IMAGEEXTENSIONS).asStringList()};
75
76 if (!exampleImagePatterns.isEmpty())
77 m_exampleImageFilter = exampleImagePatterns.join(' ');
78 else
79 m_exampleImageFilter = "*.png";
80
81 m_showLinkErrors = !config.get(CONFIG_NOLINKERRORS).asBool();
82}
83
84/*!
85 Process the topic \a command found in the \a doc with argument \a arg.
86 */
87Node *CppCodeParser::processTopicCommand(const Doc &doc, const QString &command,
88 const ArgPair &arg)
89{
91
92 if (command == COMMAND_FN) {
93 Q_UNREACHABLE();
94 } else if (s_nodeTypeMap.contains(command)) {
95 /*
96 We should only get in here if the command refers to
97 something that can appear in a C++ namespace,
98 i.e. a class, another namespace, an enum, a typedef,
99 a property or a variable. I think these are handled
100 this way to allow the writer to refer to the entity
101 without including the namespace qualifier.
102 */
103 NodeType type = s_nodeTypeMap[command];
104 QStringList words = arg.first.split(QLatin1Char(' '));
105 QStringList path;
106 qsizetype idx = 0;
107 Node *node = nullptr;
108
109 if (type == NodeType::Variable && words.size() > 1)
110 idx = words.size() - 1;
111 path = words[idx].split("::");
112
113 node = database->findNodeByNameAndType(path, s_nodeTypeTestFuncMap[command]);
114 // Allow representing a type alias as a class
115 if (node == nullptr && command == COMMAND_CLASS) {
116 node = database->findNodeByNameAndType(path, &Node::isTypeAlias);
117 if (node) {
118 const auto &access = node->access();
119 const auto &loc = node->location();
120 const auto &templateDecl = node->templateDecl();
121 node = new ClassNode(NodeType::Class, node->parent(), node->name());
122 node->setAccess(access);
123 node->setLocation(loc);
124 node->setTemplateDecl(templateDecl);
125 }
126 }
127 if (node == nullptr) {
129 doc.location().warning(
130 QStringLiteral("Cannot find '%1' specified with '\\%2' in any header file")
131 .arg(arg.first, command));
132 }
133 } else if (node->isAggregate()) {
134 if (type == NodeType::Namespace) {
135 auto *ns = static_cast<NamespaceNode *>(node);
136 ns->markSeen();
137 ns->setWhereDocumented(ns->tree()->camelCaseModuleName());
138 }
139 }
140 return node;
141 } else if (command == COMMAND_EXAMPLE) {
143 auto *en = new ExampleNode(database->primaryTreeRoot(), arg.first);
144 en->setLocation(doc.startLocation());
145 setExampleFileLists(en);
146 return en;
147 }
148 } else if (command == COMMAND_EXTERNALPAGE) {
149 auto *epn = new ExternalPageNode(database->primaryTreeRoot(), arg.first);
150 epn->setLocation(doc.startLocation());
151 return epn;
152 } else if (command == COMMAND_HEADERFILE) {
153 auto *hn = new HeaderNode(database->primaryTreeRoot(), arg.first);
154 hn->setLocation(doc.startLocation());
155 return hn;
156 } else if (command == COMMAND_GROUP) {
157 CollectionNode *cn = database->addGroup(arg.first);
159 cn->markSeen();
160 return cn;
161 } else if (command == COMMAND_MODULE) {
162 CollectionNode *cn = database->addModule(arg.first);
164 cn->markSeen();
165 return cn;
166 } else if (command == COMMAND_QMLMODULE) {
167 QStringList blankSplit = arg.first.split(QLatin1Char(' '));
168 CollectionNode *cn = database->addQmlModule(blankSplit[0]);
169 cn->setLogicalModuleInfo(blankSplit);
171 cn->markSeen();
172 return cn;
173 } else if (command == COMMAND_PAGE) {
174 auto *pn = new PageNode(database->primaryTreeRoot(), arg.first.split(' ').front());
175 pn->setLocation(doc.startLocation());
176 return pn;
177 } else if (command == COMMAND_QMLTYPE ||
178 command == COMMAND_QMLSINGLETONTYPE ||
179 command == COMMAND_QMLVALUETYPE ||
180 command == COMMAND_QMLBASICTYPE) {
181 auto nodeType = (command == COMMAND_QMLTYPE || command == COMMAND_QMLSINGLETONTYPE) ? NodeType::QmlType : NodeType::QmlValueType;
182 QString qmid;
183 if (auto args = doc.metaCommandArgs(COMMAND_INQMLMODULE); !args.isEmpty())
184 qmid = args.first().first;
185 auto *qcn = database->findQmlTypeInPrimaryTree(qmid, arg.first);
186 // A \qmlproperty may have already constructed a placeholder type
187 // without providing a module identifier; allow such cases
188 if (!qcn && !qmid.isEmpty()) {
189 qcn = database->findQmlTypeInPrimaryTree(QString(), arg.first);
190 if (qcn && !qcn->logicalModuleName().isEmpty())
191 qcn = nullptr;
192 }
193 if (!qcn || qcn->nodeType() != nodeType)
194 qcn = new QmlTypeNode(database->primaryTreeRoot(), arg.first, nodeType);
195 if (!qmid.isEmpty())
196 database->addToQmlModule(qmid, qcn);
197 qcn->setLocation(doc.startLocation());
198 if (command == COMMAND_QMLSINGLETONTYPE)
199 qcn->setSingleton(true);
200 if (command == COMMAND_QMLTYPE && !qcn->isSingleton()) {
201 auto classNode = database->findClassNode(arg.first.split(u"::"_s));
202 if (classNode && classNode->isQmlSingleton())
203 qcn->setSingleton(true);
204 }
205 return qcn;
206 } else if (command == COMMAND_QMLENUM) {
207 return processQmlEnumTopic(doc.enumItemNames(), doc.location(), arg.first);
208 } else if ((command == COMMAND_QMLSIGNAL) || (command == COMMAND_QMLMETHOD)
209 || (command == COMMAND_QMLATTACHEDSIGNAL) || (command == COMMAND_QMLATTACHEDMETHOD)) {
210 Q_UNREACHABLE();
211 }
212 return nullptr;
213}
214
215/*!
216 Finds a QmlTypeNode \a name, under the specific \a moduleName, from the primary tree.
217 If one is not found, creates one.
218
219 Returns the found or created node.
220*/
221QmlTypeNode *findOrCreateQmlType(const QString &moduleName, const QString &name, const Location &location)
222{
224 auto *aggregate = database->findQmlTypeInPrimaryTree(moduleName, name);
225 // Note: Constructing a QmlType node by default, as opposed to QmlValueType.
226 // This may lead to unexpected behavior if documenting \qmlvaluetype's members
227 // before the type itself.
228 if (!aggregate) {
229 aggregate = new QmlTypeNode(database->primaryTreeRoot(), name, NodeType::QmlType);
230 aggregate->setLocation(location);
231 if (!moduleName.isEmpty())
232 database->addToQmlModule(moduleName, aggregate);
233 }
234 return aggregate;
235}
236
238{
239 const Doc &doc = untied.documentation;
240 const TopicList &topics = doc.topicsUsed();
241 if (topics.isEmpty())
242 return {};
243
244 std::vector<TiedDocumentation> tied{};
245
246 auto firstTopicArgs =
247 QmlPropertyArguments::parse(topics.at(0).m_args, doc.location(),
249 if (!firstTopicArgs)
250 return {};
251
252 NodeList sharedNodes;
253 auto *qmlType = findOrCreateQmlType((*firstTopicArgs).m_module, (*firstTopicArgs).m_qmltype, doc.startLocation());
254
255 for (const auto &topicCommand : topics) {
256 QString cmd = topicCommand.m_topic;
257 if ((cmd == COMMAND_QMLPROPERTY) || (cmd == COMMAND_QMLATTACHEDPROPERTY)) {
258 bool attached = cmd.contains(QLatin1String("attached"));
259 if (auto qpa = QmlPropertyArguments::parse(topicCommand.m_args, doc.location(),
260 QmlPropertyArguments::ParsingOptions::RequireQualifiedPath)) {
261 if (qmlType != QDocDatabase::qdocDB()->findQmlTypeInPrimaryTree(qpa->m_module, qpa->m_qmltype)) {
262 doc.startLocation().warning(
263 QStringLiteral(
264 "All properties in a group must belong to the same type: '%1'")
265 .arg(topicCommand.m_args));
266 continue;
267 }
268 QmlPropertyNode *existingProperty = qmlType->hasQmlProperty(qpa->m_name, attached);
269 if (existingProperty) {
270 processMetaCommands(doc, existingProperty);
271 if (!doc.body().isEmpty()) {
272 doc.startLocation().warning(
273 QStringLiteral("QML property documented multiple times: '%1'")
274 .arg(topicCommand.m_args), QStringLiteral("also seen here: %1")
275 .arg(existingProperty->location().toString()));
276 }
277 continue;
278 }
279 auto *qpn = new QmlPropertyNode(qmlType, qpa->m_name, qpa->m_type, attached);
280 qpn->setIsList(qpa->m_isList);
281 qpn->setLocation(doc.startLocation());
282 qpn->setGenus(Genus::QML);
283
284 tied.emplace_back(TiedDocumentation{doc, qpn});
285
286 sharedNodes << qpn;
287 }
288 } else {
289 doc.startLocation().warning(
290 QStringLiteral("Command '\\%1'; not allowed with QML property commands")
291 .arg(cmd));
292 }
293 }
294
295 // Construct a SharedCommentNode (scn) if multiple topics generated
296 // valid nodes. Note that it's important to do this *after* constructing
297 // the topic nodes - which need to be written to index before the related
298 // scn.
299 if (sharedNodes.size() > 1) {
300 // Resolve QML property group identifier (if any) from the first topic
301 // command arguments.
302 QString group;
303 if (auto dot = (*firstTopicArgs).m_name.indexOf('.'_L1); dot != -1)
304 group = (*firstTopicArgs).m_name.left(dot);
305 auto *scn = new SharedCommentNode(qmlType, sharedNodes.size(), group);
306 scn->setLocation(doc.startLocation());
307
308 tied.emplace_back(TiedDocumentation{doc, scn});
309
310 for (const auto n : sharedNodes)
311 scn->append(n);
312 scn->sort();
313 }
314
315 return tied;
316}
317
318/*!
319 Process the metacommand \a command in the context of the
320 \a node associated with the topic command and the \a doc.
321 \a arg is the argument to the metacommand.
322
323 \a node is guaranteed to be non-null.
324 */
325void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
326 const ArgPair &argPair, Node *node)
327{
329
330 QString arg = argPair.first;
331 if (command == COMMAND_INHEADERFILE) {
332 // TODO: [incorrect-constructs][header-arg]
333 // The emptiness check for arg is required as,
334 // currently, DocParser fancies passing (without any warning)
335 // incorrect constructs doen the chain, such as an
336 // "\inheaderfile" command with no argument.
337 //
338 // As it is the case here, we require further sanity checks to
339 // preserve some of the semantic for the later phases.
340 // This generally has a ripple effect on the whole codebase,
341 // making it more complex and increasesing the surface of bugs.
342 //
343 // The following emptiness check should be removed as soon as
344 // DocParser is enhanced with correct semantics.
345 if (node->isAggregate() && !arg.isEmpty())
346 static_cast<Aggregate *>(node)->setIncludeFile(arg);
347 else
348 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_INHEADERFILE));
349 } else if (command == COMMAND_COMPARES) {
350 processComparesCommand(node, arg, doc.location());
351 } else if (command == COMMAND_COMPARESWITH) {
352 if (!node->isClassNode())
353 doc.location().warning(
354 u"Found \\%1 command outside of \\%2 context."_s
356 } else if (command == COMMAND_OVERLOAD) {
357 /*
358 Note that this might set the overload flag of the
359 primary function. This is ok because the overload
360 flags and overload numbers will be resolved later
361 in Aggregate::normalizeOverloads().
362 */
363 processOverloadCommand(node, doc);
364 } else if (command == COMMAND_REIMP) {
365 if (node->parent() && !node->parent()->isInternal()) {
366 if (node->isFunction()) {
367 auto *fn = static_cast<FunctionNode *>(node);
368 // The clang visitor class will have set the
369 // qualified name of the overridden function.
370 // If the name of the overridden function isn't
371 // set, issue a warning.
372 if (fn->overridesThis().isEmpty() && CodeParser::isWorthWarningAbout(doc)) {
373 doc.location().warning(
374 QStringLiteral("Cannot find base function for '\\%1' in %2()")
375 .arg(COMMAND_REIMP, node->name()),
376 QStringLiteral("The function either doesn't exist in any "
377 "base class with the same signature or it "
378 "exists but isn't virtual."));
379 }
380 fn->setReimpFlag();
381 } else {
382 doc.location().warning(
383 QStringLiteral("Ignored '\\%1' in %2").arg(COMMAND_REIMP, node->name()));
384 }
385 }
386 } else if (command == COMMAND_RELATES) {
387 // REMARK: Generates warnings only; Node instances are
388 // adopted from the root namespace to other Aggregates
389 // in a post-processing step, Aggregate::resolveRelates(),
390 // after all topic commands are processed.
391 if (node->isAggregate()) {
392 doc.location().warning("Invalid '\\%1' not allowed in '\\%2'"_L1
393 .arg(COMMAND_RELATES, node->nodeTypeString()));
394 }
395 } else if (command == COMMAND_NEXTPAGE) {
396 CodeParser::setLink(node, Node::NextLink, arg);
397 } else if (command == COMMAND_PREVIOUSPAGE) {
398 CodeParser::setLink(node, Node::PreviousLink, arg);
399 } else if (command == COMMAND_STARTPAGE) {
400 CodeParser::setLink(node, Node::StartLink, arg);
401 } else if (command == COMMAND_QMLINHERITS) {
402 if (node->name() == arg)
403 doc.location().warning(QStringLiteral("%1 tries to inherit itself").arg(arg));
404 else if (node->isQmlType()) {
405 auto *qmlType = static_cast<QmlTypeNode *>(node);
406 qmlType->setQmlBaseName(arg);
407 }
408 } else if (command == COMMAND_QMLNATIVETYPE || command == COMMAND_QMLINSTANTIATES) {
409 if (command == COMMAND_QMLINSTANTIATES)
410 doc.location().report(
411 u"\\instantiates is deprecated and will be removed in a future version. Use \\nativetype instead."_s);
412 // TODO: COMMAND_QMLINSTANTIATES is deprecated since 6.8. Its remains should be removed no later than Qt 7.0.0.
413 processQmlNativeTypeCommand(node, command, arg, doc.location());
414 } else if (command == COMMAND_DEFAULT) {
415 if (!node->isQmlProperty()) {
416 doc.location().warning(QStringLiteral("Ignored '\\%1', applies only to '\\%2'")
417 .arg(command, COMMAND_QMLPROPERTY));
418 } else if (arg.isEmpty()) {
419 doc.location().warning(QStringLiteral("Expected an argument for '\\%1' (maybe you meant '\\%2'?)")
420 .arg(command, COMMAND_QMLDEFAULT));
421 } else {
422 static_cast<QmlPropertyNode *>(node)->setDefaultValue(arg);
423 }
424 } else if (command == COMMAND_QMLDEFAULT) {
425 node->markDefault();
426 } else if (command == COMMAND_QMLENUMERATORSFROM) {
427 NativeEnum *nativeEnum{nullptr};
428 if (auto *ne_if = dynamic_cast<NativeEnumInterface *>(node))
429 nativeEnum = ne_if->nativeEnum();
430 else {
431 doc.location().warning("Ignored '\\%1', applies only to '\\%2' and '\\%3'"_L1
432 .arg(command, COMMAND_QMLPROPERTY, COMMAND_QMLENUM));
433 return;
434 }
435 if (!nativeEnum->resolve(argPair.first, argPair.second)) {
436 doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
437 .arg(command, arg), "Use \\value commands instead"_L1);
438 }
439 } else if (command == COMMAND_QMLREADONLY) {
440 node->markReadOnly(true);
441 } else if (command == COMMAND_QMLREQUIRED) {
442 if (!node->isQmlProperty())
443 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_QMLREQUIRED));
444 else
445 static_cast<QmlPropertyNode *>(node)->setRequired();
446 } else if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
447 if (node->isQmlType())
448 node->setAbstract(true);
449 } else if (command == COMMAND_DEPRECATED) {
450 node->setDeprecated(argPair.second);
451 } else if (command == COMMAND_INGROUP || command == COMMAND_INPUBLICGROUP) {
452 // Note: \ingroup and \inpublicgroup are the same (and now recognized as such).
453 database->addToGroup(arg, node);
454 } else if (command == COMMAND_INMODULE) {
455 database->addToModule(arg, node);
456 } else if (command == COMMAND_INQMLMODULE) {
457 // Handled when parsing topic commands
458 } else if (command == COMMAND_OBSOLETE) {
460 } else if (command == COMMAND_NONREENTRANT) {
462 } else if (command == COMMAND_PRELIMINARY) {
463 // \internal wins.
464 if (!node->isInternal())
466 } else if (command == COMMAND_INTERNAL) {
468 } else if (command == COMMAND_REENTRANT) {
470 } else if (command == COMMAND_SINCE) {
471 node->setSince(arg);
472 } else if (command == COMMAND_WRAPPER) {
473 node->setWrapper();
474 } else if (command == COMMAND_THREADSAFE) {
476 } else if (command == COMMAND_SUBTITLE) {
477 if (!node->setSubtitle(arg))
478 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
479 } else if (command == COMMAND_QTVARIABLE) {
480 node->setQtVariable(arg);
481 if (!node->isModule() && !node->isQmlModule())
482 doc.location().warning(
483 QStringLiteral(
484 "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
485 .arg(COMMAND_QTVARIABLE));
486 } else if (command == COMMAND_QTCMAKEPACKAGE) {
487 if (node->isModule())
488 node->setCMakeComponent(arg);
489 else
490 doc.location().warning(
491 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
493 } else if (command == COMMAND_QTCMAKETARGETITEM) {
494 if (node->isModule())
495 node->setCMakeTargetItem(QLatin1String("Qt6::") + arg);
496 else
497 doc.location().warning(
498 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
500 } else if (command == COMMAND_CMAKEPACKAGE) {
501 if (node->isModule())
502 node->setCMakePackage(arg);
503 else
504 doc.location().warning(
505 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
507 } else if (command == COMMAND_CMAKECOMPONENT) {
508 if (node->isModule())
509 node->setCMakeComponent(arg);
510 else
511 doc.location().warning(
512 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
514 } else if (command == COMMAND_CMAKETARGETITEM) {
515 if (node->isModule())
516 node->setCMakeTargetItem(arg);
517 else
518 doc.location().warning(
519 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
521 } else if (command == COMMAND_MODULESTATE) {
522 if (!node->isModule() && !node->isQmlModule()) {
523 doc.location().warning(
524 QStringLiteral(
525 "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
526 .arg(COMMAND_MODULESTATE));
527 } else if (!node->isPreliminary() && !node->isInternal()) {
528 static_cast<CollectionNode*>(node)->setState(arg);
529 }
530 } else if (command == COMMAND_NOAUTOLIST) {
531 if (!node->isCollectionNode() && !node->isExample()) {
532 doc.location().warning(
533 QStringLiteral(
534 "Command '\\%1' is only meaningful in '\\module', '\\qmlmodule', `\\group` and `\\example`.")
535 .arg(COMMAND_NOAUTOLIST));
536 } else {
537 static_cast<PageNode*>(node)->setNoAutoList(true);
538 }
539 } else if (command == COMMAND_ATTRIBUTION) {
540 // TODO: This condition is not currently exact enough, as it
541 // will allow any non-aggregate `PageNode` to use the command,
542 // For example, an `ExampleNode`.
543 //
544 // The command is intended only for internal usage by
545 // "qattributionscanner" and should only work on `PageNode`s
546 // that are generated from a "\page" command.
547 //
548 // It is already possible to provide a more restricted check,
549 // albeit in a somewhat dirty way. It is not expected that
550 // this warning will have any particular use.
551 // If it so happens that a case where the too-broad scope of
552 // the warning is a problem or hides a bug, modify the
553 // condition to be restrictive enough.
554 // Otherwise, wait until a more torough look at QDoc's
555 // internal representations an way to enable "Attribution
556 // Pages" is performed before looking at the issue again.
557 if (!node->isTextPageNode()) {
558 doc.location().warning(u"Command '\\%1' is only meaningful in '\\%2'"_s.arg(COMMAND_ATTRIBUTION, COMMAND_PAGE));
559 } else { static_cast<PageNode*>(node)->markAttribution(); }
560 }
561}
562
563/*!
564 \internal
565 Processes the argument \a arg that's passed to the \\compares command,
566 and sets the comparison category of the \a node accordingly.
567
568 If the argument is invalid, issue a warning at the location the command
569 appears through \a loc.
570*/
571void CppCodeParser::processComparesCommand(Node *node, const QString &arg, const Location &loc)
572{
573 if (!node->isClassNode()) {
574 loc.warning(u"Found \\%1 command outside of \\%2 context."_s.arg(COMMAND_COMPARES,
576 return;
577 }
578
579 if (auto category = comparisonCategoryFromString(arg.toStdString());
580 category != ComparisonCategory::None) {
581 node->setComparisonCategory(category);
582 } else {
583 loc.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(COMMAND_COMPARES, arg),
584 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
585 }
586}
587
588/*!
589 Processes the \\overload command for the given \a node and \a doc.
590 Handles both regular overloads and primary overloads (\\overload primary).
591 Issues warnings for multiple primary overloads with location references.
592*/
593void CppCodeParser::processOverloadCommand(Node *node, const Doc &doc)
594{
595 if (node->isFunction()) {
596 auto *fn = static_cast<FunctionNode *>(node);
597
598 // If this function is part of a SharedCommentNode, skip processing here.
599 // The SharedCommentNode will handle \overload primary position-dependently.
600 if (fn->sharedCommentNode())
601 return;
602
603 // Check if this is "\overload primary"
604 const auto &overloadArgs = doc.overloadList();
605 if (!overloadArgs.isEmpty()
606 && overloadArgs.first().first == "__qdoc_primary_overload__"_L1) {
607
608 // Note: We don't check for duplicate primary overloads here because
609 // Doc locations may not be fully initialized yet, especially for
610 // shared comment nodes with multiple \fn commands.
611 // The check is done later in Aggregate::normalizeOverloads().
612
613 fn->setPrimaryOverloadFlag();
614 // Primary overloads are still overloads, so set both flags
615 fn->setOverloadFlag();
616 } else {
617 fn->setOverloadFlag();
618 }
619 } else if (node->isSharedCommentNode()) {
620 static_cast<SharedCommentNode *>(node)->setOverloadFlags();
621 } else {
622 doc.location().warning("Ignored '\\%1'"_L1.arg(COMMAND_OVERLOAD));
623 }
624}
625
626/*!
627 The topic command has been processed, and now \a doc and
628 \a node are passed to this function to get the metacommands
629 from \a doc and process them one at a time. \a node is the
630 node where \a doc resides.
631 */
633{
634 std::vector<Node*> nodes_to_process{};
635 if (node->isSharedCommentNode()) {
636 auto scn = static_cast<SharedCommentNode*>(node);
637
638 nodes_to_process.reserve(scn->count() + 1);
639 std::copy(scn->collective().cbegin(), scn->collective().cend(), std::back_inserter(nodes_to_process));
640 }
641
642 // REMARK: Ordering is important here. If node is a
643 // SharedCommentNode it MUST be processed after all its child
644 // nodes.
645 // Failure to do so can incur in incorrect warnings.
646 // For example, if a shared documentation has a "\relates" command.
647 // When the command is processed for the SharedCommentNode it will
648 // apply to all its child nodes.
649 // If a child node is processed after the SharedCommentNode that
650 // contains it, that "\relates" command will be considered applied
651 // already, resulting in a warning.
652 nodes_to_process.push_back(node);
653
654 const QStringList metaCommandsUsed = doc.metaCommandsUsed().values();
655 for (const auto &command : metaCommandsUsed) {
656 const ArgList args = doc.metaCommandArgs(command);
657 for (const auto &arg : args) {
658 std::for_each(nodes_to_process.cbegin(), nodes_to_process.cend(), [this, doc, command, arg](auto node){
659 processMetaCommand(doc, command, arg, node);
660 });
661 }
662 }
663
664 // Apply a title (stripped of formatting) to the Node if set
665 if (!doc.title().isEmpty()) {
666 if (!node->setTitle(doc.title().toString()))
667 doc.location().warning(QStringLiteral("Ignored '\\title'"));
668 if (node->isExample())
669 QDocDatabase::qdocDB()->addExampleNode(static_cast<ExampleNode *>(node));
670 }
671}
672
673/*!
674 Creates an EnumNode instance explicitly for the \qmlenum command.
675 Utilizes QmlPropertyArguments for argument (\a arg) parsing.
676
677 Adds a list of \a enumItemNames as enumerators to facilitate linking
678 via enumerator names.
679*/
680EnumNode *CppCodeParser::processQmlEnumTopic(const QStringList &enumItemNames,
681 const Location &location, const QString &arg)
682{
683 if (arg.isEmpty()) {
684 location.warning(u"Missing argument to \\%1 command."_s.arg(COMMAND_QMLENUM));
685 return nullptr;
686 }
687
688 auto parsedArgs = QmlPropertyArguments::parse(arg, location,
691
692 if (!parsedArgs)
693 return nullptr;
694
695 auto *qmlType = findOrCreateQmlType((*parsedArgs).m_module, (*parsedArgs).m_qmltype, location);
696
697 auto *enumNode = new QmlEnumNode(qmlType, (*parsedArgs).m_name);
698 enumNode->setLocation(location);
699
700 for (const auto &item : enumItemNames)
701 enumNode->addItem(EnumItem(item, 0));
702
703 return enumNode;
704}
705
706/*!
707 Parse QML signal/method topic commands.
708 */
709FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Location &location,
710 const QString &funcArg)
711{
712 QString funcName;
713 QString returnType;
714
715 qsizetype leftParen = funcArg.indexOf(QChar('('));
716 if (leftParen > 0)
717 funcName = funcArg.left(leftParen);
718 else
719 funcName = funcArg;
720 qsizetype firstBlank = funcName.indexOf(QChar(' '));
721 if (firstBlank > 0) {
722 returnType = funcName.left(firstBlank);
723 funcName = funcName.right(funcName.size() - firstBlank - 1);
724 }
725
726 QStringList colonSplit(funcName.split("::"));
727 if (colonSplit.size() < 2) {
728 QString msg = "Unrecognizable QML module/component qualifier for " + funcArg;
729 location.warning(msg.toLatin1().data());
730 return nullptr;
731 }
732 QString moduleName;
733 QString elementName;
734 if (colonSplit.size() > 2) {
735 moduleName = colonSplit[0];
736 elementName = colonSplit[1];
737 } else {
738 elementName = colonSplit[0];
739 }
740 funcName = colonSplit.last();
741
742 auto *aggregate = findOrCreateQmlType(moduleName, elementName, location);
743
744 QString params;
745 QStringList leftParenSplit = funcArg.split('(');
746 if (leftParenSplit.size() > 1) {
747 QStringList rightParenSplit = leftParenSplit[1].split(')');
748 if (!rightParenSplit.empty())
749 params = rightParenSplit[0];
750 }
751
752 FunctionNode::Metaness metaness = FunctionNode::getMetanessFromTopic(topic);
753 bool attached = topic.contains(QLatin1String("attached"));
754 auto *fn = new FunctionNode(metaness, aggregate, funcName, attached);
755 fn->setAccess(Access::Public);
756 fn->setLocation(location);
757 fn->setReturnType(returnType);
758 fn->setParameters(params);
759 return fn;
760}
761
762/*!
763 Parse the macro arguments in \a macroArg ad hoc, without using
764 any actual parser. If successful, return a pointer to the new
765 FunctionNode for the macro. Otherwise return null. \a location
766 is used for reporting errors.
767 */
768FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QString &macroArg)
769{
771
772 QStringList leftParenSplit = macroArg.split('(');
773 if (leftParenSplit.isEmpty())
774 return nullptr;
775 QString macroName;
776 FunctionNode *oldMacroNode = nullptr;
777 QStringList blankSplit = leftParenSplit[0].split(' ');
778 if (!blankSplit.empty()) {
779 macroName = blankSplit.last();
780 oldMacroNode = database->findMacroNode(macroName);
781 }
782 QString returnType;
783 if (blankSplit.size() > 1) {
784 blankSplit.removeLast();
785 returnType = blankSplit.join(' ');
786 }
787 QString params;
788 if (leftParenSplit.size() > 1) {
789 params = QString("");
790 const QString &afterParen = leftParenSplit.at(1);
791 qsizetype rightParen = afterParen.indexOf(')');
792 if (rightParen >= 0)
793 params = afterParen.left(rightParen);
794 }
795 int i = 0;
796 while (i < macroName.size() && !macroName.at(i).isLetter())
797 i++;
798 if (i > 0) {
799 returnType += QChar(' ') + macroName.left(i);
800 macroName = macroName.mid(i);
801 }
803 if (params.isNull())
805 auto *macro = new FunctionNode(metaness, database->primaryTreeRoot(), macroName);
806 macro->setAccess(Access::Public);
807 macro->setLocation(location);
808 macro->setReturnType(returnType);
809 macro->setParameters(params);
810 if (oldMacroNode && macro->parent() == oldMacroNode->parent()
811 && compare(macro, oldMacroNode) == 0) {
812 location.warning(QStringLiteral("\\macro %1 documented more than once")
813 .arg(macroArg), QStringLiteral("also seen here: %1")
814 .arg(oldMacroNode->doc().location().toString()));
815 }
816 return macro;
817}
818
819void CppCodeParser::setExampleFileLists(ExampleNode *en)
820{
821 Config &config = Config::instance();
822 QString fullPath = config.getExampleProjectFile(en->name());
823 if (fullPath.isEmpty()) {
824 QString details = QLatin1String("Example directories: ")
825 + config.getCanonicalPathList(CONFIG_EXAMPLEDIRS).join(QLatin1Char(' '));
826 en->location().warning(
827 QStringLiteral("Cannot find project file for example '%1'").arg(en->name()),
828 details);
829 return;
830 }
831
832 QDir exampleDir(QFileInfo(fullPath).dir());
833
834 const auto& [excludeDirs, excludeFiles] = config.getExcludedPaths();
835
836 QStringList exampleFiles = Config::getFilesHere(exampleDir.path(), m_exampleNameFilter,
837 Location(), excludeDirs, excludeFiles);
838 // Search for all image files under the example project, excluding doc/images directory.
839 QSet<QString> excludeDocDirs(excludeDirs);
840 excludeDocDirs.insert(exampleDir.path() + QLatin1String("/doc/images"));
841 QStringList imageFiles = Config::getFilesHere(exampleDir.path(), m_exampleImageFilter,
842 Location(), excludeDocDirs, excludeFiles);
843 if (!exampleFiles.isEmpty()) {
844 // move main.cpp to the end, if it exists
845 QString mainCpp;
846
847 const auto isGeneratedOrMainCpp = [&mainCpp](const QString &fileName) {
848 if (fileName.endsWith("/main.cpp")) {
849 if (mainCpp.isEmpty())
850 mainCpp = fileName;
851 return true;
852 }
853 return Utilities::isGeneratedFile(fileName);
854 };
855
856 exampleFiles.erase(
857 std::remove_if(exampleFiles.begin(), exampleFiles.end(), isGeneratedOrMainCpp),
858 exampleFiles.end());
859
860 if (!mainCpp.isEmpty())
861 exampleFiles.append(mainCpp);
862
863 // Add any resource and project files
864 exampleFiles += Config::getFilesHere(exampleDir.path(),
865 QLatin1String("*.qrc *.pro *.qmlproject *.pyproject CMakeLists.txt qmldir"),
866 Location(), excludeDirs, excludeFiles);
867 }
868
869 const qsizetype pathLen = exampleDir.path().size() - en->name().size();
870 for (auto &file : exampleFiles)
871 file = file.mid(pathLen);
872 for (auto &file : imageFiles)
873 file = file.mid(pathLen);
874
875 en->setFiles(exampleFiles, fullPath.mid(pathLen));
876 en->setImages(imageFiles);
877}
878
879/*!
880 returns true if \a t is \e {qmlsignal}, \e {qmlmethod},
881 \e {qmlattachedsignal}, or \e {qmlattachedmethod}.
882 */
883bool CppCodeParser::isQMLMethodTopic(const QString &t)
884{
887}
888
889/*!
890 Returns true if \a t is \e {qmlproperty}, \e {qmlpropertygroup},
891 or \e {qmlattachedproperty}.
892 */
893bool CppCodeParser::isQMLPropertyTopic(const QString &t)
894{
896}
897
898std::pair<std::vector<TiedDocumentation>, std::vector<FnMatchError>>
899CppCodeParser::processTopicArgs(const UntiedDocumentation &untied)
900{
901 const Doc &doc = untied.documentation;
902
903 if (doc.topicsUsed().isEmpty())
904 return {};
905
906 QDocDatabase *database = QDocDatabase::qdocDB();
907
908 const QString topic = doc.topicsUsed().first().m_topic;
909
910 std::vector<TiedDocumentation> tied{};
911 std::vector<FnMatchError> errors{};
912
913 if (isQMLPropertyTopic(topic)) {
914 auto tied_qml = processQmlProperties(untied);
915 tied.insert(tied.end(), tied_qml.begin(), tied_qml.end());
916 } else {
917 ArgList args = doc.metaCommandArgs(topic);
918 Node *node = nullptr;
919 if (args.size() == 1) {
920 if (topic == COMMAND_FN) {
921 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
922 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
923 auto result = fn_parser(doc.location(), args[0].first, args[0].second, untied.context);
924 if (auto *error = std::get_if<FnMatchError>(&result))
925 errors.emplace_back(*error);
926 else
927 node = std::get<Node*>(result);
928 }
929 } else if (topic == COMMAND_MACRO) {
930 node = parseMacroArg(doc.location(), args[0].first);
931 } else if (isQMLMethodTopic(topic)) {
932 node = parseOtherFuncArg(topic, doc.location(), args[0].first);
933 } else if (topic == COMMAND_DONTDOCUMENT) {
934 database->primaryTree()->addToDontDocumentMap(args[0].first);
935 } else {
936 node = processTopicCommand(doc, topic, args[0]);
937 }
938 if (node != nullptr) {
939 tied.emplace_back(TiedDocumentation{doc, node});
940 }
941 } else if (args.size() > 1) {
942 QList<SharedCommentNode *> sharedCommentNodes;
943 for (const auto &arg : std::as_const(args)) {
944 node = nullptr;
945 if (topic == COMMAND_FN) {
946 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
947 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
948 auto result = fn_parser(doc.location(), arg.first, arg.second, untied.context);
949 if (auto *error = std::get_if<FnMatchError>(&result))
950 errors.emplace_back(*error);
951 else
952 node = std::get<Node*>(result);
953 }
954 } else if (topic == COMMAND_MACRO) {
955 node = parseMacroArg(doc.location(), arg.first);
956 } else if (isQMLMethodTopic(topic)) {
957 node = parseOtherFuncArg(topic, doc.location(), arg.first);
958 } else {
959 node = processTopicCommand(doc, topic, arg);
960 }
961 if (node != nullptr) {
962 bool found = false;
963 for (SharedCommentNode *scn : sharedCommentNodes) {
964 if (scn->parent() == node->parent()) {
965 scn->append(node);
966 found = true;
967 break;
968 }
969 }
970 if (!found) {
971 auto *scn = new SharedCommentNode(node);
972 sharedCommentNodes.append(scn);
973 tied.emplace_back(TiedDocumentation{doc, scn});
974 }
975 }
976 }
977 for (auto *scn : sharedCommentNodes) {
978 // Don't sort function nodes - preserve the order from \fn commands
979 // for position-dependent \overload primary behavior
980 if (!scn->collective().isEmpty() && !scn->collective().first()->isFunction())
981 scn->sort();
982 }
983 }
984 }
985 return std::make_pair(tied, errors);
986}
987
988/*!
989 For each node that is part of C++ API and produces a documentation
990 page, this function ensures that the node belongs to a module.
991 */
993{
994 if (n->physicalModuleName().isEmpty()) {
995 if (n->isInAPI() && !n->name().isEmpty()) {
996 switch (n->nodeType()) {
997 case NodeType::Class:
998 case NodeType::Struct:
999 case NodeType::Union:
1002 break;
1003 default:
1004 return;
1005 }
1006 n->setPhysicalModuleName(Generator::defaultModuleName());
1007 QDocDatabase::qdocDB()->addToModule(Generator::defaultModuleName(), n);
1008 n->doc().location().warning(
1009 QStringLiteral("Documentation for %1 '%2' has no \\inmodule command; "
1010 "using project name by default: %3")
1011 .arg(Node::nodeTypeString(n->nodeType()), n->name(),
1012 n->physicalModuleName()));
1013 }
1014 }
1015}
1016
1017void CppCodeParser::processMetaCommands(const std::vector<TiedDocumentation> &tied)
1018{
1019 for (auto [doc, node] : tied) {
1020 node->setDoc(doc);
1021 processMetaCommands(doc, node);
1022 checkModuleInclusion(node);
1023 if (node->isAggregate()) {
1024 auto *aggregate = static_cast<Aggregate *>(node);
1025
1026 if (!aggregate->includeFile()) {
1027 const QString className = aggregate->name();
1028
1029 // Resolution priority:
1030 // 1. Convenience header (if exists in include paths)
1031 // 2. Include-relative path from declLocation
1032 // 3. Class name as last resort (only if non-empty)
1033 QString includeFile;
1034 if (convenienceHeaderExists(className)) {
1035 includeFile = className;
1036 } else {
1037 includeFile = computeIncludeSpelling(aggregate->declLocation());
1038 if (includeFile.isEmpty() && !className.isEmpty())
1039 includeFile = className;
1040 }
1041
1042 if (!includeFile.isEmpty())
1043 aggregate->setIncludeFile(includeFile);
1044 }
1045 }
1046 }
1047}
1048
1049void CppCodeParser::processQmlNativeTypeCommand(Node *node, const QString &cmd, const QString &arg, const Location &location)
1050{
1051 Q_ASSERT(node);
1052 if (!node->isQmlNode()) {
1053 location.warning(
1054 QStringLiteral("Command '\\%1' is only meaningful in '\\%2'")
1055 .arg(cmd, COMMAND_QMLTYPE));
1056 return;
1057 }
1058
1059 auto qmlNode = static_cast<QmlTypeNode *>(node);
1060
1062 auto classNode = database->findClassNode(arg.split(u"::"_s));
1063
1064 if (!classNode) {
1065 if (m_showLinkErrors) {
1066 location.warning(
1067 QStringLiteral("C++ class %2 not found: \\%1 %2")
1068 .arg(cmd, arg));
1069 }
1070 return;
1071 }
1072
1073 if (qmlNode->classNode()) {
1074 location.warning(
1075 QStringLiteral("QML type %1 documented with %2 as its native type. Replacing %2 with %3")
1076 .arg(qmlNode->name(), qmlNode->classNode()->name(), arg));
1077 }
1078
1079 qmlNode->setClassNode(classNode);
1080 classNode->insertQmlNativeType(qmlNode);
1081
1082 if (classNode->isQmlSingleton())
1083 qmlNode->setSingleton(true);
1084}
1085
1086namespace {
1087
1088/*!
1089 Strips compiler include path prefixes (-I, -isystem, etc.) from a path.
1090 Returns an empty string if the path is an unrecognized flag.
1091*/
1092QString stripIncludePrefix(const QString &path)
1093{
1094 QString result = path.trimmed();
1095
1096 static const QStringList prefixes = {
1097 "-I"_L1, "-isystem"_L1, "-iquote"_L1, "-idirafter"_L1
1098 };
1099
1100 for (const QString &prefix : prefixes) {
1101 if (result.startsWith(prefix)) {
1102 result = result.mid(prefix.size()).trimmed();
1103 return QDir::cleanPath(result);
1104 }
1105 }
1106
1107 // Skip framework paths and other unrecognized flags
1108 if (result.startsWith(u'-'))
1109 return {};
1110
1111 return QDir::cleanPath(result);
1112}
1113
1114} // anonymous namespace
1115
1116/*!
1117 Returns the cached list of cleaned include paths, combining both
1118 command-line and qdocconf include paths with prefixes stripped.
1119*/
1120const QStringList &CppCodeParser::getCleanIncludePaths() const
1121{
1122 if (!m_includePathsCached) {
1123 // Combine command-line and qdocconf include paths
1124 QStringList rawPaths = Config::instance().includePaths();
1125 rawPaths += Config::instance().getCanonicalPathList(
1126 CONFIG_INCLUDEPATHS, Config::IncludePaths);
1127
1128 for (const QString &path : rawPaths) {
1129 QString clean = stripIncludePrefix(path);
1130 if (!clean.isEmpty() && !m_cleanIncludePaths.contains(clean))
1131 m_cleanIncludePaths.append(clean);
1132 }
1133 m_includePathsCached = true;
1134 }
1135 return m_cleanIncludePaths;
1136}
1137
1138/*!
1139 Checks if a convenience header (extensionless file matching the class name)
1140 exists in any of the configured include paths. Results are cached.
1141
1142 This supports Qt's convention where classes like QString have convenience
1143 headers (extensionless files) that redirect to the actual header.
1144*/
1145bool CppCodeParser::convenienceHeaderExists(const QString &className) const
1146{
1147 if (className.isEmpty())
1148 return false;
1149
1150 auto it = m_convenienceHeaderCache.constFind(className);
1151 if (it != m_convenienceHeaderCache.constEnd())
1152 return *it;
1153
1154 bool exists = false;
1155 for (const QString &includePath : getCleanIncludePaths()) {
1156 QFileInfo candidate(includePath + u'/' + className);
1157 if (candidate.exists() && candidate.isFile()) {
1158 exists = true;
1159 break;
1160 }
1161 }
1162
1163 m_convenienceHeaderCache.insert(className, exists);
1164 return exists;
1165}
1166
1167/*!
1168 Returns the basename of the header file from the declaration location.
1169
1170 This provides a simple, reliable include spelling that works regardless
1171 of the working directory or include path configuration. For more complex
1172 include paths (like subdirectories), users can use \\inheaderfile.
1173*/
1174QString CppCodeParser::computeIncludeSpelling(const Location &loc) const
1175{
1176 if (loc.isEmpty())
1177 return {};
1178
1179 return loc.fileName();
1180}
1181
1182QT_END_NAMESPACE
Access
Definition access.h:11
The ClassNode represents a C++ class.
Definition classnode.h:23
static bool isWorthWarningAbout(const Doc &doc)
Test for whether a doc comment warrants warnings.
A class for holding the members of a collection of doc pages.
bool asBool() const
Returns this config variable as a boolean.
Definition config.cpp:281
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:85
static bool generateExamples
Definition config.h:171
static const QString dot
Definition config.h:169
const ExcludedPaths & getExcludedPaths()
Definition config.cpp:1439
std::vector< TiedDocumentation > processQmlProperties(const UntiedDocumentation &untied)
FunctionNode * parseOtherFuncArg(const QString &topic, const Location &location, const QString &funcArg)
Parse QML signal/method topic commands.
FunctionNode * parseMacroArg(const Location &location, const QString &macroArg)
Parse the macro arguments in macroArg ad hoc, without using any actual parser.
void processMetaCommand(const Doc &doc, const QString &command, const ArgPair &argLocPair, Node *node)
Process the metacommand command in the context of the node associated with the topic command and the ...
CppCodeParser(FnCommandParser &&parser)
static bool isQMLMethodTopic(const QString &t)
returns true if t is {qmlsignal}, {qmlmethod}, {qmlattachedsignal}, or {qmlattachedmethod}...
void processMetaCommands(const std::vector< TiedDocumentation > &tied)
static bool isQMLPropertyTopic(const QString &t)
Returns true if t is {qmlproperty}, {qmlpropertygroup}, or {qmlattachedproperty}.
virtual Node * processTopicCommand(const Doc &doc, const QString &command, const ArgPair &arg)
Process the topic command found in the doc with argument arg.
void processMetaCommands(const Doc &doc, Node *node)
The topic command has been processed, and now doc and node are passed to this function to get the met...
Definition doc.h:31
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:90
const Text & title() const
Definition doc.cpp:121
const Location & startLocation() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:99
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
Definition doc.cpp:271
The ExternalPageNode represents an external documentation page.
This node is used to represent any kind of function being documented.
The Location class provides a way to mark a location in a file.
Definition location.h:20
Location()
Constructs an empty location.
Definition location.cpp:48
bool isEmpty() const
Returns true if there is no file name set yet; returns false otherwise.
Definition location.h:45
Interface implemented by Node subclasses that can refer to a C++ enum.
Definition nativeenum.h:28
virtual NativeEnum * nativeEnum()=0
Encapsulates information about native (C++) enum values.
Definition nativeenum.h:14
bool resolve(const QString &path, const QString &registeredQmlName)
Locates the node specified by path and sets it as the C++ enumeration associated with this property.
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:19
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
void addExampleNode(ExampleNode *n)
static QDocDatabase * qdocDB()
Creates the singleton.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
Status
Specifies the status of the QQmlIncubator.
bool isEmpty() const
Definition text.h:31
QString toString() const
This function traverses the atom list of the Text object, extracting all the string parts.
Definition text.cpp:124
#define COMMAND_ENUM
Definition codeparser.h:23
#define COMMAND_HEADERFILE
Definition codeparser.h:28
#define COMMAND_EXTERNALPAGE
Definition codeparser.h:25
#define COMMAND_QMLINHERITS
Definition codeparser.h:57
#define COMMAND_MODULE
Definition codeparser.h:36
#define COMMAND_MODULESTATE
Definition codeparser.h:37
#define COMMAND_INTERNAL
Definition codeparser.h:34
#define COMMAND_NONREENTRANT
Definition codeparser.h:41
#define COMMAND_QMLSIGNAL
Definition codeparser.h:66
#define COMMAND_OBSOLETE
Definition codeparser.h:42
#define COMMAND_INMODULE
Definition codeparser.h:31
#define COMMAND_STRUCT
Definition codeparser.h:76
#define COMMAND_DEPRECATED
Definition codeparser.h:21
#define COMMAND_QMLENUM
Definition codeparser.h:55
#define COMMAND_QMLSINGLETONTYPE
Definition codeparser.h:68
#define COMMAND_PRELIMINARY
Definition codeparser.h:45
#define COMMAND_PROPERTY
Definition codeparser.h:47
#define COMMAND_NEXTPAGE
Definition codeparser.h:39
#define COMMAND_RELATES
Definition codeparser.h:74
#define COMMAND_WRAPPER
Definition codeparser.h:85
#define COMMAND_CLASS
Definition codeparser.h:14
#define COMMAND_NAMESPACE
Definition codeparser.h:38
#define COMMAND_CMAKETARGETITEM
Definition codeparser.h:17
#define COMMAND_REENTRANT
Definition codeparser.h:72
#define COMMAND_QMLMODULE
Definition codeparser.h:60
#define COMMAND_QMLPROPERTY
Definition codeparser.h:62
#define COMMAND_STARTPAGE
Definition codeparser.h:78
#define COMMAND_QMLDEFAULT
Definition codeparser.h:54
#define COMMAND_SINCE
Definition codeparser.h:75
#define COMMAND_QMLABSTRACT
Definition codeparser.h:48
#define COMMAND_QMLNATIVETYPE
Definition codeparser.h:61
#define COMMAND_FN
Definition codeparser.h:26
#define COMMAND_OVERLOAD
Definition codeparser.h:43
#define COMMAND_QTVARIABLE
Definition codeparser.h:71
#define COMMAND_QTCMAKEPACKAGE
Definition codeparser.h:69
#define COMMAND_NOAUTOLIST
Definition codeparser.h:40
#define COMMAND_QMLATTACHEDPROPERTY
Definition codeparser.h:50
#define COMMAND_UNION
Definition codeparser.h:84
#define COMMAND_COMPARESWITH
Definition codeparser.h:19
#define COMMAND_QTCMAKETARGETITEM
Definition codeparser.h:70
#define COMMAND_MACRO
Definition codeparser.h:35
#define COMMAND_GROUP
Definition codeparser.h:27
#define COMMAND_REIMP
Definition codeparser.h:73
#define COMMAND_VARIABLE
Definition codeparser.h:82
#define COMMAND_INHEADERFILE
Definition codeparser.h:30
#define COMMAND_PREVIOUSPAGE
Definition codeparser.h:46
#define COMMAND_QMLBASICTYPE
Definition codeparser.h:89
#define COMMAND_PAGE
Definition codeparser.h:44
#define COMMAND_EXAMPLE
Definition codeparser.h:24
#define COMMAND_COMPARES
Definition codeparser.h:18
#define COMMAND_DEFAULT
Definition codeparser.h:20
#define COMMAND_THREADSAFE
Definition codeparser.h:79
#define COMMAND_TYPEDEF
Definition codeparser.h:81
#define COMMAND_QMLMETHOD
Definition codeparser.h:59
#define COMMAND_DONTDOCUMENT
Definition codeparser.h:22
#define COMMAND_CMAKECOMPONENT
Definition codeparser.h:16
#define COMMAND_QMLREADONLY
Definition codeparser.h:64
#define COMMAND_QMLENUMERATORSFROM
Definition codeparser.h:56
#define COMMAND_INPUBLICGROUP
Definition codeparser.h:32
#define COMMAND_QMLREQUIRED
Definition codeparser.h:65
#define COMMAND_ABSTRACT
Definition codeparser.h:13
#define COMMAND_QMLVALUETYPE
Definition codeparser.h:52
#define COMMAND_ATTRIBUTION
Definition codeparser.h:86
#define COMMAND_INQMLMODULE
Definition codeparser.h:33
#define COMMAND_QMLINSTANTIATES
Definition codeparser.h:58
#define COMMAND_TYPEALIAS
Definition codeparser.h:80
#define COMMAND_CMAKEPACKAGE
Definition codeparser.h:15
#define COMMAND_INGROUP
Definition codeparser.h:29
#define COMMAND_QMLTYPE
Definition codeparser.h:67
#define COMMAND_QMLATTACHEDMETHOD
Definition codeparser.h:49
#define COMMAND_SUBTITLE
Definition codeparser.h:77
#define COMMAND_QMLATTACHEDSIGNAL
Definition codeparser.h:51
#define CONFIG_FILEEXTENSIONS
Definition config.h:449
#define CONFIG_EXAMPLES
Definition config.h:380
#define CONFIG_EXAMPLEDIRS
Definition config.h:379
#define CONFIG_NOLINKERRORS
Definition config.h:416
#define CONFIG_IMAGEEXTENSIONS
Definition config.h:450
#define CONFIG_INCLUDEPATHS
Definition config.h:399
static const QMap< QString, NodeTypeTestFunc > s_nodeTypeTestFuncMap
bool(Node::* NodeTypeTestFunc)() const
static void checkModuleInclusion(Node *n)
For each node that is part of C++ API and produces a documentation page, this function ensures that t...
QmlTypeNode * findOrCreateQmlType(const QString &moduleName, const QString &name, const Location &location)
Finds a QmlTypeNode name, under the specific moduleName, from the primary tree.
std::pair< QString, QString > ArgPair
Definition doc.h:26
NodeType
Definition genustypes.h:150
This namespace holds QDoc-internal utility methods.
Definition utilities.h:20
QList< Node * > NodeList
Definition node.h:45
@ Public
Definition access.h:11
@ Deprecated
Definition status.h:12
@ Preliminary
Definition status.h:13
The Node class is the base class for all the nodes in QDoc's parse tree.
void markInternal()
Sets the node's access to Private and its status to Internal.
Definition node.h:203
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:235
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:119
void setAccess(Access t)
Sets the node's access type to t.
Definition node.h:170
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:108
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:126
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:121
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
Definition node.h:124
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition node.cpp:862
NodeType nodeType() const override
Returns this node's type.
Definition node.h:82
bool isStruct() const
Returns true if the node type is Struct.
Definition node.h:123
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:153
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:208
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:131
void setLocation(const Location &t)
Sets the node's declaration location, its definition location, or both, depending on the suffix of th...
Definition node.cpp:899
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:136
void setTemplateDecl(std::optional< RelaxedTemplateDeclaration > t)
Definition node.h:178
virtual void markReadOnly(bool)
If this node is a QmlPropertyNode, then the property's read-only flag is set to flag.
Definition node.h:206
void setComparisonCategory(const ComparisonCategory &category)
Definition node.h:183
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:231
const std::optional< RelaxedTemplateDeclaration > & templateDecl() const
Definition node.h:243
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:228
virtual void setWrapper()
If this node is a ClassNode or a QmlTypeNode, the node's wrapper flag data member is set to true.
Definition node.h:190
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:100
virtual void markDefault()
If this node is a QmlPropertyNode, it is marked as the default property.
Definition node.h:205
virtual bool isInAPI() const
Returns true if this node is considered to be part of the API as per the InclusionPolicy retrieved fr...
Definition node.cpp:917
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:112
bool isTypeAlias() const
Returns true if the node type is Typedef.
Definition node.h:125
bool isModule() const
Returns true if the node type is Module.
Definition node.h:107
@ ThreadSafe
Definition node.h:62
@ NonReentrant
Definition node.h:60
@ Reentrant
Definition node.h:61
virtual void setAbstract(bool)
If this node is a ClassNode or a QmlTypeNode, the node's abstract flag data member is set to b.
Definition node.h:189
bool isPreliminary() const
Returns true if this node's status is Preliminary.
Definition node.h:110
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:143
void setStatus(Status t)
Sets the node's status to t.
Definition node.cpp:570
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:144
void setThreadSafeness(ThreadSafeness t)
Sets the node's thread safeness to t.
Definition node.h:174
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:118
bool isExample() const
Returns true if the node type is Example.
Definition node.h:98
bool isUnion() const
Returns true if the node type is Union.
Definition node.h:130
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:120
Helper class for parsing QML property arguments.
static std::optional< QmlPropertyArguments > parse(const QString &arg, const Location &loc, ParsingOptions opts=ParsingOptions::None)
Parses a QML property from the input string str, with parsing options opts.
ParsingOptions
\value None No options specified.
friend ParsingOptions operator|(ParsingOptions lhs, ParsingOptions rhs)
QList< Topic > TopicList
Definition topic.h:25