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