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"
14#include "qdoclogging.h"
18#include "tokenizer.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 Aggregate::PropertySearchType searchType = isAttached
169 QmlPropertyNode *n = parent->hasQmlProperty(qpa->m_name, searchType);
170 if (n == nullptr)
171 n = new QmlPropertyNode(parent, qpa->m_name, qpa->m_type, isAttached);
172 n->setIsList(qpa->m_isList);
173 // Use the const-overload of QmlPropertyNode::isReadOnly() as there's
174 // no associated C++ property to resolve the read-only status from
175 n->markReadOnly(const_cast<const QmlPropertyNode *>(qmlProperty)->isReadOnly()
176 && !isAttached);
177 if (qmlProperty->isDefault())
179 nodes << n;
180 }
181 } else
182 qCDebug(lcQdoc) << "Failed to parse QML property:" << topic << args;
183 } else if (topic == COMMAND_QMLSIGNAL || topic == COMMAND_QMLMETHOD ||
185 if (node->isFunction()) {
186 QmlSignatureParser qsp(static_cast<FunctionNode *>(node), args, doc.location());
187 if (nodes.size() > 1) {
188 doc.location().warning("\\%1 cannot be mixed with other topic commands"_L1.arg(topic));
189 nodes = {node};
190 }
191 break;
192 }
193 } else if (topic == COMMAND_QMLTYPE || topic == COMMAND_QMLSINGLETONTYPE ||
194 topic == COMMAND_QMLVALUETYPE || topic == COMMAND_QMLBASICTYPE) {
195 if (node->isQmlType()) {
196 if (nodes.size() > 1) {
197 doc.location().warning("\\%1 cannot be mixed with other topic commands"_L1.arg(topic));
198 nodes = {node};
199 }
200 break;
201 }
202 }
203 }
204 }
205
206 for (auto *n : nodes) {
207 applyMetacommands(loc, n, doc);
208 }
209
210 // Test if we're documenting a property group; We need three nodes at minimum:
211 // The property that acts as the `root` in the group, and two more to make a
212 // group. Each property must be prefixed with the root name + '.'.
213 bool isPropertyGroup{false};
214 if (nodes.size() > 2 && parent->isQmlType()) {
215 isPropertyGroup =
216 std::all_of(std::next(nodes.cbegin()), nodes.cend(), [node](const Node *p) {
217 return p->name().startsWith("%1."_L1.arg(node->name()));
218 });
219 }
220 // Construct a SharedCommentNode (SCN) representing a QML property group.
221 //
222 // Note that it's important to do this *after* constructing
223 // the topic nodes - which need to be written to index before the related
224 // SCN.
225 if (isPropertyGroup) {
226 // The first node in `nodes` is the group property;
227 // create SCN with the same name
228 QString group{nodes.takeFirst()->name()};
229 auto *scn = new SharedCommentNode(static_cast<QmlTypeNode*>(parent),
230 nodes.size(), group);
231 scn->setDoc(doc);
232 scn->setLocation(doc.startLocation());
233 applyMetacommands(loc, scn, doc);
234 for (const auto n : std::as_const(nodes))
235 scn->append(n);
236 scn->sort();
237 } else {
238 for (auto *n : nodes) {
239 n->setDoc(doc);
240 n->setLocation(doc.location());
241 }
242 }
243
244 m_usedComments.insert(loc.offset);
245 return node;
246}
247
248QmlSignatureParser::QmlSignatureParser(FunctionNode *func, const QString &signature,
249 const Location &loc)
250 : signature_(signature), func_(func), location_(loc)
251{
252 QByteArray latin1 = signature.toLatin1();
253 Tokenizer stringTokenizer(location_, std::move(latin1));
254 stringTokenizer.setParsingFnOrMacro(true);
255 tokenizer_ = &stringTokenizer;
258}
259
260/*!
261 If the current token matches \a target, read the next
262 token and return true. Otherwise, don't read the next
263 token, and return false.
264 */
265bool QmlSignatureParser::match(int target)
266{
267 if (tok_ == target) {
269 return true;
270 }
271 return false;
272}
273
274/*!
275 Parse a QML data type into \a type and an optional
276 variable name into \a var.
277 */
278bool QmlSignatureParser::matchTypeAndName(CodeChunk *type, QString *var)
279{
280 // Match code with scope operators, such as Alpha::Beta::Gamma::...::Omega.
281 for (;;) {
282 if (match(Tok_Ident)) {
283 type->append(previousLexeme());
284 } else {
285 // If not an identifier, try to match a sequence of modifiers.
286 bool hasModifiers = false;
288 || match(Tok_int64)) {
289 // Append the matched qualifier token.
290 type->append(previousLexeme());
291 hasModifiers = true;
292 }
293
294 // Match and append a type, or return false.
295 if (hasModifiers && (match(Tok_int) || match(Tok_char) || match(Tok_double))) {
296 type->append(previousLexeme());
297 } else if (!hasModifiers) {
300 type->append(previousLexeme());
301 } else {
302 return false;
303 }
304 }
305 }
306
307 // Match and append a scope operator, or break to go to the next stage.
309 type->append(previousLexeme());
310 else
311 break;
312 }
313
314 // Matches a sequence of & * const ^ tokens.
316 type->append(previousLexeme());
317
318 /*
319 The usual case: Look for an optional identifier, then for
320 some array brackets.
321 */
322 type->appendHotspot();
323
324 // Set the variable name if it is unset and an identifier is matched.
325 if ((var != nullptr) && match(Tok_Ident))
326 *var = previousLexeme();
327
328 // Skip pairs of braces and their contents.
329 if (tok_ == Tok_LeftBracket) {
330 int bracketDepth0 = tokenizer_->bracketDepth();
331 while ((tokenizer_->bracketDepth() >= bracketDepth0 && tok_ != Tok_Eoi)
332 || tok_ == Tok_RightBracket) {
333 type->append(lexeme());
335 }
336 }
337 return true;
338}
339
341{
342 QString name;
343 CodeChunk type;
344 CodeChunk defaultValue;
345
346 bool result = matchTypeAndName(&type, &name);
347 if (name.isEmpty()) {
348 name = type.toString();
349 type.clear();
350 }
351
352 if (!result)
353 return false;
354 if (match(Tok_Equal)) {
355 int parenDepth0 = tokenizer_->parenDepth();
356 while (tokenizer_->parenDepth() >= parenDepth0
357 && (tok_ != Tok_Comma || tokenizer_->parenDepth() > parenDepth0)
358 && tok_ != Tok_Eoi) {
359 defaultValue.append(lexeme());
361 }
362 }
363 func_->parameters().append(type.toString(), name, defaultValue.toString());
364 return true;
365}
366
368{
369 CodeChunk returnType;
370
371 qsizetype firstBlank = signature_.indexOf(QChar(' '));
372 qsizetype leftParen = signature_.indexOf(QChar('('));
373 if ((firstBlank > 0) && (leftParen - firstBlank) > 1) {
374 if (!matchTypeAndName(&returnType, nullptr))
375 return false;
376 }
377
378 while (match(Tok_Ident)) {
379 names_.append(previousLexeme());
381 previousLexeme();
382 names_.pop_back();
383 break;
384 }
385 }
386
387 if (tok_ != Tok_LeftParen)
388 return false;
389 /*
390 Parsing the parameters should be moved into class Parameters,
391 but it can wait. mws 14/12/2018
392 */
394
395 func_->setLocation(location_);
396 func_->setReturnType(returnType.toString());
397
398 if (tok_ != Tok_RightParen) {
400 do {
401 if (!matchParameter())
402 return false;
403 } while (match(Tok_Comma));
404 }
406 return false;
407 return true;
408}
409
410/*!
411 Applies the metacommands found in the comment.
412 */
413void QmlDocVisitor::applyMetacommands(QQmlJS::SourceLocation, Node *node, Doc &doc)
414{
416 QSet<QString> metacommands = doc.metaCommandsUsed();
417 if (metacommands.size() > 0) {
418 metacommands.subtract(m_topics);
419 for (const auto &command : std::as_const(metacommands)) {
420 const ArgList args = doc.metaCommandArgs(command);
421 if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
422 if (node->isQmlType()) {
423 node->setAbstract(true);
424 }
425 } else if (command == COMMAND_DEPRECATED) {
426 node->setDeprecated(args[0].second);
427 } else if (command == COMMAND_INQMLMODULE) {
428 qdb->addToQmlModule(args[0].first, node);
429 } else if (command == COMMAND_QMLINHERITS) {
430 if (node->name() == args[0].first)
431 doc.location().warning(
432 QStringLiteral("%1 tries to inherit itself").arg(args[0].first));
433 else if (node->isQmlType()) {
434 auto *qmlType = static_cast<QmlTypeNode *>(node);
435 qmlType->setQmlBaseName(args[0].first);
436 }
437 } else if (command == COMMAND_DEFAULT) {
438 if (!node->isQmlProperty()) {
439 doc.location().warning(QStringLiteral("Ignored '\\%1', applies only to '\\%2'")
440 .arg(command, COMMAND_QMLPROPERTY));
441 } else if (args.isEmpty() || args[0].first.isEmpty()) {
442 doc.location().warning(QStringLiteral("Expected an argument for '\\%1' (maybe you meant '\\%2'?)")
443 .arg(command, COMMAND_QMLDEFAULT));
444 } else {
445 static_cast<QmlPropertyNode *>(node)->setDefaultValue(args[0].first);
446 }
447 } else if (command == COMMAND_QMLDEFAULT) {
448 node->markDefault();
449 } else if (command == COMMAND_QMLENUMERATORSFROM) {
450 if (!node->isQmlProperty()) {
451 doc.location().warning("Ignored '\\%1', applies only to '\\%2'"_L1
452 .arg(command, COMMAND_QMLPROPERTY));
453 } else if (!static_cast<QmlPropertyNode*>(node)->nativeEnum()->resolve(args[0].first, args[0].second)) {
454 doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
455 .arg(command, args[0].first), "Use \\value commands instead"_L1);
456 }
457 } else if (command == COMMAND_QMLREADONLY) {
458 node->markReadOnly(1);
459 } else if (command == COMMAND_QMLREQUIRED) {
460 if (node->isQmlProperty())
461 static_cast<QmlPropertyNode *>(node)->setRequired();
462 } else if ((command == COMMAND_INGROUP) && !args.isEmpty()) {
463 for (const auto &argument : args)
464 QDocDatabase::qdocDB()->addToGroup(argument.first, node);
465 } else if (command == COMMAND_INTERNAL) {
466 node->setStatus(Status::Internal);
467 } else if (command == COMMAND_OBSOLETE) {
468 node->setStatus(Status::Deprecated);
469 } else if (command == COMMAND_PRELIMINARY) {
470 node->setStatus(Status::Preliminary);
471 } else if (command == COMMAND_SINCE) {
472 QString arg = args[0].first; //.join(' ');
473 node->setSince(arg);
474 } else if (command == COMMAND_WRAPPER) {
475 node->setWrapper();
476 } else {
477 doc.location().warning(
478 QStringLiteral("The \\%1 command is ignored in QML files").arg(command));
479 }
480 }
481 }
482}
483
484/*!
485 Reconstruct the qualified \a id using dot notation
486 and return the fully qualified string.
487 */
488QString QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id)
489{
490 QString result;
491 if (id) {
492 result = id->name.toString();
493 id = id->next;
494 while (id != nullptr) {
495 result += QChar('.') + id->name.toString();
496 id = id->next;
497 }
498 }
499 return result;
500}
501
502/*!
503 Begin the visit of the object \a definition, recording it in the
504 qdoc database. Increment the object nesting level, which is used
505 to test whether we are at the public API level. The public level
506 is level 1.
507
508 Defers the construction of a QmlTypeNode instance to
509 applyDocumentation(), by passing \c nullptr as the second
510 argument.
511 */
512bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition)
513{
514 QString type = getFullyQualifiedId(definition->qualifiedTypeNameId);
515 m_nestingLevel++;
516 if (m_current->isNamespace()) {
517 auto component = applyDocumentation(definition->firstSourceLocation(), nullptr);
518 Q_ASSERT(component);
519 auto *qmlTypeNode = static_cast<QmlTypeNode *>(component);
520 // Set base type name unless one was already provided with \inherits
521 if (!component->doc().isEmpty() && qmlTypeNode->qmlBaseName().isEmpty())
522 qmlTypeNode->setQmlBaseName(type);
523 qmlTypeNode->setImportList(m_importList);
524 m_importList.clear();
525 m_current = qmlTypeNode;
526 }
527
528 return true;
529}
530
531/*!
532 End the visit of the object \a definition. In particular,
533 decrement the object nesting level, which is used to test
534 whether we are at the public API level. The public API
535 level is level 1. It won't decrement below 0.
536 */
537void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *definition)
538{
539 if (m_nestingLevel > 0) {
540 --m_nestingLevel;
541 }
542 m_lastEndOffset = definition->lastSourceLocation().end();
543}
544
545bool QmlDocVisitor::visit(QQmlJS::AST::UiImport *import)
546{
547 QString name = m_document.mid(import->fileNameToken.offset, import->fileNameToken.length);
548 if (name[0] == '\"')
549 name = name.mid(1, name.size() - 2);
550 QString version;
551 if (import->version) {
552 const auto start = import->version->firstSourceLocation().begin();
553 const auto end = import->version->lastSourceLocation().end();
554 version = m_document.mid(start, end - start);
555 }
556 QString importUri = getFullyQualifiedId(import->importUri);
557 m_importList.append(ImportRec(std::move(name), std::move(version), std::move(importUri), import->importId));
558
559 return true;
560}
561
562void QmlDocVisitor::endVisit(QQmlJS::AST::UiImport *definition)
563{
564 m_lastEndOffset = definition->lastSourceLocation().end();
565}
566
567bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectBinding *)
568{
569 ++m_nestingLevel;
570 return true;
571}
572
573void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectBinding *)
574{
575 --m_nestingLevel;
576}
577
578bool QmlDocVisitor::visit(QQmlJS::AST::UiArrayBinding *)
579{
580 return true;
581}
582
583void QmlDocVisitor::endVisit(QQmlJS::AST::UiArrayBinding *) {}
584
585static QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)
586{
587 QString s;
588
589 for (QQmlJS::AST::UiQualifiedId *it = node; it; it = it->next) {
590 s.append(it->name);
591
592 if (it->next)
593 s.append(QLatin1Char('.'));
594 }
595
596 return s;
597}
598
599/*!
600 Visits the public \a member declaration, which can be a
601 signal or a property. It is a custom signal or property.
602 Only visit the \a member if the nestingLevel is 1.
603 */
604bool QmlDocVisitor::visit(QQmlJS::AST::UiPublicMember *member)
605{
606 if (m_nestingLevel > 1) {
607 return true;
608 }
609 switch (member->type) {
610 case QQmlJS::AST::UiPublicMember::Signal: {
611 if (m_current->isQmlType()) {
612 auto *qmlType = static_cast<QmlTypeNode *>(m_current);
613 if (qmlType) {
615 QString name = member->name.toString();
616 auto *newSignal = new FunctionNode(metaness, m_current, name);
617 Parameters &parameters = newSignal->parameters();
618 for (QQmlJS::AST::UiParameterList *it = member->parameters; it; it = it->next) {
619 const QString type = it->type ? it->type->toString() : QString();
620 if (!type.isEmpty() && !it->name.isEmpty())
621 parameters.append(type, it->name.toString());
622 }
623 applyDocumentation(member->firstSourceLocation(), newSignal);
624 }
625 }
626 break;
627 }
628 case QQmlJS::AST::UiPublicMember::Property: {
629 QString type = qualifiedIdToString(member->memberType);
630 if (m_current->isQmlType()) {
631 auto *qmlType = static_cast<QmlTypeNode *>(m_current);
632 if (qmlType) {
633 QString name = member->name.toString();
634 QmlPropertyNode *qmlPropNode = qmlType->hasQmlProperty(name);
635 if (qmlPropNode == nullptr)
636 qmlPropNode = new QmlPropertyNode(qmlType, std::move(name), std::move(type), false);
637 qmlPropNode->markReadOnly(member->isReadonly());
638 if (member->isDefaultMember())
639 qmlPropNode->markDefault();
640 if (member->requiredToken().isValid())
641 qmlPropNode->setRequired();
642 qmlPropNode->setIsList(member->typeModifier == "list"_L1);
643 applyDocumentation(member->firstSourceLocation(), qmlPropNode);
644 }
645 }
646 break;
647 }
648 default:
649 return false;
650 }
651
652 return true;
653}
654
655/*!
656 End the visit of the \a member.
657 */
658void QmlDocVisitor::endVisit(QQmlJS::AST::UiPublicMember *member)
659{
660 m_lastEndOffset = member->lastSourceLocation().end();
661}
662
663bool QmlDocVisitor::visit(QQmlJS::AST::IdentifierPropertyName *)
664{
665 return true;
666}
667
668/*!
669 Begin the visit of the function declaration \a fd, but only
670 if the nesting level is 1.
671 */
672bool QmlDocVisitor::visit(QQmlJS::AST::FunctionDeclaration *fd)
673{
674 if (m_nestingLevel <= 1) {
676 if (!m_current->isQmlType())
677 return true;
678 QString name = fd->name.toString();
679 auto *method = new FunctionNode(metaness, m_current, name);
680 Parameters &parameters = method->parameters();
681 QQmlJS::AST::FormalParameterList *formals = fd->formals;
682 if (formals) {
683 QQmlJS::AST::FormalParameterList *fp = formals;
684 do {
685 QString defaultValue;
686 auto initializer = fp->element->initializer;
687 if (initializer) {
688 auto loc = initializer->firstSourceLocation();
689 defaultValue = m_document.mid(loc.begin(), loc.length);
690 }
691 parameters.append(QString(), fp->element->bindingIdentifier.toString(),
692 defaultValue);
693 fp = fp->next;
694 } while (fp && fp != formals);
695 }
696 applyDocumentation(fd->firstSourceLocation(), method);
697 }
698 return true;
699}
700
701/*!
702 End the visit of the function declaration, \a fd.
703 */
704void QmlDocVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fd)
705{
706 m_lastEndOffset = fd->lastSourceLocation().end();
707}
708
709/*!
710 Begin the visit of the signal handler declaration \a sb, but only
711 if the nesting level is 1.
712
713 This visit is now deprecated. It has been decided to document
714 public signals. If a signal handler must be discussed in the
715 documentation, that discussion must take place in the comment
716 for the signal.
717 */
718bool QmlDocVisitor::visit(QQmlJS::AST::UiScriptBinding *)
719{
720 return true;
721}
722
723void QmlDocVisitor::endVisit(QQmlJS::AST::UiScriptBinding *sb)
724{
725 m_lastEndOffset = sb->lastSourceLocation().end();
726}
727
728bool QmlDocVisitor::visit(QQmlJS::AST::UiQualifiedId *)
729{
730 return true;
731}
732
733void QmlDocVisitor::endVisit(QQmlJS::AST::UiQualifiedId *)
734{
735 // nothing.
736}
737
739{
740 hasRecursionDepthError = true;
741}
742
744{
745 return hasRecursionDepthError;
746}
747
748QT_END_NAMESPACE
PropertySearchType
Definition aggregate.h:25
@ AttachedProperties
Definition aggregate.h:27
@ UnattachedProperties
Definition aggregate.h:26
Definition doc.h:32
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:89
const Location & startLocation() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:98
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
Definition doc.cpp:270
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:43
void setLineNo(int no)
Definition location.h:42
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.
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.
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:85
#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:89
#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
Combined button and popup list for selecting options.
QList< Node * > NodeList
Definition node.h:45
static QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:108
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:121
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:208
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:900
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:100
A class for parsing and managing a function parameter list.
Definition main.cpp:28
void clear()
Definition parameters.h:24
@ 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_const
Definition tokenizer.h:53
@ 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_Caret
Definition tokenizer.h:27
@ 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_Ampersand
Definition tokenizer.h:25
@ Tok_RightBracket
Definition tokenizer.h:42
@ Tok_Aster
Definition tokenizer.h:26
@ Tok_signed
Definition tokenizer.h:59
QList< Topic > TopicList
Definition topic.h:25