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