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(), [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{
713
714 // Signatures for QML signals require no return type. Parameter list is optional.
715 if (topic.contains("signal"_L1))
716 parsingOpts = parsingOpts | QmlPropertyArguments::ParsingOptions::IgnoreType;
717 else
719
720
721 auto methodArgs = QmlPropertyArguments::parse(funcArg, location, parsingOpts);
722 if (!methodArgs)
723 return nullptr;
724
725 auto *aggregate = findOrCreateQmlType((*methodArgs).m_module,
726 (*methodArgs).m_qmltype, location);
727
728 FunctionNode::Metaness metaness = FunctionNode::getMetanessFromTopic(topic);
729 bool attached = topic.contains("attached"_L1);
730 auto *fn = new FunctionNode(metaness, aggregate, (*methodArgs).m_name, attached);
731 fn->setAccess(Access::Public);
732 fn->setLocation(location);
733 fn->setReturnType((*methodArgs).m_type);
734 fn->setParameters((*methodArgs).m_params);
735 return fn;
736}
737
738/*!
739 Parse the macro arguments in \a macroArg ad hoc, without using
740 any actual parser. If successful, return a pointer to the new
741 FunctionNode for the macro. Otherwise return null. \a location
742 is used for reporting errors.
743 */
744FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QString &macroArg)
745{
747
748 QStringList leftParenSplit = macroArg.split('(');
749 if (leftParenSplit.isEmpty())
750 return nullptr;
751 QString macroName;
752 FunctionNode *oldMacroNode = nullptr;
753 QStringList blankSplit = leftParenSplit[0].split(' ');
754 if (!blankSplit.empty()) {
755 macroName = blankSplit.last();
756 oldMacroNode = database->findMacroNode(macroName);
757 }
758 QString returnType;
759 if (blankSplit.size() > 1) {
760 blankSplit.removeLast();
761 returnType = blankSplit.join(' ');
762 }
763 QString params;
764 if (leftParenSplit.size() > 1) {
765 params = QString("");
766 const QString &afterParen = leftParenSplit.at(1);
767 qsizetype rightParen = afterParen.indexOf(')');
768 if (rightParen >= 0)
769 params = afterParen.left(rightParen);
770 }
771 int i = 0;
772 while (i < macroName.size() && !macroName.at(i).isLetter())
773 i++;
774 if (i > 0) {
775 returnType += QChar(' ') + macroName.left(i);
776 macroName = macroName.mid(i);
777 }
779 if (params.isNull())
781 auto *macro = new FunctionNode(metaness, database->primaryTreeRoot(), macroName);
782 macro->setAccess(Access::Public);
783 macro->setLocation(location);
784 macro->setReturnType(returnType);
785 macro->setParameters(params);
786 if (oldMacroNode && macro->parent() == oldMacroNode->parent()
787 && compare(macro, oldMacroNode) == 0) {
788 location.warning(QStringLiteral("\\macro %1 documented more than once")
789 .arg(macroArg), QStringLiteral("also seen here: %1")
790 .arg(oldMacroNode->doc().location().toString()));
791 }
792 return macro;
793}
794
795void CppCodeParser::setExampleFileLists(ExampleNode *en)
796{
797 Config &config = Config::instance();
798 QString fullPath = config.getExampleProjectFile(en->name());
799 if (fullPath.isEmpty()) {
800 QString details = QLatin1String("Example directories: ")
801 + config.getCanonicalPathList(CONFIG_EXAMPLEDIRS).join(QLatin1Char(' '));
802 en->location().warning(
803 QStringLiteral("Cannot find project file for example '%1'").arg(en->name()),
804 details);
805 return;
806 }
807
808 QDir exampleDir(QFileInfo(fullPath).dir());
809
810 const auto& [excludeDirs, excludeFiles] = config.getExcludedPaths();
811
812 QStringList exampleFiles = Config::getFilesHere(exampleDir.path(), m_exampleNameFilter,
813 Location(), excludeDirs, excludeFiles);
814 // Search for all image files under the example project, excluding doc/images directory.
815 QSet<QString> excludeDocDirs(excludeDirs);
816 excludeDocDirs.insert(exampleDir.path() + QLatin1String("/doc/images"));
817 QStringList imageFiles = Config::getFilesHere(exampleDir.path(), m_exampleImageFilter,
818 Location(), excludeDocDirs, excludeFiles);
819 if (!exampleFiles.isEmpty()) {
820 // move main.cpp to the end, if it exists
821 QString mainCpp;
822
823 const auto isGeneratedOrMainCpp = [&mainCpp](const QString &fileName) {
824 if (fileName.endsWith("/main.cpp")) {
825 if (mainCpp.isEmpty())
826 mainCpp = fileName;
827 return true;
828 }
829 return Utilities::isGeneratedFile(fileName);
830 };
831
832 exampleFiles.erase(
833 std::remove_if(exampleFiles.begin(), exampleFiles.end(), isGeneratedOrMainCpp),
834 exampleFiles.end());
835
836 if (!mainCpp.isEmpty())
837 exampleFiles.append(mainCpp);
838
839 // Add any resource and project files
840 exampleFiles += Config::getFilesHere(exampleDir.path(),
841 QLatin1String("*.qrc *.pro *.qmlproject *.pyproject CMakeLists.txt qmldir"),
842 Location(), excludeDirs, excludeFiles);
843 }
844
845 const qsizetype pathLen = exampleDir.path().size() - en->name().size();
846 for (auto &file : exampleFiles)
847 file = file.mid(pathLen);
848 for (auto &file : imageFiles)
849 file = file.mid(pathLen);
850
851 en->setFiles(exampleFiles, fullPath.mid(pathLen));
852 en->setImages(imageFiles);
853}
854
855/*!
856 returns true if \a t is \e {qmlsignal}, \e {qmlmethod},
857 \e {qmlattachedsignal}, or \e {qmlattachedmethod}.
858 */
859bool CppCodeParser::isQMLMethodTopic(const QString &t)
860{
863}
864
865/*!
866 Returns true if \a t is \e {qmlproperty}, \e {qmlpropertygroup},
867 or \e {qmlattachedproperty}.
868 */
869bool CppCodeParser::isQMLPropertyTopic(const QString &t)
870{
872}
873
874std::pair<std::vector<TiedDocumentation>, std::vector<FnMatchError>>
875CppCodeParser::processTopicArgs(const UntiedDocumentation &untied)
876{
877 const Doc &doc = untied.documentation;
878
879 if (doc.topicsUsed().isEmpty())
880 return {};
881
882 QDocDatabase *database = QDocDatabase::qdocDB();
883
884 const QString topic = doc.topicsUsed().first().m_topic;
885
886 std::vector<TiedDocumentation> tied{};
887 std::vector<FnMatchError> errors{};
888
889 if (isQMLPropertyTopic(topic)) {
890 auto tied_qml = processQmlProperties(untied);
891 tied.insert(tied.end(), tied_qml.begin(), tied_qml.end());
892 } else {
893 ArgList args = doc.metaCommandArgs(topic);
894 Node *node = nullptr;
895 if (args.size() == 1) {
896 if (topic == COMMAND_FN) {
897 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
898 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
899 auto result = fn_parser(doc.location(), args[0].first, args[0].second, untied.context);
900 if (auto *error = std::get_if<FnMatchError>(&result))
901 errors.emplace_back(*error);
902 else
903 node = std::get<Node*>(result);
904 }
905 } else if (topic == COMMAND_MACRO) {
906 node = parseMacroArg(doc.location(), args[0].first);
907 } else if (isQMLMethodTopic(topic)) {
908 node = parseOtherFuncArg(topic, doc.location(), args[0].first);
909 } else if (topic == COMMAND_DONTDOCUMENT) {
910 database->primaryTree()->addToDontDocumentMap(args[0].first);
911 } else {
912 node = processTopicCommand(doc, topic, args[0]);
913 }
914 if (node != nullptr) {
915 tied.emplace_back(TiedDocumentation{doc, node});
916 }
917 } else if (args.size() > 1) {
918 // Find nodes for each of the topic commands and add them to shared
919 // comment nodes.
920 QList<SharedCommentNode *> sharedCommentNodes;
921 for (const auto &arg : std::as_const(args)) {
922 node = nullptr;
923 if (topic == COMMAND_FN) {
924 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
925 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
926 auto result = fn_parser(doc.location(), arg.first, arg.second, untied.context);
927 if (auto *error = std::get_if<FnMatchError>(&result))
928 errors.emplace_back(*error);
929 else
930 node = std::get<Node*>(result);
931 }
932 } else if (topic == COMMAND_MACRO) {
933 node = parseMacroArg(doc.location(), arg.first);
934 } else if (isQMLMethodTopic(topic)) {
935 node = parseOtherFuncArg(topic, doc.location(), arg.first);
936 } else {
937 node = processTopicCommand(doc, topic, arg);
938 }
939 if (node != nullptr) {
940 bool found = false;
941 for (SharedCommentNode *scn : sharedCommentNodes) {
942 if (scn->parent() == node->parent()) {
943 scn->append(node);
944 found = true;
945 break;
946 }
947 }
948 if (!found) {
949 auto *scn = new SharedCommentNode(node);
950 sharedCommentNodes.append(scn);
951 tied.emplace_back(TiedDocumentation{doc, scn});
952 }
953 }
954 }
955 for (auto *scn : sharedCommentNodes) {
956 // Don't sort function nodes - preserve the order from \fn commands
957 // for position-dependent \overload primary behavior
958 if (!scn->collective().isEmpty() && !scn->collective().first()->isFunction())
959 scn->sort();
960 }
961 }
962 }
963 return std::make_pair(tied, errors);
964}
965
966/*!
967 For each node that is part of C++ API and produces a documentation
968 page, this function ensures that the node belongs to a module.
969 */
971{
972 if (n->physicalModuleName().isEmpty()) {
973 if (n->isInAPI() && !n->name().isEmpty()) {
974 switch (n->nodeType()) {
975 case NodeType::Class:
976 case NodeType::Struct:
977 case NodeType::Union:
980 break;
981 default:
982 return;
983 }
984 n->setPhysicalModuleName(Generator::defaultModuleName());
985 QDocDatabase::qdocDB()->addToModule(Generator::defaultModuleName(), n);
986 n->doc().location().warning(
987 QStringLiteral("Documentation for %1 '%2' has no \\inmodule command; "
988 "using project name by default: %3")
989 .arg(Node::nodeTypeString(n->nodeType()), n->name(),
990 n->physicalModuleName()));
991 }
992 }
993}
994
995void CppCodeParser::processMetaCommands(const std::vector<TiedDocumentation> &tied)
996{
997 for (auto [doc, node] : tied) {
998 node->setDoc(doc);
999 processMetaCommands(doc, node);
1000 checkModuleInclusion(node);
1001 if (node->isAggregate()) {
1002 auto *aggregate = static_cast<Aggregate *>(node);
1003
1004 if (!aggregate->includeFile()) {
1005 const QString className = aggregate->name();
1006
1007 // Resolution priority:
1008 // 1. Convenience header (if exists in include paths)
1009 // 2. Include-relative path from declLocation
1010 // 3. Class name as last resort (only if non-empty)
1011 QString includeFile = convenienceHeaderExists(className)
1012 ? className
1013 : computeIncludeSpelling(aggregate->declLocation());
1014
1015 if (includeFile.isEmpty() && !className.isEmpty())
1016 includeFile = className;
1017
1018 if (!includeFile.isEmpty())
1019 aggregate->setIncludeFile(includeFile);
1020 }
1021 }
1022 }
1023}
1024
1025void CppCodeParser::processQmlNativeTypeCommand(Node *node, const QString &cmd, const QString &arg, const Location &location)
1026{
1027 Q_ASSERT(node);
1028 if (!node->isQmlNode()) {
1029 location.warning(
1030 QStringLiteral("Command '\\%1' is only meaningful in '\\%2'")
1031 .arg(cmd, COMMAND_QMLTYPE));
1032 return;
1033 }
1034
1035 auto qmlNode = static_cast<QmlTypeNode *>(node);
1036
1038 auto classNode = database->findClassNode(arg.split(u"::"_s));
1039
1040 if (!classNode) {
1041 if (!Config::instance().get(CONFIG_NOLINKERRORS).asBool()) {
1042 location.warning(
1043 QStringLiteral("C++ class %2 not found: \\%1 %2")
1044 .arg(cmd, arg));
1045 }
1046 return;
1047 }
1048
1049 if (qmlNode->classNode()) {
1050 location.warning(
1051 QStringLiteral("QML type %1 documented with %2 as its native type. Replacing %2 with %3")
1052 .arg(qmlNode->name(), qmlNode->classNode()->name(), arg));
1053 }
1054
1055 qmlNode->setClassNode(classNode);
1056 classNode->insertQmlNativeType(qmlNode);
1057
1058 if (classNode->isQmlSingleton())
1059 qmlNode->setSingleton(true);
1060}
1061
1062namespace {
1063
1064/*!
1065 Strips compiler include path prefixes (-I, -isystem, etc.) from a path.
1066 Returns an empty string if the path is an unrecognized flag.
1067*/
1068QString stripIncludePrefix(const QString &path)
1069{
1070 QString result = path.trimmed();
1071
1072 static const QStringList prefixes = {
1073 "-I"_L1, "-isystem"_L1, "-iquote"_L1, "-idirafter"_L1
1074 };
1075
1076 for (const QString &prefix : prefixes) {
1077 if (result.startsWith(prefix)) {
1078 result = result.mid(prefix.size()).trimmed();
1079 return QDir::cleanPath(result);
1080 }
1081 }
1082
1083 // Skip framework paths and other unrecognized flags
1084 if (result.startsWith(u'-'))
1085 return {};
1086
1087 return QDir::cleanPath(result);
1088}
1089
1090} // anonymous namespace
1091
1092/*!
1093 Returns the cached list of cleaned include paths, combining both
1094 command-line and qdocconf include paths with prefixes stripped.
1095*/
1096const QStringList &CppCodeParser::getCleanIncludePaths() const
1097{
1098 if (!m_includePathsCached) {
1099 // Combine command-line and qdocconf include paths
1100 QStringList rawPaths = Config::instance().includePaths();
1101 rawPaths += Config::instance().getCanonicalPathList(
1102 CONFIG_INCLUDEPATHS, Config::IncludePaths);
1103
1104 for (const QString &path : rawPaths) {
1105 QString clean = stripIncludePrefix(path);
1106 if (!clean.isEmpty() && !m_cleanIncludePaths.contains(clean))
1107 m_cleanIncludePaths.append(clean);
1108 }
1109 m_includePathsCached = true;
1110 }
1111 return m_cleanIncludePaths;
1112}
1113
1114/*!
1115 Checks if a convenience header (extensionless file matching the class name)
1116 exists in any of the configured include paths. Results are cached.
1117
1118 This supports Qt's convention where classes like QString have convenience
1119 headers (extensionless files) that redirect to the actual header.
1120*/
1121bool CppCodeParser::convenienceHeaderExists(const QString &className) const
1122{
1123 if (className.isEmpty())
1124 return false;
1125
1126 auto it = m_convenienceHeaderCache.constFind(className);
1127 if (it != m_convenienceHeaderCache.constEnd())
1128 return *it;
1129
1130 bool exists = false;
1131 for (const QString &includePath : getCleanIncludePaths()) {
1132 QFileInfo candidate(includePath + u'/' + className);
1133 if (candidate.exists() && candidate.isFile()) {
1134 exists = true;
1135 break;
1136 }
1137 }
1138
1139 m_convenienceHeaderCache.insert(className, exists);
1140 return exists;
1141}
1142
1143/*!
1144 Returns the basename of the header file from the declaration location.
1145
1146 This provides a simple, reliable include spelling that works regardless
1147 of the working directory or include path configuration. For more complex
1148 include paths (like subdirectories), users can use \\inheaderfile.
1149*/
1150QString CppCodeParser::computeIncludeSpelling(const Location &loc) const
1151{
1152 if (loc.isEmpty())
1153 return {};
1154
1155 return loc.fileName();
1156}
1157
1158QT_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:282
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:1441
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.
static 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.
static 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:32
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:451
#define CONFIG_EXAMPLES
Definition config.h:382
#define CONFIG_EXAMPLEDIRS
Definition config.h:381
#define CONFIG_NOLINKERRORS
Definition config.h:418
#define CONFIG_IMAGEEXTENSIONS
Definition config.h:452
#define CONFIG_INCLUDEPATHS
Definition config.h:401
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:27
NodeType
Definition genustypes.h:150
This namespace holds QDoc-internal utility methods.
Definition utilities.h:23
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
virtual 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 and QML method 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