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 Aggregate::PropertySearchType searchType = attached ? Aggregate::AttachedProperties
269 : Aggregate::UnattachedProperties;
270 QmlPropertyNode *existingProperty = qmlType->hasQmlProperty(qpa->m_name, searchType);
271 if (existingProperty) {
272 processMetaCommands(doc, existingProperty);
273 if (!doc.body().isEmpty()) {
274 doc.startLocation().warning(
275 QStringLiteral("QML property documented multiple times: '%1'")
276 .arg(topicCommand.m_args), QStringLiteral("also seen here: %1")
277 .arg(existingProperty->location().toString()));
278 }
279 continue;
280 }
281 auto *qpn = new QmlPropertyNode(qmlType, qpa->m_name, qpa->m_type, attached);
282 qpn->setIsList(qpa->m_isList);
283 qpn->setLocation(doc.startLocation());
284 qpn->setGenus(Genus::QML);
285
286 tied.emplace_back(TiedDocumentation{doc, qpn});
287
288 sharedNodes << qpn;
289 }
290 } else {
291 doc.startLocation().warning(
292 QStringLiteral("Command '\\%1'; not allowed with QML property commands")
293 .arg(cmd));
294 }
295 }
296
297 // Construct a SharedCommentNode (scn) if multiple topics generated
298 // valid nodes. Note that it's important to do this *after* constructing
299 // the topic nodes - which need to be written to index before the related
300 // scn.
301 if (sharedNodes.size() > 1) {
302 // Resolve QML property group identifier (if any) from the first topic
303 // command arguments.
304 QString group;
305 if (auto dot = (*firstTopicArgs).m_name.indexOf('.'_L1); dot != -1)
306 group = (*firstTopicArgs).m_name.left(dot);
307 auto *scn = new SharedCommentNode(qmlType, sharedNodes.size(), group);
308 scn->setLocation(doc.startLocation());
309
310 tied.emplace_back(TiedDocumentation{doc, scn});
311
312 for (const auto n : sharedNodes)
313 scn->append(n);
314 scn->sort();
315 }
316
317 return tied;
318}
319
320/*!
321 Process the metacommand \a command in the context of the
322 \a node associated with the topic command and the \a doc.
323 \a arg is the argument to the metacommand.
324
325 \a node is guaranteed to be non-null.
326 */
327void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
328 const ArgPair &argPair, Node *node)
329{
331
332 QString arg = argPair.first;
333 if (command == COMMAND_INHEADERFILE) {
334 // TODO: [incorrect-constructs][header-arg]
335 // The emptiness check for arg is required as,
336 // currently, DocParser fancies passing (without any warning)
337 // incorrect constructs doen the chain, such as an
338 // "\inheaderfile" command with no argument.
339 //
340 // As it is the case here, we require further sanity checks to
341 // preserve some of the semantic for the later phases.
342 // This generally has a ripple effect on the whole codebase,
343 // making it more complex and increasesing the surface of bugs.
344 //
345 // The following emptiness check should be removed as soon as
346 // DocParser is enhanced with correct semantics.
347 if (node->isAggregate() && !arg.isEmpty())
348 static_cast<Aggregate *>(node)->setIncludeFile(arg);
349 else
350 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_INHEADERFILE));
351 } else if (command == COMMAND_COMPARES) {
352 processComparesCommand(node, arg, doc.location());
353 } else if (command == COMMAND_COMPARESWITH) {
354 if (!node->isClassNode())
355 doc.location().warning(
356 u"Found \\%1 command outside of \\%2 context."_s
358 } else if (command == COMMAND_OVERLOAD) {
359 /*
360 Note that this might set the overload flag of the
361 primary function. This is ok because the overload
362 flags and overload numbers will be resolved later
363 in Aggregate::normalizeOverloads().
364 */
365 processOverloadCommand(node, doc);
366 } else if (command == COMMAND_REIMP) {
367 if (node->parent() && !node->parent()->isInternal()) {
368 if (node->isFunction()) {
369 auto *fn = static_cast<FunctionNode *>(node);
370 // The clang visitor class will have set the
371 // qualified name of the overridden function.
372 // If the name of the overridden function isn't
373 // set, issue a warning.
374 if (fn->overridesThis().isEmpty() && CodeParser::isWorthWarningAbout(doc)) {
375 doc.location().warning(
376 QStringLiteral("Cannot find base function for '\\%1' in %2()")
377 .arg(COMMAND_REIMP, node->name()),
378 QStringLiteral("The function either doesn't exist in any "
379 "base class with the same signature or it "
380 "exists but isn't virtual."));
381 }
382 fn->setReimpFlag();
383 } else {
384 doc.location().warning(
385 QStringLiteral("Ignored '\\%1' in %2").arg(COMMAND_REIMP, node->name()));
386 }
387 }
388 } else if (command == COMMAND_RELATES) {
389 // REMARK: Generates warnings only; Node instances are
390 // adopted from the root namespace to other Aggregates
391 // in a post-processing step, Aggregate::resolveRelates(),
392 // after all topic commands are processed.
393 if (node->isAggregate()) {
394 doc.location().warning("Invalid '\\%1' not allowed in '\\%2'"_L1
395 .arg(COMMAND_RELATES, node->nodeTypeString()));
396 }
397 } else if (command == COMMAND_NEXTPAGE) {
398 CodeParser::setLink(node, Node::NextLink, arg);
399 } else if (command == COMMAND_PREVIOUSPAGE) {
400 CodeParser::setLink(node, Node::PreviousLink, arg);
401 } else if (command == COMMAND_STARTPAGE) {
402 CodeParser::setLink(node, Node::StartLink, arg);
403 } else if (command == COMMAND_QMLINHERITS) {
404 if (node->name() == arg)
405 doc.location().warning(QStringLiteral("%1 tries to inherit itself").arg(arg));
406 else if (node->isQmlType()) {
407 auto *qmlType = static_cast<QmlTypeNode *>(node);
408 qmlType->setQmlBaseName(arg);
409 }
410 } else if (command == COMMAND_QMLNATIVETYPE || command == COMMAND_QMLINSTANTIATES) {
411 if (command == COMMAND_QMLINSTANTIATES)
412 doc.location().report(
413 u"\\instantiates is deprecated and will be removed in a future version. Use \\nativetype instead."_s);
414 // TODO: COMMAND_QMLINSTANTIATES is deprecated since 6.8. Its remains should be removed no later than Qt 7.0.0.
415 processQmlNativeTypeCommand(node, command, arg, doc.location());
416 } else if (command == COMMAND_DEFAULT) {
417 if (!node->isQmlProperty()) {
418 doc.location().warning(QStringLiteral("Ignored '\\%1', applies only to '\\%2'")
419 .arg(command, COMMAND_QMLPROPERTY));
420 } else if (arg.isEmpty()) {
421 doc.location().warning(QStringLiteral("Expected an argument for '\\%1' (maybe you meant '\\%2'?)")
422 .arg(command, COMMAND_QMLDEFAULT));
423 } else {
424 static_cast<QmlPropertyNode *>(node)->setDefaultValue(arg);
425 }
426 } else if (command == COMMAND_QMLDEFAULT) {
427 node->markDefault();
428 } else if (command == COMMAND_QMLENUMERATORSFROM) {
429 NativeEnum *nativeEnum{nullptr};
430 if (auto *ne_if = dynamic_cast<NativeEnumInterface *>(node))
431 nativeEnum = ne_if->nativeEnum();
432 else {
433 doc.location().warning("Ignored '\\%1', applies only to '\\%2' and '\\%3'"_L1
434 .arg(command, COMMAND_QMLPROPERTY, COMMAND_QMLENUM));
435 return;
436 }
437 if (!nativeEnum->resolve(argPair.first, argPair.second)) {
438 doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
439 .arg(command, arg), "Use \\value commands instead"_L1);
440 }
441 } else if (command == COMMAND_QMLREADONLY) {
442 node->markReadOnly(true);
443 } else if (command == COMMAND_QMLREQUIRED) {
444 if (!node->isQmlProperty())
445 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_QMLREQUIRED));
446 else
447 static_cast<QmlPropertyNode *>(node)->setRequired();
448 } else if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
449 if (node->isQmlType())
450 node->setAbstract(true);
451 } else if (command == COMMAND_DEPRECATED) {
452 node->setDeprecated(argPair.second);
453 } else if (command == COMMAND_INGROUP || command == COMMAND_INPUBLICGROUP) {
454 // Note: \ingroup and \inpublicgroup are the same (and now recognized as such).
455 database->addToGroup(arg, node);
456 } else if (command == COMMAND_INMODULE) {
457 database->addToModule(arg, node);
458 } else if (command == COMMAND_INQMLMODULE) {
459 // Handled when parsing topic commands
460 } else if (command == COMMAND_OBSOLETE) {
462 } else if (command == COMMAND_NONREENTRANT) {
464 } else if (command == COMMAND_PRELIMINARY) {
465 // \internal wins.
466 if (!node->isInternal())
468 } else if (command == COMMAND_INTERNAL) {
470 } else if (command == COMMAND_REENTRANT) {
472 } else if (command == COMMAND_SINCE) {
473 node->setSince(arg);
474 } else if (command == COMMAND_WRAPPER) {
475 node->setWrapper();
476 } else if (command == COMMAND_THREADSAFE) {
478 } else if (command == COMMAND_SUBTITLE) {
479 if (!node->setSubtitle(arg))
480 doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
481 } else if (command == COMMAND_QTVARIABLE) {
482 node->setQtVariable(arg);
483 if (!node->isModule() && !node->isQmlModule())
484 doc.location().warning(
485 QStringLiteral(
486 "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
487 .arg(COMMAND_QTVARIABLE));
488 } else if (command == COMMAND_QTCMAKEPACKAGE) {
489 if (node->isModule())
490 node->setCMakeComponent(arg);
491 else
492 doc.location().warning(
493 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
495 } else if (command == COMMAND_QTCMAKETARGETITEM) {
496 if (node->isModule())
497 node->setCMakeTargetItem(QLatin1String("Qt6::") + arg);
498 else
499 doc.location().warning(
500 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
502 } else if (command == COMMAND_CMAKEPACKAGE) {
503 if (node->isModule())
504 node->setCMakePackage(arg);
505 else
506 doc.location().warning(
507 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
509 } else if (command == COMMAND_CMAKECOMPONENT) {
510 if (node->isModule())
511 node->setCMakeComponent(arg);
512 else
513 doc.location().warning(
514 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
516 } else if (command == COMMAND_CMAKETARGETITEM) {
517 if (node->isModule())
518 node->setCMakeTargetItem(arg);
519 else
520 doc.location().warning(
521 QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
523 } else if (command == COMMAND_MODULESTATE) {
524 if (!node->isModule() && !node->isQmlModule()) {
525 doc.location().warning(
526 QStringLiteral(
527 "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
528 .arg(COMMAND_MODULESTATE));
529 } else if (!node->isPreliminary() && !node->isInternal()) {
530 static_cast<CollectionNode*>(node)->setState(arg);
531 }
532 } else if (command == COMMAND_NOAUTOLIST) {
533 if (!node->isCollectionNode() && !node->isExample()) {
534 doc.location().warning(
535 QStringLiteral(
536 "Command '\\%1' is only meaningful in '\\module', '\\qmlmodule', `\\group` and `\\example`.")
537 .arg(COMMAND_NOAUTOLIST));
538 } else {
539 static_cast<PageNode*>(node)->setNoAutoList(true);
540 }
541 } else if (command == COMMAND_ATTRIBUTION) {
542 // TODO: This condition is not currently exact enough, as it
543 // will allow any non-aggregate `PageNode` to use the command,
544 // For example, an `ExampleNode`.
545 //
546 // The command is intended only for internal usage by
547 // "qattributionscanner" and should only work on `PageNode`s
548 // that are generated from a "\page" command.
549 //
550 // It is already possible to provide a more restricted check,
551 // albeit in a somewhat dirty way. It is not expected that
552 // this warning will have any particular use.
553 // If it so happens that a case where the too-broad scope of
554 // the warning is a problem or hides a bug, modify the
555 // condition to be restrictive enough.
556 // Otherwise, wait until a more torough look at QDoc's
557 // internal representations an way to enable "Attribution
558 // Pages" is performed before looking at the issue again.
559 if (!node->isTextPageNode()) {
560 doc.location().warning(u"Command '\\%1' is only meaningful in '\\%2'"_s.arg(COMMAND_ATTRIBUTION, COMMAND_PAGE));
561 } else { static_cast<PageNode*>(node)->markAttribution(); }
562 }
563}
564
565/*!
566 \internal
567 Processes the argument \a arg that's passed to the \\compares command,
568 and sets the comparison category of the \a node accordingly.
569
570 If the argument is invalid, issue a warning at the location the command
571 appears through \a loc.
572*/
573void CppCodeParser::processComparesCommand(Node *node, const QString &arg, const Location &loc)
574{
575 if (!node->isClassNode()) {
576 loc.warning(u"Found \\%1 command outside of \\%2 context."_s.arg(COMMAND_COMPARES,
578 return;
579 }
580
581 if (auto category = comparisonCategoryFromString(arg.toStdString());
582 category != ComparisonCategory::None) {
583 node->setComparisonCategory(category);
584 } else {
585 loc.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(COMMAND_COMPARES, arg),
586 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
587 }
588}
589
590/*!
591 Processes the \\overload command for the given \a node and \a doc.
592 Handles both regular overloads and primary overloads (\\overload primary).
593 Issues warnings for multiple primary overloads with location references.
594*/
595void CppCodeParser::processOverloadCommand(Node *node, const Doc &doc)
596{
597 if (node->isFunction()) {
598 auto *fn = static_cast<FunctionNode *>(node);
599
600 // If this function is part of a SharedCommentNode, skip processing here.
601 // The SharedCommentNode will handle \overload primary position-dependently.
602 if (fn->sharedCommentNode())
603 return;
604
605 // Check if this is "\overload primary"
606 const auto &overloadArgs = doc.overloadList();
607 if (!overloadArgs.isEmpty()
608 && overloadArgs.first().first == "__qdoc_primary_overload__"_L1) {
609
610 // Note: We don't check for duplicate primary overloads here because
611 // Doc locations may not be fully initialized yet, especially for
612 // shared comment nodes with multiple \fn commands.
613 // The check is done later in Aggregate::normalizeOverloads().
614
615 fn->setPrimaryOverloadFlag();
616 // Primary overloads are still overloads, so set both flags
617 fn->setOverloadFlag();
618 } else {
619 fn->setOverloadFlag();
620 }
621 } else if (node->isSharedCommentNode()) {
622 static_cast<SharedCommentNode *>(node)->setOverloadFlags();
623 } else {
624 doc.location().warning("Ignored '\\%1'"_L1.arg(COMMAND_OVERLOAD));
625 }
626}
627
628/*!
629 The topic command has been processed, and now \a doc and
630 \a node are passed to this function to get the metacommands
631 from \a doc and process them one at a time. \a node is the
632 node where \a doc resides.
633 */
635{
636 std::vector<Node*> nodes_to_process{};
637 if (node->isSharedCommentNode()) {
638 auto scn = static_cast<SharedCommentNode*>(node);
639
640 nodes_to_process.reserve(scn->count() + 1);
641 std::copy(scn->collective().cbegin(), scn->collective().cend(), std::back_inserter(nodes_to_process));
642 }
643
644 // REMARK: Ordering is important here. If node is a
645 // SharedCommentNode it MUST be processed after all its child
646 // nodes.
647 // Failure to do so can incur in incorrect warnings.
648 // For example, if a shared documentation has a "\relates" command.
649 // When the command is processed for the SharedCommentNode it will
650 // apply to all its child nodes.
651 // If a child node is processed after the SharedCommentNode that
652 // contains it, that "\relates" command will be considered applied
653 // already, resulting in a warning.
654 nodes_to_process.push_back(node);
655
656 const QStringList metaCommandsUsed = doc.metaCommandsUsed().values();
657 for (const auto &command : metaCommandsUsed) {
658 const ArgList args = doc.metaCommandArgs(command);
659 for (const auto &arg : args) {
660 std::for_each(nodes_to_process.cbegin(), nodes_to_process.cend(), [doc, command, arg](auto node){
661 processMetaCommand(doc, command, arg, node);
662 });
663 }
664 }
665
666 // Apply a title (stripped of formatting) to the Node if set
667 if (!doc.title().isEmpty()) {
668 if (!node->setTitle(doc.title().toString()))
669 doc.location().warning(QStringLiteral("Ignored '\\title'"));
670 if (node->isExample())
671 QDocDatabase::qdocDB()->addExampleNode(static_cast<ExampleNode *>(node));
672 }
673}
674
675/*!
676 Creates an EnumNode instance explicitly for the \qmlenum command.
677 Utilizes QmlPropertyArguments for argument (\a arg) parsing.
678
679 Adds a list of \a enumItemNames as enumerators to facilitate linking
680 via enumerator names.
681*/
682EnumNode *CppCodeParser::processQmlEnumTopic(const QStringList &enumItemNames,
683 const Location &location, const QString &arg)
684{
685 if (arg.isEmpty()) {
686 location.warning(u"Missing argument to \\%1 command."_s.arg(COMMAND_QMLENUM));
687 return nullptr;
688 }
689
690 auto parsedArgs = QmlPropertyArguments::parse(arg, location,
693
694 if (!parsedArgs)
695 return nullptr;
696
697 auto *qmlType = findOrCreateQmlType((*parsedArgs).m_module, (*parsedArgs).m_qmltype, location);
698
699 auto *enumNode = new QmlEnumNode(qmlType, (*parsedArgs).m_name);
700 enumNode->setLocation(location);
701
702 for (const auto &item : enumItemNames)
703 enumNode->addItem(EnumItem(item, 0));
704
705 return enumNode;
706}
707
708/*!
709 Parse QML signal/method topic commands.
710 */
711FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Location &location,
712 const QString &funcArg)
713{
715
716 // Signatures for QML signals require no return type. Parameter list is optional.
717 if (topic.contains("signal"_L1))
718 parsingOpts = parsingOpts | QmlPropertyArguments::ParsingOptions::IgnoreType;
719 else
721
722
723 auto methodArgs = QmlPropertyArguments::parse(funcArg, location, parsingOpts);
724 if (!methodArgs)
725 return nullptr;
726
727 auto *aggregate = findOrCreateQmlType((*methodArgs).m_module,
728 (*methodArgs).m_qmltype, location);
729
730 FunctionNode::Metaness metaness = FunctionNode::getMetanessFromTopic(topic);
731 bool attached = topic.contains("attached"_L1);
732 auto *fn = new FunctionNode(metaness, aggregate, (*methodArgs).m_name, attached);
733 fn->setAccess(Access::Public);
734 fn->setLocation(location);
735 fn->setReturnType((*methodArgs).m_type);
736 fn->setParameters((*methodArgs).m_params);
737 return fn;
738}
739
740/*!
741 Parse the macro arguments in \a macroArg ad hoc, without using
742 any actual parser. If successful, return a pointer to the new
743 FunctionNode for the macro. Otherwise return null. \a location
744 is used for reporting errors.
745 */
746FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QString &macroArg)
747{
749
750 QStringList leftParenSplit = macroArg.split('(');
751 if (leftParenSplit.isEmpty())
752 return nullptr;
753 QString macroName;
754 FunctionNode *oldMacroNode = nullptr;
755 QStringList blankSplit = leftParenSplit[0].split(' ');
756 if (!blankSplit.empty()) {
757 macroName = blankSplit.last();
758 oldMacroNode = database->findMacroNode(macroName);
759 }
760 QString returnType;
761 if (blankSplit.size() > 1) {
762 blankSplit.removeLast();
763 returnType = blankSplit.join(' ');
764 }
765 QString params;
766 if (leftParenSplit.size() > 1) {
767 params = QString("");
768 const QString &afterParen = leftParenSplit.at(1);
769 qsizetype rightParen = afterParen.indexOf(')');
770 if (rightParen >= 0)
771 params = afterParen.left(rightParen);
772 }
773 int i = 0;
774 while (i < macroName.size() && !macroName.at(i).isLetter())
775 i++;
776 if (i > 0) {
777 returnType += QChar(' ') + macroName.left(i);
778 macroName = macroName.mid(i);
779 }
781 if (params.isNull())
783 auto *macro = new FunctionNode(metaness, database->primaryTreeRoot(), macroName);
784 macro->setAccess(Access::Public);
785 macro->setLocation(location);
786 macro->setReturnType(returnType);
787 macro->setParameters(params);
788 if (oldMacroNode && macro->parent() == oldMacroNode->parent()
789 && compare(macro, oldMacroNode) == 0) {
790 location.warning(QStringLiteral("\\macro %1 documented more than once")
791 .arg(macroArg), QStringLiteral("also seen here: %1")
792 .arg(oldMacroNode->doc().location().toString()));
793 }
794 return macro;
795}
796
797void CppCodeParser::setExampleFileLists(ExampleNode *en)
798{
799 Config &config = Config::instance();
800 QString fullPath = config.getExampleProjectFile(en->name());
801 if (fullPath.isEmpty()) {
802 QString details = QLatin1String("Example directories: ")
803 + config.getCanonicalPathList(CONFIG_EXAMPLEDIRS).join(QLatin1Char(' '));
804 en->location().warning(
805 QStringLiteral("Cannot find project file for example '%1'").arg(en->name()),
806 details);
807 return;
808 }
809
810 QDir exampleDir(QFileInfo(fullPath).dir());
811
812 const auto& [excludeDirs, excludeFiles] = config.getExcludedPaths();
813
814 QStringList exampleFiles = Config::getFilesHere(exampleDir.path(), m_exampleNameFilter,
815 Location(), excludeDirs, excludeFiles);
816 // Search for all image files under the example project, excluding doc/images directory.
817 QSet<QString> excludeDocDirs(excludeDirs);
818 excludeDocDirs.insert(exampleDir.path() + QLatin1String("/doc/images"));
819 QStringList imageFiles = Config::getFilesHere(exampleDir.path(), m_exampleImageFilter,
820 Location(), excludeDocDirs, excludeFiles);
821 if (!exampleFiles.isEmpty()) {
822 // move main.cpp to the end, if it exists
823 QString mainCpp;
824
825 const auto isGeneratedOrMainCpp = [&mainCpp](const QString &fileName) {
826 if (fileName.endsWith("/main.cpp")) {
827 if (mainCpp.isEmpty())
828 mainCpp = fileName;
829 return true;
830 }
831 return Utilities::isGeneratedFile(fileName);
832 };
833
834 exampleFiles.erase(
835 std::remove_if(exampleFiles.begin(), exampleFiles.end(), isGeneratedOrMainCpp),
836 exampleFiles.end());
837
838 if (!mainCpp.isEmpty())
839 exampleFiles.append(mainCpp);
840
841 // Add any resource and project files
842 exampleFiles += Config::getFilesHere(exampleDir.path(),
843 QLatin1String("*.qrc *.pro *.qmlproject *.pyproject CMakeLists.txt qmldir"),
844 Location(), excludeDirs, excludeFiles);
845 }
846
847 const qsizetype pathLen = exampleDir.path().size() - en->name().size();
848 for (auto &file : exampleFiles)
849 file = file.mid(pathLen);
850 for (auto &file : imageFiles)
851 file = file.mid(pathLen);
852
853 en->setFiles(exampleFiles, fullPath.mid(pathLen));
854 en->setImages(imageFiles);
855}
856
857/*!
858 returns true if \a t is \e {qmlsignal}, \e {qmlmethod},
859 \e {qmlattachedsignal}, or \e {qmlattachedmethod}.
860 */
861bool CppCodeParser::isQMLMethodTopic(const QString &t)
862{
865}
866
867/*!
868 Returns true if \a t is \e {qmlproperty}, \e {qmlpropertygroup},
869 or \e {qmlattachedproperty}.
870 */
871bool CppCodeParser::isQMLPropertyTopic(const QString &t)
872{
874}
875
876std::pair<std::vector<TiedDocumentation>, std::vector<FnMatchError>>
877CppCodeParser::processTopicArgs(const UntiedDocumentation &untied)
878{
879 const Doc &doc = untied.documentation;
880
881 if (doc.topicsUsed().isEmpty())
882 return {};
883
884 QDocDatabase *database = QDocDatabase::qdocDB();
885
886 const QString topic = doc.topicsUsed().first().m_topic;
887
888 std::vector<TiedDocumentation> tied{};
889 std::vector<FnMatchError> errors{};
890
891 if (isQMLPropertyTopic(topic)) {
892 auto tied_qml = processQmlProperties(untied);
893 tied.insert(tied.end(), tied_qml.begin(), tied_qml.end());
894 } else {
895 ArgList args = doc.metaCommandArgs(topic);
896 Node *node = nullptr;
897 if (args.size() == 1) {
898 if (topic == COMMAND_FN) {
899 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
900 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
901 auto result = fn_parser(doc.location(), args[0].first, args[0].second, untied.context);
902 if (auto *error = std::get_if<FnMatchError>(&result))
903 errors.emplace_back(*error);
904 else
905 node = std::get<Node*>(result);
906 }
907 } else if (topic == COMMAND_MACRO) {
908 node = parseMacroArg(doc.location(), args[0].first);
909 } else if (isQMLMethodTopic(topic)) {
910 node = parseOtherFuncArg(topic, doc.location(), args[0].first);
911 } else if (topic == COMMAND_DONTDOCUMENT) {
912 database->primaryTree()->addToDontDocumentMap(args[0].first);
913 } else {
914 node = processTopicCommand(doc, topic, args[0]);
915 }
916 if (node != nullptr) {
917 tied.emplace_back(TiedDocumentation{doc, node});
918 }
919 } else if (args.size() > 1) {
920 // Find nodes for each of the topic commands and add them to shared
921 // comment nodes.
922 QList<SharedCommentNode *> sharedCommentNodes;
923 for (const auto &arg : std::as_const(args)) {
924 node = nullptr;
925 if (topic == COMMAND_FN) {
926 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
927 if (InclusionFilter::processInternalDocs(policy) || !doc.isInternal()) {
928 auto result = fn_parser(doc.location(), arg.first, arg.second, untied.context);
929 if (auto *error = std::get_if<FnMatchError>(&result))
930 errors.emplace_back(*error);
931 else
932 node = std::get<Node*>(result);
933 }
934 } else if (topic == COMMAND_MACRO) {
935 node = parseMacroArg(doc.location(), arg.first);
936 } else if (isQMLMethodTopic(topic)) {
937 node = parseOtherFuncArg(topic, doc.location(), arg.first);
938 } else {
939 node = processTopicCommand(doc, topic, arg);
940 }
941 if (node != nullptr) {
942 bool found = false;
943 for (SharedCommentNode *scn : sharedCommentNodes) {
944 if (scn->parent() == node->parent()) {
945 scn->append(node);
946 found = true;
947 break;
948 }
949 }
950 if (!found) {
951 auto *scn = new SharedCommentNode(node);
952 sharedCommentNodes.append(scn);
953 tied.emplace_back(TiedDocumentation{doc, scn});
954 }
955 }
956 }
957 for (auto *scn : sharedCommentNodes) {
958 // Don't sort function nodes - preserve the order from \fn commands
959 // for position-dependent \overload primary behavior
960 if (!scn->collective().isEmpty() && !scn->collective().first()->isFunction())
961 scn->sort();
962 }
963 }
964 }
965 return std::make_pair(tied, errors);
966}
967
968/*!
969 For each node that is part of C++ API and produces a documentation
970 page, this function ensures that the node belongs to a module.
971 */
973{
974 if (n->physicalModuleName().isEmpty()) {
975 if (n->isInAPI() && !n->name().isEmpty()) {
976 switch (n->nodeType()) {
977 case NodeType::Class:
978 case NodeType::Struct:
979 case NodeType::Union:
982 break;
983 default:
984 return;
985 }
986 n->setPhysicalModuleName(Generator::defaultModuleName());
987 QDocDatabase::qdocDB()->addToModule(Generator::defaultModuleName(), n);
988 n->doc().location().warning(
989 QStringLiteral("Documentation for %1 '%2' has no \\inmodule command; "
990 "using project name by default: %3")
991 .arg(Node::nodeTypeString(n->nodeType()), n->name(),
992 n->physicalModuleName()));
993 }
994 }
995}
996
997void CppCodeParser::processMetaCommands(const std::vector<TiedDocumentation> &tied)
998{
999 for (auto [doc, node] : tied) {
1000 node->setDoc(doc);
1001 processMetaCommands(doc, node);
1002 checkModuleInclusion(node);
1003 if (node->isAggregate()) {
1004 auto *aggregate = static_cast<Aggregate *>(node);
1005
1006 if (!aggregate->includeFile()) {
1007 const QString className = aggregate->name();
1008
1009 // Resolution priority:
1010 // 1. Convenience header (if exists in include paths)
1011 // 2. Include-relative path from declLocation
1012 // 3. Class name as last resort (only if non-empty)
1013 QString includeFile = convenienceHeaderExists(className)
1014 ? className
1015 : computeIncludeSpelling(aggregate->declLocation());
1016
1017 if (includeFile.isEmpty() && !className.isEmpty())
1018 includeFile = className;
1019
1020 if (!includeFile.isEmpty())
1021 aggregate->setIncludeFile(includeFile);
1022 }
1023 }
1024 }
1025}
1026
1027void CppCodeParser::processQmlNativeTypeCommand(Node *node, const QString &cmd, const QString &arg, const Location &location)
1028{
1029 Q_ASSERT(node);
1030 if (!node->isQmlNode()) {
1031 location.warning(
1032 QStringLiteral("Command '\\%1' is only meaningful in '\\%2'")
1033 .arg(cmd, COMMAND_QMLTYPE));
1034 return;
1035 }
1036
1037 auto qmlNode = static_cast<QmlTypeNode *>(node);
1038
1040 auto classNode = database->findClassNode(arg.split(u"::"_s));
1041
1042 if (!classNode) {
1043 if (!Config::instance().get(CONFIG_NOLINKERRORS).asBool()) {
1044 location.warning(
1045 QStringLiteral("C++ class %2 not found: \\%1 %2")
1046 .arg(cmd, arg));
1047 }
1048 return;
1049 }
1050
1051 if (qmlNode->classNode()) {
1052 location.warning(
1053 QStringLiteral("QML type %1 documented with %2 as its native type. Replacing %2 with %3")
1054 .arg(qmlNode->name(), qmlNode->classNode()->name(), arg));
1055 }
1056
1057 qmlNode->setClassNode(classNode);
1058 classNode->insertQmlNativeType(qmlNode);
1059
1060 if (classNode->isQmlSingleton())
1061 qmlNode->setSingleton(true);
1062}
1063
1064namespace {
1065
1066/*!
1067 Strips compiler include path prefixes (-I, -isystem, etc.) from a path.
1068 Returns an empty string if the path is an unrecognized flag.
1069*/
1070QString stripIncludePrefix(const QString &path)
1071{
1072 QString result = path.trimmed();
1073
1074 static const QStringList prefixes = {
1075 "-I"_L1, "-isystem"_L1, "-iquote"_L1, "-idirafter"_L1
1076 };
1077
1078 for (const QString &prefix : prefixes) {
1079 if (result.startsWith(prefix)) {
1080 result = result.mid(prefix.size()).trimmed();
1081 return QDir::cleanPath(result);
1082 }
1083 }
1084
1085 // Skip framework paths and other unrecognized flags
1086 if (result.startsWith(u'-'))
1087 return {};
1088
1089 return QDir::cleanPath(result);
1090}
1091
1092} // anonymous namespace
1093
1094/*!
1095 Returns the cached list of cleaned include paths, combining both
1096 command-line and qdocconf include paths with prefixes stripped.
1097*/
1098const QStringList &CppCodeParser::getCleanIncludePaths() const
1099{
1100 if (!m_includePathsCached) {
1101 // Combine command-line and qdocconf include paths
1102 QStringList rawPaths = Config::instance().includePaths();
1103 rawPaths += Config::instance().getCanonicalPathList(
1104 CONFIG_INCLUDEPATHS, Config::IncludePaths);
1105
1106 for (const QString &path : rawPaths) {
1107 QString clean = stripIncludePrefix(path);
1108 if (!clean.isEmpty() && !m_cleanIncludePaths.contains(clean))
1109 m_cleanIncludePaths.append(clean);
1110 }
1111 m_includePathsCached = true;
1112 }
1113 return m_cleanIncludePaths;
1114}
1115
1116/*!
1117 Checks if a convenience header (extensionless file matching the class name)
1118 exists in any of the configured include paths. Results are cached.
1119
1120 This supports Qt's convention where classes like QString have convenience
1121 headers (extensionless files) that redirect to the actual header.
1122*/
1123bool CppCodeParser::convenienceHeaderExists(const QString &className) const
1124{
1125 if (className.isEmpty())
1126 return false;
1127
1128 auto it = m_convenienceHeaderCache.constFind(className);
1129 if (it != m_convenienceHeaderCache.constEnd())
1130 return *it;
1131
1132 bool exists = false;
1133 for (const QString &includePath : getCleanIncludePaths()) {
1134 QFileInfo candidate(includePath + u'/' + className);
1135 if (candidate.exists() && candidate.isFile()) {
1136 exists = true;
1137 break;
1138 }
1139 }
1140
1141 m_convenienceHeaderCache.insert(className, exists);
1142 return exists;
1143}
1144
1145/*!
1146 Returns the basename of the header file from the declaration location.
1147
1148 This provides a simple, reliable include spelling that works regardless
1149 of the working directory or include path configuration. For more complex
1150 include paths (like subdirectories), users can use \\inheaderfile.
1151*/
1152QString CppCodeParser::computeIncludeSpelling(const Location &loc) const
1153{
1154 if (loc.isEmpty())
1155 return {};
1156
1157 return loc.fileName();
1158}
1159
1160QT_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:1439
std::vector< TiedDocumentation > processQmlProperties(const UntiedDocumentation &untied)
FunctionNode * parseOtherFuncArg(const QString &topic, const Location &location, const QString &funcArg)
Parse QML signal/method topic commands.
FunctionNode * parseMacroArg(const Location &location, const QString &macroArg)
Parse the macro arguments in macroArg ad hoc, without using any actual parser.
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:89
const Text & title() const
Definition doc.cpp:120
const Location & startLocation() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:98
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
Definition doc.cpp:270
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
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:21
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:863
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:900
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:918
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:571
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