16#include <QtCore/qdebug.h>
17#include <QtCore/qfileinfo.h>
18#include <QtCore/qglobal.h>
20#include <private/qqmljsast_p.h>
21#include <private/qqmljsengine_p.h>
25using namespace Qt::StringLiterals;
28
29
31 const QSet<QString> &commands,
const QSet<QString> &topics)
35 this->m_filePath = filePath;
36 this->m_name = QFileInfo(filePath).baseName();
38 this->m_engine = engine;
39 this->m_commands = commands;
40 this->m_topics = topics;
45
46
47QQmlJS::SourceLocation
QmlDocVisitor::precedingComment(quint32 offset)
const
49 const auto comments = m_engine->comments();
50 for (
auto it = comments.rbegin(); it != comments.rend(); ++it) {
51 QQmlJS::SourceLocation loc = *it;
53 if (loc.begin() <= m_lastEndOffset) {
56 }
else if (m_usedComments.contains(loc.begin())) {
59 }
else if (loc.begin() > m_lastEndOffset && loc.end() < offset) {
61 if (m_document.at(loc.offset - 1) == QLatin1Char(
'*')) {
62 QString comment = m_document.mid(loc.offset, loc.length);
63 if (comment.startsWith(QLatin1Char(
'!')) || comment.startsWith(QLatin1Char(
'*'))) {
70 return QQmlJS::SourceLocation();
96
97
98
99
100
101
102
103
104
105
106
107
108
109
112 QQmlJS::SourceLocation loc = precedingComment(location.begin());
117 if (!loc.isValid()) {
119 node =
new QmlTypeNode(m_current, m_name, Node::QmlType);
125 QString source = m_document.mid(loc.offset + 1, loc.length - 1);
129 Doc doc(comment_loc, comment_loc, source, m_commands, m_topics);
135 qmid = args.first().first;
136 node = QDocDatabase::qdocDB()->findQmlTypeInPrimaryTree(qmid, m_name);
138 node =
new QmlTypeNode(m_current, m_name, Node::QmlType);
146 if (!topicsUsed.empty()) {
147 for (
int i = 0; i < topicsUsed.size(); ++i) {
148 QString topic = topicsUsed.at(i).m_topic;
149 QString args = topicsUsed.at(i).m_args;
150 if (topic.endsWith(QLatin1String(
"property"))) {
153 if (splitQmlPropertyArg(doc, args, qpa)) {
154 if (qpa.m_name == node->name()) {
156 qmlProperty->setDataType(qpa.m_type);
158 bool isAttached = topic.contains(QLatin1String(
"attached"));
173 qCDebug(lcQdoc) <<
"Failed to parse QML property:" << topic << args;
174 }
else if (topic.endsWith(QLatin1String(
"method")) || topic ==
COMMAND_QMLSIGNAL) {
182 for (
const auto &node : nodes)
183 applyMetacommands(loc, node, doc);
185 m_usedComments.insert(loc.offset);
193 QByteArray latin1 = signature.toLatin1();
194 Tokenizer stringTokenizer(location_, latin1);
196 tokenizer_ = &stringTokenizer;
202
203
204
205
208 if (tok_ == target) {
216
217
218
222
223
224
231 type->append(previousLexeme());
238 type->append(previousLexeme());
241 type->append(previousLexeme());
245 type->append(previousLexeme());
249 type->append(previousLexeme());
254 while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) || match(Tok_Caret))
255 type->append(previousLexeme());
258
259
260
261 type->appendHotspot();
263 if ((var !=
nullptr) && match(Tok_Ident))
264 *var = previousLexeme();
270 type->append(lexeme());
281 CodeChunk defaultValue;
284 if (name.isEmpty()) {
285 name = type.toString();
296 defaultValue.append(lexeme());
300 func_
->parameters().append(type.toString(), name, defaultValue.toString());
306 CodeChunk returnType;
308 qsizetype firstBlank = signature_.indexOf(QChar(
' '));
309 qsizetype leftParen = signature_.indexOf(QChar(
'('));
310 if ((firstBlank > 0) && (leftParen - firstBlank) > 1) {
316 names_.append(previousLexeme());
327
328
329
333 func_->setReturnType(returnType.toString());
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
367 QStringList blankSplit = arg.split(QLatin1Char(
' '));
368 if (blankSplit.size() > 1) {
369 qpa.m_type = blankSplit[0];
370 QStringList colonSplit(blankSplit[1].split(
"::"));
371 if (colonSplit.size() == 3) {
372 qpa.m_module = colonSplit[0];
373 qpa.m_component = colonSplit[1];
374 qpa.m_name = colonSplit[2];
376 }
else if (colonSplit.size() == 2) {
377 qpa.m_component = colonSplit[0];
378 qpa.m_name = colonSplit[1];
380 }
else if (colonSplit.size() == 1) {
381 qpa.m_name = colonSplit[0];
385 QStringLiteral(
"Unrecognizable QML module/component qualifier for %1.").arg(arg));
387 doc
.location().warning(QStringLiteral(
"Missing property type for %1.").arg(arg));
393
394
398 QSet<QString> metacommands = doc.metaCommandsUsed();
399 if (metacommands.size() > 0) {
400 metacommands.subtract(m_topics);
401 for (
const auto &command : std::as_const(metacommands)) {
402 const ArgList args = doc.metaCommandArgs(command);
404 if (node->isQmlType()) {
405 node->setAbstract(
true);
408 node->setDeprecated(args[0].second);
410 qdb->addToQmlModule(args[0].first, node);
412 if (node->name() == args[0].first)
413 doc.location().warning(
414 QStringLiteral(
"%1 tries to inherit itself").arg(args[0].first));
415 else if (node->isQmlType()) {
416 auto *qmlType =
static_cast<QmlTypeNode *>(node);
417 qmlType->setQmlBaseName(args[0].first);
420 if (!node->isQmlProperty()) {
421 doc.location().warning(QStringLiteral(
"Ignored '\\%1', applies only to '\\%2'")
423 }
else if (args.isEmpty() || args[0].first.isEmpty()) {
424 doc.location().warning(QStringLiteral(
"Expected an argument for '\\%1' (maybe you meant '\\%2'?)")
427 static_cast<QmlPropertyNode *>(node)->setDefaultValue(args[0].first);
432 if (!node->isQmlProperty()) {
433 doc.location().warning(
"Ignored '\\%1', applies only to '\\%2'"_L1
435 }
else if (!
static_cast<QmlPropertyNode*>(node)->setEnumNode(args[0].first, args[0].second)) {
436 doc.location().warning(
"Failed to find C++ enumeration '%2' passed to \\%1"_L1
437 .arg(command, args[0].first),
"Use \\value commands instead"_L1);
440 node->markReadOnly(1);
442 if (node->isQmlProperty())
443 static_cast<QmlPropertyNode *>(node)->setRequired();
445 for (
const auto &argument : args)
446 QDocDatabase::qdocDB()->addToGroup(argument.first, node);
448 node->setStatus(Node::Internal);
450 node->setStatus(Node::Deprecated);
452 node->setStatus(Node::Preliminary);
454 QString arg = args[0].first;
459 doc.location().warning(
460 QStringLiteral(
"The \\%1 command is ignored in QML files").arg(command));
467
468
469
470QString
QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id)
474 result = id->name.toString();
476 while (id !=
nullptr) {
477 result += QChar(
'.') + id->name.toString();
485
486
487
488
489
490
491
492
493
496 QString type = getFullyQualifiedId(definition->qualifiedTypeNameId);
499 auto component = applyDocumentation(definition->firstSourceLocation(),
nullptr);
501 auto *qmlTypeNode =
static_cast<
QmlTypeNode *>(component);
502 if (!component->doc().isEmpty())
503 qmlTypeNode->setQmlBaseName(type);
504 qmlTypeNode->setTitle(m_name);
505 qmlTypeNode->setImportList(m_importList);
506 m_importList.clear();
507 m_current = qmlTypeNode;
514
515
516
517
518
521 if (m_nestingLevel > 0) {
524 m_lastEndOffset = definition->lastSourceLocation().end();
529 QString name = m_document.mid(import->fileNameToken.offset, import->fileNameToken.length);
531 name = name.mid(1, name.size() - 2);
533 if (import->version) {
534 const auto start = import->version->firstSourceLocation().begin();
535 const auto end = import->version->lastSourceLocation().end();
536 version = m_document.mid(start, end - start);
538 QString importUri = getFullyQualifiedId(import->importUri);
539 m_importList.append(
ImportRec(name, version, importUri));
546 m_lastEndOffset = definition->lastSourceLocation().end();
571 for (QQmlJS::AST::UiQualifiedId *it = node; it; it = it->next) {
575 s.append(QLatin1Char(
'.'));
582
583
584
585
588 if (m_nestingLevel > 1) {
591 switch (member->type) {
592 case QQmlJS::AST::UiPublicMember::Signal: {
594 auto *qmlType =
static_cast<
QmlTypeNode *>(m_current);
597 QString name = member->name.toString();
598 auto *newSignal =
new FunctionNode(metaness, m_current, name);
599 Parameters ¶meters = newSignal->parameters();
600 for (QQmlJS::AST::UiParameterList *it = member->parameters; it; it = it->next) {
601 const QString type = it->type ? it->type->toString() : QString();
602 if (!type.isEmpty() && !it->name.isEmpty())
603 parameters.append(type, it->name.toString());
605 applyDocumentation(member->firstSourceLocation(), newSignal);
610 case QQmlJS::AST::UiPublicMember::Property: {
611 QString type = qualifiedIdToString(member->memberType);
613 auto *qmlType =
static_cast<
QmlTypeNode *>(m_current);
615 QString name = member->name.toString();
617 if (qmlPropNode ==
nullptr)
620 if (member->isDefaultMember())
622 if (member->requiredToken().isValid())
624 applyDocumentation(member->firstSourceLocation(), qmlPropNode);
637
638
641 m_lastEndOffset = member->lastSourceLocation().end();
650
651
652
655 if (m_nestingLevel <= 1) {
659 QString name = fd->name.toString();
660 auto *method =
new FunctionNode(metaness, m_current, name);
661 Parameters ¶meters = method->parameters();
662 QQmlJS::AST::FormalParameterList *formals = fd->formals;
664 QQmlJS::AST::FormalParameterList *fp = formals;
666 QString defaultValue;
667 auto initializer = fp->element->initializer;
669 auto loc = initializer->firstSourceLocation();
670 defaultValue = m_document.mid(loc.begin(), loc.length);
672 parameters.append(QString(), fp->element->bindingIdentifier.toString(),
675 }
while (fp && fp != formals);
677 applyDocumentation(fd->firstSourceLocation(), method);
683
684
687 m_lastEndOffset = fd->lastSourceLocation().end();
691
692
693
694
695
696
697
698
706 m_lastEndOffset = sb->lastSourceLocation().end();
721 hasRecursionDepthError =
true;
726 return hasRecursionDepthError;
const Location & location() const
Returns the starting location of a qdoc comment.
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
This node is used to represent any kind of function being documented.
Parameters & parameters()
The Location class provides a way to mark a location in a file.
bool isNamespace() const
Returns true if the node type is Namespace.
bool isFunction(Genus g=DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Aggregate * parent() const
Returns the node's parent pointer.
void setLocation(const Location &t)
Sets the node's declaration location, its definition location, or both, depending on the suffix of th...
void setDoc(const Doc &doc, bool replace=false)
Sets this Node's Doc to doc.
LinkType
An unsigned char value that probably should be moved out of the Node base class.
A class for parsing and managing a function parameter list.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
bool visit(QQmlJS::AST::UiImport *import) override
void throwRecursionDepthError() final
void endVisit(QQmlJS::AST::UiImport *definition) override
QmlDocVisitor(const QString &filePath, const QString &code, QQmlJS::Engine *engine, const QSet< QString > &commands, const QSet< QString > &topics)
The constructor stores all the parameters in local data members.
bool isDefault() const override
Returns true if the QML property node is marked as default.
void markReadOnly(bool flag) override
If this node is a QmlPropertyNode, then the property's read-only flag is set to flag.
void markDefault() override
If this node is a QmlPropertyNode, it is marked as the default property.
bool isReadOnly() const
Returns true if this QML property node is marked as a read-only property.
bool isAlias() const override
Returns true if this QML property is marked as an alias.
bool match(int target)
If the current token matches target, read the next token and return true.
QmlSignatureParser(FunctionNode *func, const QString &signature, const Location &loc)
bool matchTypeAndName(CodeChunk *type, QString *var)
Parse a QML data type into type and an optional variable name into var.
QString previousLexeme() const
void setParsingFnOrMacro(bool macro)
#define COMMAND_QMLINHERITS
#define COMMAND_QMLSIGNAL
#define COMMAND_DEPRECATED
#define COMMAND_PRELIMINARY
#define COMMAND_QMLPROPERTY
#define COMMAND_QMLDEFAULT
#define COMMAND_QMLABSTRACT
#define COMMAND_QMLREADONLY
#define COMMAND_QMLENUMERATORSFROM
#define COMMAND_QMLREQUIRED
#define COMMAND_INQMLMODULE
Combined button and popup list for selecting options.
static QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)