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