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
qmlvisitor.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
4#include "qmlvisitor.h"
5
6#include "aggregate.h"
7#include "codechunk.h"
8#include "codeparser.h"
9#include "functionnode.h"
10#include "genustypes.h"
11#include "nativeenum.h"
12#include "node.h"
13#include "qdocdatabase.h"
17#include "tokenizer.h"
18#include "utilities.h"
19
20#include <QtCore/qdebug.h>
21#include <QtCore/qfileinfo.h>
22#include <QtCore/qglobal.h>
23
24#include <private/qqmljsast_p.h>
25#include <private/qqmljsengine_p.h>
26
28
29using namespace Qt::StringLiterals;
30
31/*!
32 The constructor stores all the parameters in local data members.
33 */
34QmlDocVisitor::QmlDocVisitor(const QString &filePath, const QString &code, QQmlJS::Engine *engine,
35 const QSet<QString> &commands, const QSet<QString> &topics)
37{
38 m_lastEndOffset = 0;
39 this->m_filePath = filePath;
40 this->m_name = QFileInfo(filePath).baseName();
41 m_document = code;
42 this->m_engine = engine;
43 this->m_commands = commands;
44 this->m_topics = topics;
46}
47
48/*!
49 Returns the location of the nearest comment above the \a offset.
50 */
51QQmlJS::SourceLocation QmlDocVisitor::precedingComment(quint32 offset) const
52{
53 const auto comments = m_engine->comments();
54 for (auto it = comments.rbegin(); it != comments.rend(); ++it) {
55 QQmlJS::SourceLocation loc = *it;
56
57 if (loc.begin() <= m_lastEndOffset) {
58 // Return if we reach the end of the preceding structure.
59 break;
60 } else if (m_usedComments.contains(loc.begin())) {
61 // Return if we encounter a previously used comment.
62 break;
63 } else if (loc.begin() > m_lastEndOffset && loc.end() < offset) {
64 // Only examine multiline comments in order to avoid snippet markers.
65 if (m_document.at(loc.offset - 1) == QLatin1Char('*')) {
66 QString comment = m_document.mid(loc.offset, loc.length);
67 if (comment.startsWith(QLatin1Char('!')) || comment.startsWith(QLatin1Char('*'))) {
68 return loc;
69 }
70 }
71 }
72 }
73
74 return QQmlJS::SourceLocation();
75}
76
78{
79public:
80 QmlSignatureParser(FunctionNode *func, const QString &signature, const Location &loc);
81 void readToken() { tok_ = tokenizer_->getToken(); }
82 QString lexeme() { return tokenizer_->lexeme(); }
83 QString previousLexeme() { return tokenizer_->previousLexeme(); }
84
85 bool match(int target);
86 bool matchTypeAndName(CodeChunk *type, QString *var);
89
90private:
91 QString signature_;
92 QStringList names_;
93 Tokenizer *tokenizer_;
94 int tok_;
95 FunctionNode *func_;
96 const Location &location_;
97};
98
99/*!
100 Finds the nearest unused qdoc comment above the QML entity
101 represented by a \a node and processes the qdoc commands
102 in that comment. The processed documentation is stored in
103 \a node.
104
105 If \a node is a \c nullptr and there is a valid comment block,
106 the QML module identifier (\inqmlmodule argument) is used
107 for searching an existing QML type node. If an existing node
108 is not found, constructs a new QmlTypeNode instance.
109
110 Returns a pointer to the QmlTypeNode instance if one was
111 found or constructed. Otherwise, returns a pointer to the \a
112 node that was passed as an argument.
113 */
114Node *QmlDocVisitor::applyDocumentation(QQmlJS::SourceLocation location, Node *node)
115{
116 QQmlJS::SourceLocation loc = precedingComment(location.begin());
117 Location comment_loc(m_filePath);
118
119 // No preceding comment; construct a new QML type if
120 // needed.
121 if (!loc.isValid()) {
122 if (!node) {
123 node = new QmlTypeNode(m_current, m_name, NodeType::QmlType);
124 if (m_singletonPragmaFound)
125 static_cast<QmlTypeNode*>(node)->setSingleton(true);
126 }
127 comment_loc.setLineNo(location.startLine);
128 node->setLocation(comment_loc);
129 return node;
130 }
131
132 QString source = m_document.mid(loc.offset + 1, loc.length - 1);
133 comment_loc.setLineNo(loc.startLine);
134 comment_loc.setColumnNo(loc.startColumn);
135
136 Doc doc(comment_loc, comment_loc, source, m_commands, m_topics);
137 const TopicList &topicsUsed = doc.topicsUsed();
138 NodeList nodes;
139 if (!node) {
140 QString qmid;
141 if (auto args = doc.metaCommandArgs(COMMAND_INQMLMODULE); !args.isEmpty())
142 qmid = args.first().first;
143 node = QDocDatabase::qdocDB()->findQmlTypeInPrimaryTree(qmid, m_name);
144 if (!node) {
145 node = new QmlTypeNode(m_current, m_name, NodeType::QmlType);
146 node->setLocation(comment_loc);
147 }
148 if (m_singletonPragmaFound && node->isQmlNode())
149 static_cast<QmlTypeNode*>(node)->setSingleton(true);
150 }
151
152 auto *parent{node->parent()};
153 nodes << node;
154 if (!topicsUsed.empty()) {
155 for (int i = 0; i < topicsUsed.size(); ++i) {
156 QString topic = topicsUsed.at(i).m_topic;
157 QString args = topicsUsed.at(i).m_args;
158 if (topic.endsWith(QLatin1String("property"))) {
159 auto *qmlProperty = static_cast<QmlPropertyNode *>(node);
160 if (auto qpa = QmlPropertyArguments::parse(args, doc.location())) {
161 if (qpa->m_name == node->name()) {
162 // Allow overriding data type from the arguments
163 qmlProperty->setDataType(qpa->m_type);
164 } else {
165 bool isAttached = topic.contains(QLatin1String("attached"));
166 QmlPropertyNode *n = parent->hasQmlProperty(qpa->m_name, isAttached);
167 if (n == nullptr)
168 n = new QmlPropertyNode(parent, qpa->m_name, qpa->m_type, isAttached);
169 n->setIsList(qpa->m_isList);
170 // Use the const-overload of QmlPropertyNode::isReadOnly() as there's
171 // no associated C++ property to resolve the read-only status from
172 n->markReadOnly(const_cast<const QmlPropertyNode *>(qmlProperty)->isReadOnly()
173 && !isAttached);
174 if (qmlProperty->isDefault())
176 nodes << n;
177 }
178 } else
179 qCDebug(lcQdoc) << "Failed to parse QML property:" << topic << args;
180 } else if (topic == COMMAND_QMLSIGNAL || topic == COMMAND_QMLMETHOD ||
182 if (node->isFunction()) {
183 QmlSignatureParser qsp(static_cast<FunctionNode *>(node), args, doc.location());
184 if (nodes.size() > 1) {
185 doc.location().warning("\\%1 cannot be mixed with other topic commands"_L1.arg(topic));
186 nodes = {node};
187 }
188 break;
189 }
190 } else if (topic == COMMAND_QMLTYPE || topic == COMMAND_QMLSINGLETONTYPE ||
191 topic == COMMAND_QMLVALUETYPE || topic == COMMAND_QMLBASICTYPE) {
192 if (node->isQmlType()) {
193 if (nodes.size() > 1) {
194 doc.location().warning("\\%1 cannot be mixed with other topic commands"_L1.arg(topic));
195 nodes = {node};
196 }
197 break;
198 }
199 }
200 }
201 }
202
203 for (auto *n : nodes) {
204 applyMetacommands(loc, n, doc);
205 }
206
207 // Test if we're documenting a property group; We need three nodes at minimum:
208 // The property that acts as the `root` in the group, and two more to make a
209 // group. Each property must be prefixed with the root name + '.'.
210 bool isPropertyGroup{false};
211 if (nodes.size() > 2 && parent->isQmlType()) {
212 isPropertyGroup =
213 std::all_of(std::next(nodes.cbegin()), nodes.cend(), [node](const Node *p) {
214 return p->name().startsWith("%1."_L1.arg(node->name()));
215 });
216 }
217 // Construct a SharedCommentNode (SCN) representing a QML property group.
218 //
219 // Note that it's important to do this *after* constructing
220 // the topic nodes - which need to be written to index before the related
221 // SCN.
222 if (isPropertyGroup) {
223 // The first node in `nodes` is the group property;
224 // create SCN with the same name
225 QString group{nodes.takeFirst()->name()};
226 auto *scn = new SharedCommentNode(static_cast<QmlTypeNode*>(parent),
227 nodes.size(), group);
228 scn->setDoc(doc);
229 scn->setLocation(doc.startLocation());
230 applyMetacommands(loc, scn, doc);
231 for (const auto n : std::as_const(nodes))
232 scn->append(n);
233 scn->sort();
234 } else {
235 for (auto *n : nodes) {
236 n->setDoc(doc);
237 n->setLocation(doc.location());
238 }
239 }
240
241 m_usedComments.insert(loc.offset);
242 return node;
243}
244
245QmlSignatureParser::QmlSignatureParser(FunctionNode *func, const QString &signature,
246 const Location &loc)
247 : signature_(signature), func_(func), location_(loc)
248{
249 QByteArray latin1 = signature.toLatin1();
250 Tokenizer stringTokenizer(location_, std::move(latin1));
251 stringTokenizer.setParsingFnOrMacro(true);
252 tokenizer_ = &stringTokenizer;
255}
256
257/*!
258 If the current token matches \a target, read the next
259 token and return true. Otherwise, don't read the next
260 token, and return false.
261 */
262bool QmlSignatureParser::match(int target)
263{
264 if (tok_ == target) {
266 return true;
267 }
268 return false;
269}
270
271/*!
272 Parse a QML data type into \a type and an optional
273 variable name into \a var.
274 */
275bool QmlSignatureParser::matchTypeAndName(CodeChunk *type, QString *var)
276{
277 // Match code with scope operators, such as Alpha::Beta::Gamma::...::Omega.
278 for (;;) {
279 if (match(Tok_Ident)) {
280 type->append(previousLexeme());
281 } else {
282 // If not an identifier, try to match a sequence of modifiers.
283 bool hasModifiers = false;
285 || match(Tok_int64)) {
286 // Append the matched qualifier token.
287 type->append(previousLexeme());
288 hasModifiers = true;
289 }
290
291 // Match and append a type, or return false.
292 if (hasModifiers && (match(Tok_int) || match(Tok_char) || match(Tok_double))) {
293 type->append(previousLexeme());
294 } else if (!hasModifiers) {
297 type->append(previousLexeme());
298 } else {
299 return false;
300 }
301 }
302 }
303
304 // Match and append a scope operator, or break to go to the next stage.
306 type->append(previousLexeme());
307 else
308 break;
309 }
310
311 // Matches a sequence of & * const ^ tokens.
312 while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) || match(Tok_Caret))
313 type->append(previousLexeme());
314
315 /*
316 The usual case: Look for an optional identifier, then for
317 some array brackets.
318 */
319 type->appendHotspot();
320
321 // Set the variable name if it is unset and an identifier is matched.
322 if ((var != nullptr) && match(Tok_Ident))
323 *var = previousLexeme();
324
325 // Skip pairs of braces and their contents.
326 if (tok_ == Tok_LeftBracket) {
327 int bracketDepth0 = tokenizer_->bracketDepth();
328 while ((tokenizer_->bracketDepth() >= bracketDepth0 && tok_ != Tok_Eoi)
329 || tok_ == Tok_RightBracket) {
330 type->append(lexeme());
332 }
333 }
334 return true;
335}
336
338{
339 QString name;
340 CodeChunk type;
341 CodeChunk defaultValue;
342
343 bool result = matchTypeAndName(&type, &name);
344 if (name.isEmpty()) {
345 name = type.toString();
346 type.clear();
347 }
348
349 if (!result)
350 return false;
351 if (match(Tok_Equal)) {
352 int parenDepth0 = tokenizer_->parenDepth();
353 while (tokenizer_->parenDepth() >= parenDepth0
354 && (tok_ != Tok_Comma || tokenizer_->parenDepth() > parenDepth0)
355 && tok_ != Tok_Eoi) {
356 defaultValue.append(lexeme());
358 }
359 }
360 func_->parameters().append(type.toString(), name, defaultValue.toString());
361 return true;
362}
363
365{
366 CodeChunk returnType;
367
368 qsizetype firstBlank = signature_.indexOf(QChar(' '));
369 qsizetype leftParen = signature_.indexOf(QChar('('));
370 if ((firstBlank > 0) && (leftParen - firstBlank) > 1) {
371 if (!matchTypeAndName(&returnType, nullptr))
372 return false;
373 }
374
375 while (match(Tok_Ident)) {
376 names_.append(previousLexeme());
378 previousLexeme();
379 names_.pop_back();
380 break;
381 }
382 }
383
384 if (tok_ != Tok_LeftParen)
385 return false;
386 /*
387 Parsing the parameters should be moved into class Parameters,
388 but it can wait. mws 14/12/2018
389 */
391
392 func_->setLocation(location_);
393 func_->setReturnType(returnType.toString());
394
395 if (tok_ != Tok_RightParen) {
397 do {
398 if (!matchParameter())
399 return false;
400 } while (match(Tok_Comma));
401 }
403 return false;
404 return true;
405}
406
407/*!
408 Applies the metacommands found in the comment.
409 */
410void QmlDocVisitor::applyMetacommands(QQmlJS::SourceLocation, Node *node, Doc &doc)
411{
413 QSet<QString> metacommands = doc.metaCommandsUsed();
414 if (metacommands.size() > 0) {
415 metacommands.subtract(m_topics);
416 for (const auto &command : std::as_const(metacommands)) {
417 const ArgList args = doc.metaCommandArgs(command);
418 if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
419 if (node->isQmlType()) {
420 node->setAbstract(true);
421 }
422 } else if (command == COMMAND_DEPRECATED) {
423 node->setDeprecated(args[0].second);
424 } else if (command == COMMAND_INQMLMODULE) {
425 qdb->addToQmlModule(args[0].first, node);
426 } else if (command == COMMAND_QMLINHERITS) {
427 if (node->name() == args[0].first)
428 doc.location().warning(
429 QStringLiteral("%1 tries to inherit itself").arg(args[0].first));
430 else if (node->isQmlType()) {
431 auto *qmlType = static_cast<QmlTypeNode *>(node);
432 qmlType->setQmlBaseName(args[0].first);
433 }
434 } else if (command == COMMAND_DEFAULT) {
435 if (!node->isQmlProperty()) {
436 doc.location().warning(QStringLiteral("Ignored '\\%1', applies only to '\\%2'")
437 .arg(command, COMMAND_QMLPROPERTY));
438 } else if (args.isEmpty() || args[0].first.isEmpty()) {
439 doc.location().warning(QStringLiteral("Expected an argument for '\\%1' (maybe you meant '\\%2'?)")
440 .arg(command, COMMAND_QMLDEFAULT));
441 } else {
442 static_cast<QmlPropertyNode *>(node)->setDefaultValue(args[0].first);
443 }
444 } else if (command == COMMAND_QMLDEFAULT) {
445 node->markDefault();
446 } else if (command == COMMAND_QMLENUMERATORSFROM) {
447 if (!node->isQmlProperty()) {
448 doc.location().warning("Ignored '\\%1', applies only to '\\%2'"_L1
449 .arg(command, COMMAND_QMLPROPERTY));
450 } else if (!static_cast<QmlPropertyNode*>(node)->nativeEnum()->resolve(args[0].first, args[0].second)) {
451 doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
452 .arg(command, args[0].first), "Use \\value commands instead"_L1);
453 }
454 } else if (command == COMMAND_QMLREADONLY) {
455 node->markReadOnly(1);
456 } else if (command == COMMAND_QMLREQUIRED) {
457 if (node->isQmlProperty())
458 static_cast<QmlPropertyNode *>(node)->setRequired();
459 } else if ((command == COMMAND_INGROUP) && !args.isEmpty()) {
460 for (const auto &argument : args)
461 QDocDatabase::qdocDB()->addToGroup(argument.first, node);
462 } else if (command == COMMAND_INTERNAL) {
463 node->setStatus(Node::Internal);
464 } else if (command == COMMAND_OBSOLETE) {
465 node->setStatus(Node::Deprecated);
466 } else if (command == COMMAND_PRELIMINARY) {
467 node->setStatus(Node::Preliminary);
468 } else if (command == COMMAND_SINCE) {
469 QString arg = args[0].first; //.join(' ');
470 node->setSince(arg);
471 } else if (command == COMMAND_WRAPPER) {
472 node->setWrapper();
473 } else {
474 doc.location().warning(
475 QStringLiteral("The \\%1 command is ignored in QML files").arg(command));
476 }
477 }
478 }
479}
480
481/*!
482 Reconstruct the qualified \a id using dot notation
483 and return the fully qualified string.
484 */
485QString QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id)
486{
487 QString result;
488 if (id) {
489 result = id->name.toString();
490 id = id->next;
491 while (id != nullptr) {
492 result += QChar('.') + id->name.toString();
493 id = id->next;
494 }
495 }
496 return result;
497}
498
499/*!
500 Begin the visit of the object \a definition, recording it in the
501 qdoc database. Increment the object nesting level, which is used
502 to test whether we are at the public API level. The public level
503 is level 1.
504
505 Defers the construction of a QmlTypeNode instance to
506 applyDocumentation(), by passing \c nullptr as the second
507 argument.
508 */
509bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition)
510{
511 QString type = getFullyQualifiedId(definition->qualifiedTypeNameId);
512 m_nestingLevel++;
513 if (m_current->isNamespace()) {
514 auto component = applyDocumentation(definition->firstSourceLocation(), nullptr);
515 Q_ASSERT(component);
516 auto *qmlTypeNode = static_cast<QmlTypeNode *>(component);
517 if (!component->doc().isEmpty())
518 qmlTypeNode->setQmlBaseName(type);
519 qmlTypeNode->setTitle(m_name);
520 qmlTypeNode->setImportList(m_importList);
521 m_importList.clear();
522 m_current = qmlTypeNode;
523 }
524
525 return true;
526}
527
528/*!
529 End the visit of the object \a definition. In particular,
530 decrement the object nesting level, which is used to test
531 whether we are at the public API level. The public API
532 level is level 1. It won't decrement below 0.
533 */
534void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *definition)
535{
536 if (m_nestingLevel > 0) {
537 --m_nestingLevel;
538 }
539 m_lastEndOffset = definition->lastSourceLocation().end();
540}
541
542bool QmlDocVisitor::visit(QQmlJS::AST::UiImport *import)
543{
544 QString name = m_document.mid(import->fileNameToken.offset, import->fileNameToken.length);
545 if (name[0] == '\"')
546 name = name.mid(1, name.size() - 2);
547 QString version;
548 if (import->version) {
549 const auto start = import->version->firstSourceLocation().begin();
550 const auto end = import->version->lastSourceLocation().end();
551 version = m_document.mid(start, end - start);
552 }
553 QString importUri = getFullyQualifiedId(import->importUri);
554 m_importList.append(ImportRec(std::move(name), std::move(version), std::move(importUri), import->importId));
555
556 return true;
557}
558
559void QmlDocVisitor::endVisit(QQmlJS::AST::UiImport *definition)
560{
561 m_lastEndOffset = definition->lastSourceLocation().end();
562}
563
564bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectBinding *)
565{
566 ++m_nestingLevel;
567 return true;
568}
569
570void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectBinding *)
571{
572 --m_nestingLevel;
573}
574
575bool QmlDocVisitor::visit(QQmlJS::AST::UiArrayBinding *)
576{
577 return true;
578}
579
580void QmlDocVisitor::endVisit(QQmlJS::AST::UiArrayBinding *) {}
581
582static QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)
583{
584 QString s;
585
586 for (QQmlJS::AST::UiQualifiedId *it = node; it; it = it->next) {
587 s.append(it->name);
588
589 if (it->next)
590 s.append(QLatin1Char('.'));
591 }
592
593 return s;
594}
595
596/*!
597 Visits the public \a member declaration, which can be a
598 signal or a property. It is a custom signal or property.
599 Only visit the \a member if the nestingLevel is 1.
600 */
601bool QmlDocVisitor::visit(QQmlJS::AST::UiPublicMember *member)
602{
603 if (m_nestingLevel > 1) {
604 return true;
605 }
606 switch (member->type) {
607 case QQmlJS::AST::UiPublicMember::Signal: {
608 if (m_current->isQmlType()) {
609 auto *qmlType = static_cast<QmlTypeNode *>(m_current);
610 if (qmlType) {
612 QString name = member->name.toString();
613 auto *newSignal = new FunctionNode(metaness, m_current, name);
614 Parameters &parameters = newSignal->parameters();
615 for (QQmlJS::AST::UiParameterList *it = member->parameters; it; it = it->next) {
616 const QString type = it->type ? it->type->toString() : QString();
617 if (!type.isEmpty() && !it->name.isEmpty())
618 parameters.append(type, it->name.toString());
619 }
620 applyDocumentation(member->firstSourceLocation(), newSignal);
621 }
622 }
623 break;
624 }
625 case QQmlJS::AST::UiPublicMember::Property: {
626 QString type = qualifiedIdToString(member->memberType);
627 if (m_current->isQmlType()) {
628 auto *qmlType = static_cast<QmlTypeNode *>(m_current);
629 if (qmlType) {
630 QString name = member->name.toString();
631 QmlPropertyNode *qmlPropNode = qmlType->hasQmlProperty(name);
632 if (qmlPropNode == nullptr)
633 qmlPropNode = new QmlPropertyNode(qmlType, std::move(name), std::move(type), false);
634 qmlPropNode->markReadOnly(member->isReadonly());
635 if (member->isDefaultMember())
636 qmlPropNode->markDefault();
637 if (member->requiredToken().isValid())
638 qmlPropNode->setRequired();
639 qmlPropNode->setIsList(member->typeModifier == "list"_L1);
640 applyDocumentation(member->firstSourceLocation(), qmlPropNode);
641 }
642 }
643 break;
644 }
645 default:
646 return false;
647 }
648
649 return true;
650}
651
652/*!
653 End the visit of the \a member.
654 */
655void QmlDocVisitor::endVisit(QQmlJS::AST::UiPublicMember *member)
656{
657 m_lastEndOffset = member->lastSourceLocation().end();
658}
659
660bool QmlDocVisitor::visit(QQmlJS::AST::IdentifierPropertyName *)
661{
662 return true;
663}
664
665/*!
666 Begin the visit of the function declaration \a fd, but only
667 if the nesting level is 1.
668 */
669bool QmlDocVisitor::visit(QQmlJS::AST::FunctionDeclaration *fd)
670{
671 if (m_nestingLevel <= 1) {
673 if (!m_current->isQmlType())
674 return true;
675 QString name = fd->name.toString();
676 auto *method = new FunctionNode(metaness, m_current, name);
677 Parameters &parameters = method->parameters();
678 QQmlJS::AST::FormalParameterList *formals = fd->formals;
679 if (formals) {
680 QQmlJS::AST::FormalParameterList *fp = formals;
681 do {
682 QString defaultValue;
683 auto initializer = fp->element->initializer;
684 if (initializer) {
685 auto loc = initializer->firstSourceLocation();
686 defaultValue = m_document.mid(loc.begin(), loc.length);
687 }
688 parameters.append(QString(), fp->element->bindingIdentifier.toString(),
689 defaultValue);
690 fp = fp->next;
691 } while (fp && fp != formals);
692 }
693 applyDocumentation(fd->firstSourceLocation(), method);
694 }
695 return true;
696}
697
698/*!
699 End the visit of the function declaration, \a fd.
700 */
701void QmlDocVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fd)
702{
703 m_lastEndOffset = fd->lastSourceLocation().end();
704}
705
706/*!
707 Begin the visit of the signal handler declaration \a sb, but only
708 if the nesting level is 1.
709
710 This visit is now deprecated. It has been decided to document
711 public signals. If a signal handler must be discussed in the
712 documentation, that discussion must take place in the comment
713 for the signal.
714 */
715bool QmlDocVisitor::visit(QQmlJS::AST::UiScriptBinding *)
716{
717 return true;
718}
719
720void QmlDocVisitor::endVisit(QQmlJS::AST::UiScriptBinding *sb)
721{
722 m_lastEndOffset = sb->lastSourceLocation().end();
723}
724
725bool QmlDocVisitor::visit(QQmlJS::AST::UiQualifiedId *)
726{
727 return true;
728}
729
730void QmlDocVisitor::endVisit(QQmlJS::AST::UiQualifiedId *)
731{
732 // nothing.
733}
734
736{
737 hasRecursionDepthError = true;
738}
739
741{
742 return hasRecursionDepthError;
743}
744
745QT_END_NAMESPACE
Definition doc.h:31
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:90
const Location & startLocation() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:99
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
Definition doc.cpp:246
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.
Definition location.h:20
void setColumnNo(int no)
Definition location.h:41
void setLineNo(int no)
Definition location.h:40
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
bool hasError() const
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.
void setIsList(bool isList)
Marks this property as a list if isList is true.
QString previousLexeme()
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.
void setSingleton(bool singleton=true)
Definition qmltypenode.h:34
QString previousLexeme() const
int getToken()
int parenDepth() const
Definition tokenizer.h:94
QString lexeme() const
int bracketDepth() const
Definition tokenizer.h:95
void setParsingFnOrMacro(bool macro)
Definition tokenizer.h:88
#define COMMAND_QMLINHERITS
Definition codeparser.h:57
#define COMMAND_INTERNAL
Definition codeparser.h:34
#define COMMAND_QMLSIGNAL
Definition codeparser.h:66
#define COMMAND_OBSOLETE
Definition codeparser.h:42
#define COMMAND_DEPRECATED
Definition codeparser.h:21
#define COMMAND_QMLSINGLETONTYPE
Definition codeparser.h:68
#define COMMAND_PRELIMINARY
Definition codeparser.h:45
#define COMMAND_WRAPPER
Definition codeparser.h:86
#define COMMAND_QMLPROPERTY
Definition codeparser.h:62
#define COMMAND_QMLDEFAULT
Definition codeparser.h:54
#define COMMAND_SINCE
Definition codeparser.h:75
#define COMMAND_QMLABSTRACT
Definition codeparser.h:48
#define COMMAND_QMLBASICTYPE
Definition codeparser.h:90
#define COMMAND_DEFAULT
Definition codeparser.h:20
#define COMMAND_QMLMETHOD
Definition codeparser.h:59
#define COMMAND_QMLREADONLY
Definition codeparser.h:64
#define COMMAND_QMLENUMERATORSFROM
Definition codeparser.h:56
#define COMMAND_QMLREQUIRED
Definition codeparser.h:65
#define COMMAND_ABSTRACT
Definition codeparser.h:13
#define COMMAND_QMLVALUETYPE
Definition codeparser.h:52
#define COMMAND_INQMLMODULE
Definition codeparser.h:33
#define COMMAND_INGROUP
Definition codeparser.h:29
#define COMMAND_QMLTYPE
Definition codeparser.h:67
#define COMMAND_QMLATTACHEDMETHOD
Definition codeparser.h:49
#define COMMAND_QMLATTACHEDSIGNAL
Definition codeparser.h:51
QList< Node * > NodeList
Definition node.h:44
static QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:126
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:115
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:128
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:215
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:839
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:107
A class for parsing and managing a function parameter list.
Definition main.cpp:28
void clear()
Definition parameters.h:62
@ Tok_Eoi
Definition tokenizer.h:24
@ Tok_void
Definition tokenizer.h:62
@ Tok_int64
Definition tokenizer.h:64
@ Tok_unsigned
Definition tokenizer.h:61
@ Tok_RightParen
Definition tokenizer.h:29
@ Tok_char
Definition tokenizer.h:51
@ Tok_short
Definition tokenizer.h:58
@ Tok_Gulbrandsen
Definition tokenizer.h:40
@ Tok_Equal
Definition tokenizer.h:31
@ Tok_int
Definition tokenizer.h:55
@ Tok_long
Definition tokenizer.h:56
@ Tok_LeftBracket
Definition tokenizer.h:41
@ Tok_Comma
Definition tokenizer.h:38
@ Tok_Ident
Definition tokenizer.h:49
@ Tok_double
Definition tokenizer.h:54
@ Tok_LeftParen
Definition tokenizer.h:28
@ Tok_RightBracket
Definition tokenizer.h:42
@ Tok_signed
Definition tokenizer.h:59
QList< Topic > TopicList
Definition topic.h:25