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