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 if (node->isFunction()) {
364 auto *fn = static_cast<FunctionNode *>(node);
365
366 // Check if this is "\overload primary"
367 const auto &overloadArgs = doc.overloadList();
368 if (!overloadArgs.isEmpty() && overloadArgs.first().first == "primary") {
370 } else {
372 }
373 } else if (node->isSharedCommentNode()) {
374 static_cast<SharedCommentNode *>(node)->setOverloadFlags();
375 } else {
376 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_OVERLOAD));
377 }
378 } else if (command == COMMAND_REIMP) {
379 if (node->parent() && !node->parent()->isInternal()) {
380 if (node->isFunction()) {
381 auto *fn = static_cast<FunctionNode *>(node);
382 // The clang visitor class will have set the
383 // qualified name of the overridden function.
384 // If the name of the overridden function isn't
385 // set, issue a warning.
387 doc.location().warning(
388 QStringLiteral("Cannot find base function for '\\%1' in %2()")
389 .arg(COMMAND_REIMP, node->name()),
390 QStringLiteral("The function either doesn't exist in any "
391 "base class with the same signature or it "
392 "exists but isn't virtual."));
393 }
395 } else {
396 doc.location().warning(
397 QStringLiteral("Ignored '\\%1' in %2").arg(COMMAND_REIMP, node->name()));
398 }
399 }
400 } else if (command == COMMAND_RELATES) {
401 // REMARK: Generates warnings only; Node instances are
402 // adopted from the root namespace to other Aggregates
403 // in a post-processing step, Aggregate::resolveRelates(),
404 // after all topic commands are processed.
405 if (node->isAggregate()) {
406 doc.location().warning("Invalid '\\%1' not allowed in '\\%2'"_L1
407 .arg(COMMAND_RELATES, node->nodeTypeString()));
408 }
409 } else if (command == COMMAND_NEXTPAGE) {
410 CodeParser::setLink(node, Node::NextLink, arg);
411 } else if (command == COMMAND_PREVIOUSPAGE) {
412 CodeParser::setLink(node, Node::PreviousLink, arg);
413 } else if (command == COMMAND_STARTPAGE) {
414 CodeParser::setLink(node, Node::StartLink, arg);
415 } else if (command == COMMAND_QMLINHERITS) {
416 if (node->name() == arg)
417 doc.location().warning(QStringLiteral("%1 tries to inherit itself").arg(arg));
418 else if (node->isQmlType()) {
419 auto *qmlType = static_cast<QmlTypeNode *>(node);
420 qmlType->setQmlBaseName(arg);
421 }
422 } else if (command == COMMAND_QMLNATIVETYPE || command == COMMAND_QMLINSTANTIATES) {
423 if (command == COMMAND_QMLINSTANTIATES)
424 doc.location().report(
425 u"\\instantiates is deprecated and will be removed in a future version. Use \\nativetype instead."_s);
426 // TODO: COMMAND_QMLINSTANTIATES is deprecated since 6.8. Its remains should be removed no later than Qt 7.0.0.
427 processQmlNativeTypeCommand(node, command, arg, doc.location());
428 } else if (command == COMMAND_DEFAULT) {
429 if (!node->isQmlProperty()) {
430 doc.location().warning(QStringLiteral("Ignored '\\%1', applies only to '\\%2'")
431 .arg(command, COMMAND_QMLPROPERTY));
432 } else if (arg.isEmpty()) {
433 doc.location().warning(QStringLiteral("Expected an argument for '\\%1' (maybe you meant '\\%2'?)")
434 .arg(command, COMMAND_QMLDEFAULT));
435 } else {
436 static_cast<QmlPropertyNode *>(node)->setDefaultValue(arg);
437 }
438 } else if (command == COMMAND_QMLDEFAULT) {
439 node->markDefault();
440 } else if (command == COMMAND_QMLENUMERATORSFROM) {
441 NativeEnum *nativeEnum{nullptr};
442 if (auto *ne_if = dynamic_cast<NativeEnumInterface *>(node))
443 nativeEnum = ne_if->nativeEnum();
444 else {
445 doc.location().warning("Ignored '\\%1', applies only to '\\%2' and '\\%3'"_L1
446 .arg(command, COMMAND_QMLPROPERTY, COMMAND_QMLENUM));
447 return;
448 }
449 if (!nativeEnum->resolve(argPair.first, argPair.second)) {
450 doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
451 .arg(command, arg), "Use \\value commands instead"_L1);
452 }
453 } else if (command == COMMAND_QMLREADONLY) {
454 node->markReadOnly(true);
455 } else if (command == COMMAND_QMLREQUIRED) {
456 if (!node->isQmlProperty())
457 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_QMLREQUIRED));
458 else
459 static_cast<QmlPropertyNode *>(node)->setRequired();
460 } else if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
461 if (node->isQmlType())
462 node->setAbstract(true);
463 } else if (command == COMMAND_DEPRECATED) {
464 node->setDeprecated(argPair.second);
465 } else if (command == COMMAND_INGROUP || command == COMMAND_INPUBLICGROUP) {
466 // Note: \ingroup and \inpublicgroup are the same (and now recognized as such).
467 database->addToGroup(arg, node);
468 } else if (command == COMMAND_INMODULE) {
469 database->addToModule(arg, node);
470 } else if (command == COMMAND_INQMLMODULE) {
471 // Handled when parsing topic commands
472 } else if (command == COMMAND_OBSOLETE) {
474 } else if (command == COMMAND_NONREENTRANT) {
476 } else if (command == COMMAND_PRELIMINARY) {
477 // \internal wins.
478 if (!node->isInternal())
480 } else if (command == COMMAND_INTERNAL) {
481 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
484 } else if (command == COMMAND_REENTRANT) {
486 } else if (command == COMMAND_SINCE) {
487 node->setSince(arg);
488 } else if (command == COMMAND_WRAPPER) {
489 node->setWrapper();
490 } else if (command == COMMAND_THREADSAFE) {
492 } else if (command == COMMAND_TITLE) {
493 if (!node->setTitle(arg))
494 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_TITLE));
495 else if (node->isExample())
496 database->addExampleNode(static_cast<ExampleNode *>(node));
497 } else if (command == COMMAND_SUBTITLE) {
498 if (!node->setSubtitle(arg))
499 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
500 } else if (command == COMMAND_QTVARIABLE) {
501 node->setQtVariable(arg);
502 if (!node->isModule() && !node->isQmlModule())
503 doc.location().warning(
504 QStringLiteral(
505 "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
506 .arg(COMMAND_QTVARIABLE));
507 } else if (command == COMMAND_QTCMAKEPACKAGE) {
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_QTCMAKETARGETITEM) {
515 if (node->isModule())
516 node->setCMakeTargetItem(QLatin1String("Qt6::") + arg);
517 else
518 doc.location().warning(
519 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
521 } else if (command == COMMAND_CMAKEPACKAGE) {
522 if (node->isModule())
523 node->setCMakePackage(arg);
524 else
525 doc.location().warning(
526 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
528 } else if (command == COMMAND_CMAKECOMPONENT) {
529 if (node->isModule())
530 node->setCMakeComponent(arg);
531 else
532 doc.location().warning(
533 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
535 } else if (command == COMMAND_CMAKETARGETITEM) {
536 if (node->isModule())
537 node->setCMakeTargetItem(arg);
538 else
539 doc.location().warning(
540 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
542 } else if (command == COMMAND_MODULESTATE ) {
543 if (!node->isModule() && !node->isQmlModule()) {
544 doc.location().warning(
545 QStringLiteral(
546 "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
547 .arg(COMMAND_MODULESTATE));
548 } else {
549 static_cast<CollectionNode*>(node)->setState(arg);
550 }
551 } else if (command == COMMAND_NOAUTOLIST) {
552 if (!node->isCollectionNode() && !node->isExample()) {
553 doc.location().warning(
554 QStringLiteral(
555 "Command '\\%1' is only meaningful in '\\module', '\\qmlmodule', `\\group` and `\\example`.")
556 .arg(COMMAND_NOAUTOLIST));
557 } else {
558 static_cast<PageNode*>(node)->setNoAutoList(true);
559 }
560 } else if (command == COMMAND_ATTRIBUTION) {
561 // TODO: This condition is not currently exact enough, as it
562 // will allow any non-aggregate `PageNode` to use the command,
563 // For example, an `ExampleNode`.
564 //
565 // The command is intended only for internal usage by
566 // "qattributionscanner" and should only work on `PageNode`s
567 // that are generated from a "\page" command.
568 //
569 // It is already possible to provide a more restricted check,
570 // albeit in a somewhat dirty way. It is not expected that
571 // this warning will have any particular use.
572 // If it so happens that a case where the too-broad scope of
573 // the warning is a problem or hides a bug, modify the
574 // condition to be restrictive enough.
575 // Otherwise, wait until a more torough look at QDoc's
576 // internal representations an way to enable "Attribution
577 // Pages" is performed before looking at the issue again.
578 if (!node->isTextPageNode()) {
579 doc.location().warning(u"Command '\\%1' is only meaningful in '\\%2'"_s.arg(COMMAND_ATTRIBUTION, COMMAND_PAGE));
580 } else { static_cast<PageNode*>(node)->markAttribution(); }
581 }
582}
583
584/*!
585 \internal
586 Processes the argument \a arg that's passed to the \\compares command,
587 and sets the comparison category of the \a node accordingly.
588
589 If the argument is invalid, issue a warning at the location the command
590 appears through \a loc.
591*/
592void CppCodeParser::processComparesCommand(Node *node, const QString &arg, const Location &loc)
593{
594 if (!node->isClassNode()) {
595 loc.warning(u"Found \\%1 command outside of \\%2 context."_s.arg(COMMAND_COMPARES,
597 return;
598 }
599
600 if (auto category = comparisonCategoryFromString(arg.toStdString());
601 category != ComparisonCategory::None) {
602 node->setComparisonCategory(category);
603 } else {
604 loc.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(COMMAND_COMPARES, arg),
605 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
606 }
607}
608
609/*!
610 The topic command has been processed, and now \a doc and
611 \a node are passed to this function to get the metacommands
612 from \a doc and process them one at a time. \a node is the
613 node where \a doc resides.
614 */
616{
617 std::vector<Node*> nodes_to_process{};
618 if (node->isSharedCommentNode()) {
619 auto scn = static_cast<SharedCommentNode*>(node);
620
621 nodes_to_process.reserve(scn->count() + 1);
622 std::copy(scn->collective().cbegin(), scn->collective().cend(), std::back_inserter(nodes_to_process));
623 }
624
625 // REMARK: Ordering is important here. If node is a
626 // SharedCommentNode it MUST be processed after all its child
627 // nodes.
628 // Failure to do so can incur in incorrect warnings.
629 // For example, if a shared documentation has a "\relates" command.
630 // When the command is processed for the SharedCommentNode it will
631 // apply to all its child nodes.
632 // If a child node is processed after the SharedCommentNode that
633 // contains it, that "\relates" command will be considered applied
634 // already, resulting in a warning.
635 nodes_to_process.push_back(node);
636
637 const QStringList metaCommandsUsed = doc.metaCommandsUsed().values();
638 for (const auto &command : metaCommandsUsed) {
639 const ArgList args = doc.metaCommandArgs(command);
640 for (const auto &arg : args) {
641 std::for_each(nodes_to_process.cbegin(), nodes_to_process.cend(), [this, doc, command, arg](auto node){
642 processMetaCommand(doc, command, arg, node);
643 });
644 }
645 }
646}
647
648/*!
649 Creates an EnumNode instance explicitly for the \qmlenum command.
650 Utilizes QmlPropertyArguments for argument (\a arg) parsing.
651
652 Adds a list of \a enumItemNames as enumerators to facilitate linking
653 via enumerator names.
654*/
655EnumNode *CppCodeParser::processQmlEnumTopic(const QStringList &enumItemNames,
656 const Location &location, const QString &arg)
657{
658 if (arg.isEmpty()) {
659 location.warning(u"Missing argument to \\%1 command."_s.arg(COMMAND_QMLENUM));
660 return nullptr;
661 }
662
663 auto parsedArgs = QmlPropertyArguments::parse(arg, location,
666
667 if (!parsedArgs)
668 return nullptr;
669
670 auto *qmlType = findOrCreateQmlType((*parsedArgs).m_module, (*parsedArgs).m_qmltype, location);
671
672 auto *enumNode = new QmlEnumNode(qmlType, (*parsedArgs).m_name);
673 enumNode->setLocation(location);
674
675 for (const auto &item : enumItemNames)
676 enumNode->addItem(EnumItem(item, 0));
677
678 return enumNode;
679}
680
681/*!
682 Parse QML signal/method topic commands.
683 */
684FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Location &location,
685 const QString &funcArg)
686{
687 QString funcName;
688 QString returnType;
689
690 qsizetype leftParen = funcArg.indexOf(QChar('('));
691 if (leftParen > 0)
692 funcName = funcArg.left(leftParen);
693 else
694 funcName = funcArg;
695 qsizetype firstBlank = funcName.indexOf(QChar(' '));
696 if (firstBlank > 0) {
697 returnType = funcName.left(firstBlank);
698 funcName = funcName.right(funcName.size() - firstBlank - 1);
699 }
700
701 QStringList colonSplit(funcName.split("::"));
702 if (colonSplit.size() < 2) {
703 QString msg = "Unrecognizable QML module/component qualifier for " + funcArg;
704 location.warning(msg.toLatin1().data());
705 return nullptr;
706 }
707 QString moduleName;
708 QString elementName;
709 if (colonSplit.size() > 2) {
710 moduleName = colonSplit[0];
711 elementName = colonSplit[1];
712 } else {
713 elementName = colonSplit[0];
714 }
715 funcName = colonSplit.last();
716
717 auto *aggregate = findOrCreateQmlType(moduleName, elementName, location);
718
719 QString params;
720 QStringList leftParenSplit = funcArg.split('(');
721 if (leftParenSplit.size() > 1) {
722 QStringList rightParenSplit = leftParenSplit[1].split(')');
723 if (!rightParenSplit.empty())
724 params = rightParenSplit[0];
725 }
726
727 FunctionNode::Metaness metaness = FunctionNode::getMetanessFromTopic(topic);
728 bool attached = topic.contains(QLatin1String("attached"));
729 auto *fn = new FunctionNode(metaness, aggregate, funcName, attached);
730 fn->setAccess(Access::Public);
731 fn->setLocation(location);
732 fn->setReturnType(returnType);
733 fn->setParameters(params);
734 return fn;
735}
736
737/*!
738 Parse the macro arguments in \a macroArg ad hoc, without using
739 any actual parser. If successful, return a pointer to the new
740 FunctionNode for the macro. Otherwise return null. \a location
741 is used for reporting errors.
742 */
743FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QString &macroArg)
744{
746
747 QStringList leftParenSplit = macroArg.split('(');
748 if (leftParenSplit.isEmpty())
749 return nullptr;
750 QString macroName;
751 FunctionNode *oldMacroNode = nullptr;
752 QStringList blankSplit = leftParenSplit[0].split(' ');
753 if (!blankSplit.empty()) {
754 macroName = blankSplit.last();
755 oldMacroNode = database->findMacroNode(macroName);
756 }
757 QString returnType;
758 if (blankSplit.size() > 1) {
759 blankSplit.removeLast();
760 returnType = blankSplit.join(' ');
761 }
762 QString params;
763 if (leftParenSplit.size() > 1) {
764 params = QString("");
765 const QString &afterParen = leftParenSplit.at(1);
766 qsizetype rightParen = afterParen.indexOf(')');
767 if (rightParen >= 0)
768 params = afterParen.left(rightParen);
769 }
770 int i = 0;
771 while (i < macroName.size() && !macroName.at(i).isLetter())
772 i++;
773 if (i > 0) {
774 returnType += QChar(' ') + macroName.left(i);
775 macroName = macroName.mid(i);
776 }
778 if (params.isNull())
780 auto *macro = new FunctionNode(metaness, database->primaryTreeRoot(), macroName);
781 macro->setAccess(Access::Public);
782 macro->setLocation(location);
783 macro->setReturnType(returnType);
784 macro->setParameters(params);
785 if (oldMacroNode && macro->parent() == oldMacroNode->parent()
786 && compare(macro, oldMacroNode) == 0) {
787 location.warning(QStringLiteral("\\macro %1 documented more than once")
788 .arg(macroArg), QStringLiteral("also seen here: %1")
789 .arg(oldMacroNode->doc().location().toString()));
790 }
791 return macro;
792}
793
794void CppCodeParser::setExampleFileLists(ExampleNode *en)
795{
796 Config &config = Config::instance();
797 QString fullPath = config.getExampleProjectFile(en->name());
798 if (fullPath.isEmpty()) {
799 QString details = QLatin1String("Example directories: ")
800 + config.getCanonicalPathList(CONFIG_EXAMPLEDIRS).join(QLatin1Char(' '));
801 en->location().warning(
802 QStringLiteral("Cannot find project file for example '%1'").arg(en->name()),
803 details);
804 return;
805 }
806
807 QDir exampleDir(QFileInfo(fullPath).dir());
808
809 const auto& [excludeDirs, excludeFiles] = config.getExcludedPaths();
810
811 QStringList exampleFiles = Config::getFilesHere(exampleDir.path(), m_exampleNameFilter,
812 Location(), excludeDirs, excludeFiles);
813 // Search for all image files under the example project, excluding doc/images directory.
814 QSet<QString> excludeDocDirs(excludeDirs);
815 excludeDocDirs.insert(exampleDir.path() + QLatin1String("/doc/images"));
816 QStringList imageFiles = Config::getFilesHere(exampleDir.path(), m_exampleImageFilter,
817 Location(), excludeDocDirs, excludeFiles);
818 if (!exampleFiles.isEmpty()) {
819 // move main.cpp to the end, if it exists
820 QString mainCpp;
821
822 const auto isGeneratedOrMainCpp = [&mainCpp](const QString &fileName) {
823 if (fileName.endsWith("/main.cpp")) {
824 if (mainCpp.isEmpty())
825 mainCpp = fileName;
826 return true;
827 }
828 return Utilities::isGeneratedFile(fileName);
829 };
830
831 exampleFiles.erase(
832 std::remove_if(exampleFiles.begin(), exampleFiles.end(), isGeneratedOrMainCpp),
833 exampleFiles.end());
834
835 if (!mainCpp.isEmpty())
836 exampleFiles.append(mainCpp);
837
838 // Add any resource and project files
839 exampleFiles += Config::getFilesHere(exampleDir.path(),
840 QLatin1String("*.qrc *.pro *.qmlproject *.pyproject CMakeLists.txt qmldir"),
841 Location(), excludeDirs, excludeFiles);
842 }
843
844 const qsizetype pathLen = exampleDir.path().size() - en->name().size();
845 for (auto &file : exampleFiles)
846 file = file.mid(pathLen);
847 for (auto &file : imageFiles)
848 file = file.mid(pathLen);
849
850 en->setFiles(exampleFiles, fullPath.mid(pathLen));
851 en->setImages(imageFiles);
852}
853
854/*!
855 returns true if \a t is \e {qmlsignal}, \e {qmlmethod},
856 \e {qmlattachedsignal}, or \e {qmlattachedmethod}.
857 */
858bool CppCodeParser::isQMLMethodTopic(const QString &t)
859{
862}
863
864/*!
865 Returns true if \a t is \e {qmlproperty}, \e {qmlpropertygroup},
866 or \e {qmlattachedproperty}.
867 */
868bool CppCodeParser::isQMLPropertyTopic(const QString &t)
869{
871}
872
873std::pair<std::vector<TiedDocumentation>, std::vector<FnMatchError>>
874CppCodeParser::processTopicArgs(const UntiedDocumentation &untied)
875{
876 const Doc &doc = untied.documentation;
877
878 if (doc.topicsUsed().isEmpty())
879 return {};
880
881 QDocDatabase *database = QDocDatabase::qdocDB();
882
883 const QString topic = doc.topicsUsed().first().m_topic;
884
885 std::vector<TiedDocumentation> tied{};
886 std::vector<FnMatchError> errors{};
887
888 if (isQMLPropertyTopic(topic)) {
889 auto tied_qml = processQmlProperties(untied);
890 tied.insert(tied.end(), tied_qml.begin(), tied_qml.end());
891 } else {
892 ArgList args = doc.metaCommandArgs(topic);
893 Node *node = nullptr;
894 if (args.size() == 1) {
895 if (topic == COMMAND_FN) {
896 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
897 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
898 auto result = fn_parser(doc.location(), args[0].first, args[0].second, untied.context);
899 if (auto *error = std::get_if<FnMatchError>(&result))
900 errors.emplace_back(*error);
901 else
902 node = std::get<Node*>(result);
903 }
904 } else if (topic == COMMAND_MACRO) {
905 node = parseMacroArg(doc.location(), args[0].first);
906 } else if (isQMLMethodTopic(topic)) {
907 node = parseOtherFuncArg(topic, doc.location(), args[0].first);
908 } else if (topic == COMMAND_DONTDOCUMENT) {
909 database->primaryTree()->addToDontDocumentMap(args[0].first);
910 } else {
911 node = processTopicCommand(doc, topic, args[0]);
912 }
913 if (node != nullptr) {
914 tied.emplace_back(TiedDocumentation{doc, node});
915 }
916 } else if (args.size() > 1) {
917 QList<SharedCommentNode *> sharedCommentNodes;
918 for (const auto &arg : std::as_const(args)) {
919 node = nullptr;
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(), arg.first, arg.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(), arg.first);
931 } else if (isQMLMethodTopic(topic)) {
932 node = parseOtherFuncArg(topic, doc.location(), arg.first);
933 } else {
934 node = processTopicCommand(doc, topic, arg);
935 }
936 if (node != nullptr) {
937 bool found = false;
938 for (SharedCommentNode *scn : sharedCommentNodes) {
939 if (scn->parent() == node->parent()) {
940 scn->append(node);
941 found = true;
942 break;
943 }
944 }
945 if (!found) {
946 auto *scn = new SharedCommentNode(node);
947 sharedCommentNodes.append(scn);
948 tied.emplace_back(TiedDocumentation{doc, scn});
949 }
950 }
951 }
952 for (auto *scn : sharedCommentNodes)
953 scn->sort();
954 }
955 }
956 return std::make_pair(tied, errors);
957}
958
959/*!
960 For each node that is part of C++ API and produces a documentation
961 page, this function ensures that the node belongs to a module.
962 */
964{
965 if (n->physicalModuleName().isEmpty()) {
966 if (n->isInAPI() && !n->name().isEmpty()) {
967 switch (n->nodeType()) {
968 case NodeType::Class:
969 case NodeType::Struct:
970 case NodeType::Union:
973 break;
974 default:
975 return;
976 }
977 n->setPhysicalModuleName(Generator::defaultModuleName());
978 QDocDatabase::qdocDB()->addToModule(Generator::defaultModuleName(), n);
979 n->doc().location().warning(
980 QStringLiteral("Documentation for %1 '%2' has no \\inmodule command; "
981 "using project name by default: %3")
982 .arg(Node::nodeTypeString(n->nodeType()), n->name(),
983 n->physicalModuleName()));
984 }
985 }
986}
987
988void CppCodeParser::processMetaCommands(const std::vector<TiedDocumentation> &tied)
989{
990 for (auto [doc, node] : tied) {
991 processMetaCommands(doc, node);
992 node->setDoc(doc);
993 checkModuleInclusion(node);
994 if (node->isAggregate()) {
995 auto *aggregate = static_cast<Aggregate *>(node);
996
997 if (!aggregate->includeFile()) {
998 Aggregate *parent = aggregate;
999 while (parent->physicalModuleName().isEmpty() && (parent->parent() != nullptr))
1000 parent = parent->parent();
1001
1002 if (parent == aggregate)
1003 // TODO: Understand if the name can be empty.
1004 // In theory it should not be possible as
1005 // there would be no aggregate to refer to
1006 // such that this code is never reached.
1007 //
1008 // If the name can be empty, this would
1009 // endanger users of the include file down the
1010 // line, forcing them to ensure that, further
1011 // to there being an actual include file, that
1012 // include file is not an empty string, such
1013 // that we would require a different way to
1014 // generate the include file here.
1015 aggregate->setIncludeFile(aggregate->name());
1016 else if (aggregate->includeFile())
1017 aggregate->setIncludeFile(*parent->includeFile());
1018 }
1019 }
1020 }
1021}
1022
1023void CppCodeParser::processQmlNativeTypeCommand(Node *node, const QString &cmd, const QString &arg, const Location &location)
1024{
1025 Q_ASSERT(node);
1026 if (!node->isQmlNode()) {
1027 location.warning(
1028 QStringLiteral("Command '\\%1' is only meaningful in '\\%2'")
1029 .arg(cmd, COMMAND_QMLTYPE));
1030 return;
1031 }
1032
1033 auto qmlNode = static_cast<QmlTypeNode *>(node);
1034
1036 auto classNode = database->findClassNode(arg.split(u"::"_s));
1037
1038 if (!classNode) {
1039 if (m_showLinkErrors) {
1040 location.warning(
1041 QStringLiteral("C++ class %2 not found: \\%1 %2")
1042 .arg(cmd, arg));
1043 }
1044 return;
1045 }
1046
1047 if (qmlNode->classNode()) {
1048 location.warning(
1049 QStringLiteral("QML type %1 documented with %2 as its native type. Replacing %2 with %3")
1050 .arg(qmlNode->name(), qmlNode->classNode()->name(), arg));
1051 }
1052
1053 qmlNode->setClassNode(classNode);
1054 classNode->insertQmlNativeType(qmlNode);
1055
1056 if (classNode->isQmlSingleton())
1057 qmlNode->setSingleton(true);
1058}
1059
1060QT_END_NAMESPACE
Access
Definition access.h:11
The ClassNode represents a C++ class.
Definition classnode.h:21
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:273
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:85
static bool generateExamples
Definition config.h:170
static const QString dot
Definition config.h:168
const ExcludedPaths & getExcludedPaths()
Definition config.cpp:1426
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 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:246
The ExternalPageNode represents an external documentation page.
This node is used to represent any kind of function being documented.
void setReimpFlag()
Sets the function node's reimp flag to true, which means the {\reimp} command was used in the qdoc co...
void setOverloadFlag()
const QString & overridesThis() const
void setPrimaryOverloadFlag()
static bool processInternalDocs(const InclusionPolicy &policy)
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:46
This class represents a C++ namespace.
Tree * tree() const override
Returns a pointer to the Tree that contains this NamespaceNode.
void markSeen()
Sets the data member that indicates that the {\namespace} command this NamespaceNode represents has b...
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
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:19
void setNoAutoList(bool b)
Sets the no auto-list flag to b.
Definition pagenode.h:43
void markAttribution()
Definition pagenode.h:50
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.
ClassNode * classNode() override
If this is a QmlTypeNode, this function returns the pointer to the C++ ClassNode that this QML type r...
Definition qmltypenode.h:27
void setSingleton(bool singleton=true)
Definition qmltypenode.h:34
void setClassNode(ClassNode *cn) override
If this is a QmlTypeNode, this function sets the C++ class node to cn.
Definition qmltypenode.h:28
void setOverloadFlags()
Searches the shared comment node's member nodes for function nodes.
const QString & camelCaseModuleName() const
Definition tree.h:72
#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:86
#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_TITLE
Definition codeparser.h:80
#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:85
#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:83
#define COMMAND_INHEADERFILE
Definition codeparser.h:30
#define COMMAND_PREVIOUSPAGE
Definition codeparser.h:46
#define COMMAND_QMLBASICTYPE
Definition codeparser.h:90
#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:82
#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:87
#define COMMAND_INQMLMODULE
Definition codeparser.h:33
#define COMMAND_QMLINSTANTIATES
Definition codeparser.h:58
#define COMMAND_TYPEALIAS
Definition codeparser.h:81
#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:429
#define CONFIG_EXAMPLES
Definition config.h:364
#define CONFIG_EXAMPLEDIRS
Definition config.h:363
#define CONFIG_NOLINKERRORS
Definition config.h:398
#define CONFIG_IMAGEEXTENSIONS
Definition config.h:430
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:44
@ Public
Definition access.h:11
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:210
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:242
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:126
void setAccess(Access t)
Sets the node's access type to t.
Definition node.h:177
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:115
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:133
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:128
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
Definition node.h:131
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:802
NodeType nodeType() const override
Returns this node's type.
Definition node.h:89
bool isStruct() const
Returns true if the node type is Struct.
Definition node.h:130
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:160
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:215
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:138
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:839
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:143
void setTemplateDecl(std::optional< RelaxedTemplateDeclaration > t)
Definition node.h:185
virtual void markReadOnly(bool)
If this node is a QmlPropertyNode, then the property's read-only flag is set to flag.
Definition node.h:213
void setComparisonCategory(const ComparisonCategory &category)
Definition node.h:190
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:238
const std::optional< RelaxedTemplateDeclaration > & templateDecl() const
Definition node.h:253
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:235
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:197
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:107
virtual void markDefault()
If this node is a QmlPropertyNode, it is marked as the default property.
Definition node.h:212
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:119
bool isTypeAlias() const
Returns true if the node type is Typedef.
Definition node.h:132
bool isModule() const
Returns true if the node type is Module.
Definition node.h:114
@ ThreadSafe
Definition node.h:69
@ NonReentrant
Definition node.h:67
@ Reentrant
Definition node.h:68
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:196
virtual bool isInAPI() const
Definition node.h:243
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:150
void setStatus(Status t)
Sets the node's status to t.
Definition node.cpp:510
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:151
void setThreadSafeness(ThreadSafeness t)
Sets the node's thread safeness to t.
Definition node.h:181
@ Deprecated
Definition node.h:58
@ Preliminary
Definition node.h:59
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:125
bool isExample() const
Returns true if the node type is Example.
Definition node.h:105
bool isUnion() const
Returns true if the node type is Union.
Definition node.h:137
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:127
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