1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "typedescriptionreader.h"
30
31#include <QtQml/private/qqmljsparser_p.h>
32#include <QtQml/private/qqmljslexer_p.h>
33#include <QtQml/private/qqmljsengine_p.h>
34
35#include <QtCore/qdir.h>
36
37using namespace QQmlJS;
38using namespace QQmlJS::AST;
39
40QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
41{
42 QString result;
43
44 for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
45 if (iter != qualifiedId)
46 result += delimiter;
47
48 result += iter->name;
49 }
50
51 return result;
52}
53
54bool TypeDescriptionReader::operator()(
55 QHash<QString, ScopeTree::ConstPtr> *objects,
56 QList<ModuleApiInfo> *moduleApis,
57 QStringList *dependencies)
58{
59 Engine engine;
60
61 Lexer lexer(&engine);
62 Parser parser(&engine);
63
64 lexer.setCode(code: m_source, /*lineno = */ 1, /*qmlMode = */true);
65
66 if (!parser.parse()) {
67 m_errorMessage = QString::fromLatin1(str: "%1:%2: %3").arg(
68 QString::number(parser.errorLineNumber()),
69 QString::number(parser.errorColumnNumber()),
70 parser.errorMessage());
71 return false;
72 }
73
74 m_objects = objects;
75 m_moduleApis = moduleApis;
76 m_dependencies = dependencies;
77 readDocument(ast: parser.ast());
78
79 return m_errorMessage.isEmpty();
80}
81
82void TypeDescriptionReader::readDocument(UiProgram *ast)
83{
84 if (!ast) {
85 addError(loc: SourceLocation(), message: tr(sourceText: "Could not parse document."));
86 return;
87 }
88
89 if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast: ast->headers->headerItem)) {
90 addError(loc: SourceLocation(), message: tr(sourceText: "Expected a single import."));
91 return;
92 }
93
94 auto *import = cast<UiImport *>(ast: ast->headers->headerItem);
95 if (toString(qualifiedId: import->importUri) != QLatin1String("QtQuick.tooling")) {
96 addError(loc: import->importToken, message: tr(sourceText: "Expected import of QtQuick.tooling."));
97 return;
98 }
99
100 if (!import->version) {
101 addError(loc: import->firstSourceLocation(), message: tr(sourceText: "Import statement without version."));
102 return;
103 }
104
105 if (import->version->majorVersion != 1) {
106 addError(loc: import->version->firstSourceLocation(),
107 message: tr(sourceText: "Major version different from 1 not supported."));
108 return;
109 }
110
111 if (!ast->members || !ast->members->member || ast->members->next) {
112 addError(loc: SourceLocation(), message: tr(sourceText: "Expected document to contain a single object definition."));
113 return;
114 }
115
116 auto *module = cast<UiObjectDefinition *>(ast: ast->members->member);
117 if (!module) {
118 addError(loc: SourceLocation(), message: tr(sourceText: "Expected document to contain a single object definition."));
119 return;
120 }
121
122 if (toString(qualifiedId: module->qualifiedTypeNameId) != QLatin1String("Module")) {
123 addError(loc: SourceLocation(), message: tr(sourceText: "Expected document to contain a Module {} member."));
124 return;
125 }
126
127 readModule(ast: module);
128}
129
130void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
131{
132 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
133 UiObjectMember *member = it->member;
134 auto *component = cast<UiObjectDefinition *>(ast: member);
135
136 auto *script = cast<UiScriptBinding *>(ast: member);
137 if (script && (toString(qualifiedId: script->qualifiedId) == QStringLiteral("dependencies"))) {
138 readDependencies(ast: script);
139 continue;
140 }
141
142 QString typeName;
143 if (component)
144 typeName = toString(qualifiedId: component->qualifiedTypeNameId);
145
146 if (!component || (typeName != QLatin1String("Component")
147 && typeName != QLatin1String("ModuleApi"))) {
148 continue;
149 }
150
151 if (typeName == QLatin1String("Component"))
152 readComponent(ast: component);
153 else if (typeName == QLatin1String("ModuleApi"))
154 readModuleApi(ast: component);
155 }
156}
157
158void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
159{
160 m_errorMessage += QString::fromLatin1(str: "%1:%2:%3: %4\n").arg(
161 args: QDir::toNativeSeparators(pathName: m_fileName),
162 args: QString::number(loc.startLine),
163 args: QString::number(loc.startColumn),
164 args: message);
165}
166
167void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
168{
169 m_warningMessage += QString::fromLatin1(str: "%1:%2:%3: %4\n").arg(
170 args: QDir::toNativeSeparators(pathName: m_fileName),
171 args: QString::number(loc.startLine),
172 args: QString::number(loc.startColumn),
173 args: message);
174}
175
176void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
177{
178 auto *stmt = cast<ExpressionStatement*>(ast: ast->statement);
179 if (!stmt) {
180 addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected dependency definitions"));
181 return;
182 }
183 auto *exp = cast<ArrayPattern *>(ast: stmt->expression);
184 if (!exp) {
185 addError(loc: stmt->expression->firstSourceLocation(), message: tr(sourceText: "Expected dependency definitions"));
186 return;
187 }
188 for (PatternElementList *l = exp->elements; l; l = l->next) {
189 auto *str = cast<StringLiteral *>(ast: l->element->initializer);
190 *m_dependencies << str->value.toString();
191 }
192}
193
194void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
195{
196 ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope));
197
198 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
199 UiObjectMember *member = it->member;
200 auto *component = cast<UiObjectDefinition *>(ast: member);
201 auto *script = cast<UiScriptBinding *>(ast: member);
202 if (component) {
203 QString name = toString(qualifiedId: component->qualifiedTypeNameId);
204 if (name == QLatin1String("Property"))
205 readProperty(ast: component, scope);
206 else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
207 readSignalOrMethod(ast: component, isMethod: name == QLatin1String("Method"), scope);
208 else if (name == QLatin1String("Enum"))
209 readEnum(ast: component, scope);
210 else
211 addWarning(loc: component->firstSourceLocation(),
212 message: tr(sourceText: "Expected only Property, Method, Signal and Enum object definitions, "
213 "not \"%1\".").arg(a: name));
214 } else if (script) {
215 QString name = toString(qualifiedId: script->qualifiedId);
216 if (name == QLatin1String("name")) {
217 scope->setClassName(readStringBinding(ast: script));
218 } else if (name == QLatin1String("prototype")) {
219 scope->setSuperclassName(readStringBinding(ast: script));
220 } else if (name == QLatin1String("defaultProperty")) {
221 scope->setDefaultPropertyName(readStringBinding(ast: script));
222 } else if (name == QLatin1String("exports")) {
223 readExports(ast: script, scope);
224 } else if (name == QLatin1String("exportMetaObjectRevisions")) {
225 readMetaObjectRevisions(ast: script, scope);
226 } else if (name == QLatin1String("attachedType")) {
227 scope->setAttachedTypeName(readStringBinding(ast: script));
228 } else if (name == QLatin1String("isSingleton")) {
229 scope->setIsSingleton(readBoolBinding(ast: script));
230 } else if (name == QLatin1String("isCreatable")) {
231 scope->setIsCreatable(readBoolBinding(ast: script));
232 } else if (name == QLatin1String("isComposite")) {
233 scope->setIsComposite(readBoolBinding(ast: script));
234 } else {
235 addWarning(loc: script->firstSourceLocation(),
236 message: tr(sourceText: "Expected only name, prototype, defaultProperty, attachedType, "
237 "exports, isSingleton, isCreatable, isComposite and "
238 "exportMetaObjectRevisions script bindings, not \"%1\".").arg(a: name));
239 }
240 } else {
241 addWarning(loc: member->firstSourceLocation(),
242 message: tr(sourceText: "Expected only script bindings and object definitions."));
243 }
244 }
245
246 if (scope->className().isEmpty()) {
247 addError(loc: ast->firstSourceLocation(), message: tr(sourceText: "Component definition is missing a name binding."));
248 return;
249 }
250
251 // ### add implicit export into the package of c++ types
252 scope->addExport(name: scope->className(), QStringLiteral("<cpp>"), version: ComponentVersion());
253 m_objects->insert(akey: scope->className(), avalue: scope);
254}
255
256void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
257{
258 ModuleApiInfo apiInfo;
259
260 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
261 UiObjectMember *member = it->member;
262 auto *script = cast<UiScriptBinding *>(ast: member);
263
264 if (script) {
265 const QString name = toString(qualifiedId: script->qualifiedId);
266 if (name == QLatin1String("uri")) {
267 apiInfo.uri = readStringBinding(ast: script);
268 } else if (name == QLatin1String("version")) {
269 apiInfo.version = readNumericVersionBinding(ast: script);
270 } else if (name == QLatin1String("name")) {
271 apiInfo.cppName = readStringBinding(ast: script);
272 } else {
273 addWarning(loc: script->firstSourceLocation(),
274 message: tr(sourceText: "Expected only uri, version and name script bindings."));
275 }
276 } else {
277 addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected only script bindings."));
278 }
279 }
280
281 if (!apiInfo.version.isValid()) {
282 addError(loc: ast->firstSourceLocation(),
283 message: tr(sourceText: "ModuleApi definition has no or invalid version binding."));
284 return;
285 }
286
287 if (m_moduleApis)
288 m_moduleApis->append(t: apiInfo);
289}
290
291void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod,
292 const ScopeTree::Ptr &scope)
293{
294 MetaMethod metaMethod;
295 // ### confusion between Method and Slot. Method should be removed.
296 if (isMethod)
297 metaMethod.setMethodType(MetaMethod::Slot);
298 else
299 metaMethod.setMethodType(MetaMethod::Signal);
300
301 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
302 UiObjectMember *member = it->member;
303 auto *component = cast<UiObjectDefinition *>(ast: member);
304 auto *script = cast<UiScriptBinding *>(ast: member);
305 if (component) {
306 QString name = toString(qualifiedId: component->qualifiedTypeNameId);
307 if (name == QLatin1String("Parameter")) {
308 readParameter(ast: component, metaMethod: &metaMethod);
309 } else {
310 addWarning(loc: component->firstSourceLocation(),
311 message: tr(sourceText: "Expected only Parameter object definitions."));
312 }
313 } else if (script) {
314 QString name = toString(qualifiedId: script->qualifiedId);
315 if (name == QLatin1String("name")) {
316 metaMethod.setMethodName(readStringBinding(ast: script));
317 } else if (name == QLatin1String("type")) {
318 metaMethod.setReturnType(readStringBinding(ast: script));
319 } else if (name == QLatin1String("revision")) {
320 metaMethod.setRevision(readIntBinding(ast: script));
321 } else {
322 addWarning(loc: script->firstSourceLocation(),
323 message: tr(sourceText: "Expected only name and type script bindings."));
324 }
325 } else {
326 addWarning(loc: member->firstSourceLocation(),
327 message: tr(sourceText: "Expected only script bindings and object definitions."));
328 }
329 }
330
331 if (metaMethod.methodName().isEmpty()) {
332 addError(loc: ast->firstSourceLocation(),
333 message: tr(sourceText: "Method or signal is missing a name script binding."));
334 return;
335 }
336
337 scope->addMethod(method: metaMethod);
338}
339
340void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)
341{
342 QString name;
343 QString type;
344 bool isPointer = false;
345 bool isReadonly = false;
346 bool isList = false;
347 int revision = 0;
348
349 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
350 UiObjectMember *member = it->member;
351 auto *script = cast<UiScriptBinding *>(ast: member);
352 if (!script) {
353 addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected script binding."));
354 continue;
355 }
356
357 QString id = toString(qualifiedId: script->qualifiedId);
358 if (id == QLatin1String("name")) {
359 name = readStringBinding(ast: script);
360 } else if (id == QLatin1String("type")) {
361 type = readStringBinding(ast: script);
362 } else if (id == QLatin1String("isPointer")) {
363 isPointer = readBoolBinding(ast: script);
364 } else if (id == QLatin1String("isReadonly")) {
365 isReadonly = readBoolBinding(ast: script);
366 } else if (id == QLatin1String("isList")) {
367 isList = readBoolBinding(ast: script);
368 } else if (id == QLatin1String("revision")) {
369 revision = readIntBinding(ast: script);
370 } else {
371 addWarning(loc: script->firstSourceLocation(),
372 message: tr(sourceText: "Expected only type, name, revision, isPointer, isReadonly and"
373 " isList script bindings."));
374 }
375 }
376
377 if (name.isEmpty() || type.isEmpty()) {
378 addError(loc: ast->firstSourceLocation(),
379 message: tr(sourceText: "Property object is missing a name or type script binding."));
380 return;
381 }
382
383 scope->addProperty(prop: MetaProperty(name, type, isList, !isReadonly, isPointer, false, revision));
384}
385
386void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)
387{
388 MetaEnum metaEnum;
389
390 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
391 UiObjectMember *member = it->member;
392 auto *script = cast<UiScriptBinding *>(ast: member);
393 if (!script) {
394 addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected script binding."));
395 continue;
396 }
397
398 QString name = toString(qualifiedId: script->qualifiedId);
399 if (name == QLatin1String("name")) {
400 metaEnum.setName(readStringBinding(ast: script));
401 } else if (name == QLatin1String("alias")) {
402 metaEnum.setAlias(readStringBinding(ast: script));
403 } else if (name == QLatin1String("isFlag")) {
404 metaEnum.setIsFlag(readBoolBinding(ast: script));
405 } else if (name == QLatin1String("values")) {
406 readEnumValues(ast: script, metaEnum: &metaEnum);
407 } else {
408 addWarning(loc: script->firstSourceLocation(),
409 message: tr(sourceText: "Expected only name and values script bindings."));
410 }
411 }
412
413 scope->addEnum(fakeEnum: metaEnum);
414}
415
416void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, MetaMethod *metaMethod)
417{
418 QString name;
419 QString type;
420
421 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
422 UiObjectMember *member = it->member;
423 auto *script = cast<UiScriptBinding *>(ast: member);
424 if (!script) {
425 addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected script binding."));
426 continue;
427 }
428
429 const QString id = toString(qualifiedId: script->qualifiedId);
430 if (id == QLatin1String("name")) {
431 name = readStringBinding(ast: script);
432 } else if (id == QLatin1String("type")) {
433 type = readStringBinding(ast: script);
434 } else if (id == QLatin1String("isPointer")) {
435 // ### unhandled
436 } else if (id == QLatin1String("isReadonly")) {
437 // ### unhandled
438 } else if (id == QLatin1String("isList")) {
439 // ### unhandled
440 } else {
441 addWarning(loc: script->firstSourceLocation(),
442 message: tr(sourceText: "Expected only name and type script bindings."));
443 }
444 }
445
446 metaMethod->addParameter(name, type);
447}
448
449QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
450{
451 Q_ASSERT(ast);
452
453 if (!ast->statement) {
454 addError(loc: ast->colonToken, message: tr(sourceText: "Expected string after colon."));
455 return QString();
456 }
457
458 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
459 if (!expStmt) {
460 addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected string after colon."));
461 return QString();
462 }
463
464 auto *stringLit = cast<StringLiteral *>(ast: expStmt->expression);
465 if (!stringLit) {
466 addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected string after colon."));
467 return QString();
468 }
469
470 return stringLit->value.toString();
471}
472
473bool TypeDescriptionReader::readBoolBinding(UiScriptBinding *ast)
474{
475 Q_ASSERT(ast);
476
477 if (!ast->statement) {
478 addError(loc: ast->colonToken, message: tr(sourceText: "Expected boolean after colon."));
479 return false;
480 }
481
482 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
483 if (!expStmt) {
484 addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected boolean after colon."));
485 return false;
486 }
487
488 auto *trueLit = cast<TrueLiteral *>(ast: expStmt->expression);
489 auto *falseLit = cast<FalseLiteral *>(ast: expStmt->expression);
490 if (!trueLit && !falseLit) {
491 addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected true or false after colon."));
492 return false;
493 }
494
495 return trueLit;
496}
497
498double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
499{
500 Q_ASSERT(ast);
501
502 if (!ast->statement) {
503 addError(loc: ast->colonToken, message: tr(sourceText: "Expected numeric literal after colon."));
504 return 0;
505 }
506
507 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
508 if (!expStmt) {
509 addError(loc: ast->statement->firstSourceLocation(),
510 message: tr(sourceText: "Expected numeric literal after colon."));
511 return 0;
512 }
513
514 auto *numericLit = cast<NumericLiteral *>(ast: expStmt->expression);
515 if (!numericLit) {
516 addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected numeric literal after colon."));
517 return 0;
518 }
519
520 return numericLit->value;
521}
522
523ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
524{
525 ComponentVersion invalidVersion;
526
527 if (!ast || !ast->statement) {
528 addError(loc: (ast ? ast->colonToken : SourceLocation()),
529 message: tr(sourceText: "Expected numeric literal after colon."));
530 return invalidVersion;
531 }
532
533 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
534 if (!expStmt) {
535 addError(loc: ast->statement->firstSourceLocation(),
536 message: tr(sourceText: "Expected numeric literal after colon."));
537 return invalidVersion;
538 }
539
540 auto *numericLit = cast<NumericLiteral *>(ast: expStmt->expression);
541 if (!numericLit) {
542 addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected numeric literal after colon."));
543 return invalidVersion;
544 }
545
546 return ComponentVersion(m_source.mid(position: numericLit->literalToken.begin(),
547 n: numericLit->literalToken.length));
548}
549
550int TypeDescriptionReader::readIntBinding(UiScriptBinding *ast)
551{
552 double v = readNumericBinding(ast);
553 int i = static_cast<int>(v);
554
555 if (i != v) {
556 addError(loc: ast->firstSourceLocation(), message: tr(sourceText: "Expected integer after colon."));
557 return 0;
558 }
559
560 return i;
561}
562
563void TypeDescriptionReader::readExports(UiScriptBinding *ast, const ScopeTree::Ptr &scope)
564{
565 Q_ASSERT(ast);
566
567 if (!ast->statement) {
568 addError(loc: ast->colonToken, message: tr(sourceText: "Expected array of strings after colon."));
569 return;
570 }
571
572 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
573 if (!expStmt) {
574 addError(loc: ast->statement->firstSourceLocation(),
575 message: tr(sourceText: "Expected array of strings after colon."));
576 return;
577 }
578
579 auto *arrayLit = cast<ArrayPattern *>(ast: expStmt->expression);
580 if (!arrayLit) {
581 addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected array of strings after colon."));
582 return;
583 }
584
585 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
586 auto *stringLit = cast<StringLiteral *>(ast: it->element->initializer);
587 if (!stringLit) {
588 addError(loc: arrayLit->firstSourceLocation(),
589 message: tr(sourceText: "Expected array literal with only string literal members."));
590 return;
591 }
592 QString exp = stringLit->value.toString();
593 int slashIdx = exp.indexOf(c: QLatin1Char('/'));
594 int spaceIdx = exp.indexOf(c: QLatin1Char(' '));
595 ComponentVersion version(exp.mid(position: spaceIdx + 1));
596
597 if (spaceIdx == -1 || !version.isValid()) {
598 addError(loc: stringLit->firstSourceLocation(),
599 message: tr(sourceText: "Expected string literal to contain 'Package/Name major.minor' "
600 "or 'Name major.minor'."));
601 continue;
602 }
603 QString package;
604 if (slashIdx != -1)
605 package = exp.left(n: slashIdx);
606 QString name = exp.mid(position: slashIdx + 1, n: spaceIdx - (slashIdx+1));
607
608 // ### relocatable exports where package is empty?
609 scope->addExport(name, package, version);
610 }
611}
612
613void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast,
614 const ScopeTree::Ptr &scope)
615{
616 Q_ASSERT(ast);
617
618 if (!ast->statement) {
619 addError(loc: ast->colonToken, message: tr(sourceText: "Expected array of numbers after colon."));
620 return;
621 }
622
623 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
624 if (!expStmt) {
625 addError(loc: ast->statement->firstSourceLocation(),
626 message: tr(sourceText: "Expected array of numbers after colon."));
627 return;
628 }
629
630 auto *arrayLit = cast<ArrayPattern *>(ast: expStmt->expression);
631 if (!arrayLit) {
632 addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected array of numbers after colon."));
633 return;
634 }
635
636 int exportIndex = 0;
637 const int exportCount = scope->exports().size();
638 for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
639 auto *numberLit = cast<NumericLiteral *>(ast: it->element->initializer);
640 if (!numberLit) {
641 addError(loc: arrayLit->firstSourceLocation(),
642 message: tr(sourceText: "Expected array literal with only number literal members."));
643 return;
644 }
645
646 if (exportIndex >= exportCount) {
647 addError(loc: numberLit->firstSourceLocation(),
648 message: tr(sourceText: "Meta object revision without matching export."));
649 return;
650 }
651
652 const double v = numberLit->value;
653 const int metaObjectRevision = static_cast<int>(v);
654 if (metaObjectRevision != v) {
655 addError(loc: numberLit->firstSourceLocation(), message: tr(sourceText: "Expected integer."));
656 return;
657 }
658
659 scope->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
660 }
661}
662
663void TypeDescriptionReader::readEnumValues(UiScriptBinding *ast, MetaEnum *metaEnum)
664{
665 if (!ast)
666 return;
667 if (!ast->statement) {
668 addError(loc: ast->colonToken, message: tr(sourceText: "Expected object literal after colon."));
669 return;
670 }
671
672 auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement);
673 if (!expStmt) {
674 addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected expression after colon."));
675 return;
676 }
677
678 if (auto *objectLit = cast<ObjectPattern *>(ast: expStmt->expression)) {
679 for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
680 if (PatternProperty *assignement = it->property) {
681 if (auto *name = cast<StringLiteralPropertyName *>(ast: assignement->name)) {
682 metaEnum->addKey(key: name->id.toString());
683 continue;
684 }
685 }
686 addError(loc: it->firstSourceLocation(), message: tr(sourceText: "Expected strings as enum keys."));
687 }
688 } else if (auto *arrayLit = cast<ArrayPattern *>(ast: expStmt->expression)) {
689 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
690 if (PatternElement *element = it->element) {
691 if (auto *name = cast<StringLiteral *>(ast: element->initializer)) {
692 metaEnum->addKey(key: name->value.toString());
693 continue;
694 }
695 }
696 addError(loc: it->firstSourceLocation(), message: tr(sourceText: "Expected strings as enum keys."));
697 }
698 } else {
699 addError(loc: ast->statement->firstSourceLocation(),
700 message: tr(sourceText: "Expected either array or object literal as enum definition."));
701 }
702}
703

source code of qtdeclarative/tools/qmllint/typedescriptionreader.cpp