aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmllint/qmljstypedescriptionreader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmllint/qmljstypedescriptionreader.cpp')
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.cpp704
1 files changed, 704 insertions, 0 deletions
diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/qmljstypedescriptionreader.cpp
new file mode 100644
index 0000000000..542cdf99eb
--- /dev/null
+++ b/tools/qmllint/qmljstypedescriptionreader.cpp
@@ -0,0 +1,704 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmljstypedescriptionreader.h"
+
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsengine_p.h>
+
+#include <QDir>
+
+#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
+#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
+#define QTC_ASSERT_STRING(cond) qDebug() << (\
+ "\"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
+
+using namespace QQmlJS;
+using namespace QQmlJS::AST;
+using namespace LanguageUtils;
+
+QString toString(const AST::UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
+{
+ QString result;
+
+ for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
+ if (iter != qualifiedId)
+ result += delimiter;
+
+ result += iter->name;
+ }
+
+ return result;
+}
+
+TypeDescriptionReader::TypeDescriptionReader(const QString &fileName, const QString &data)
+ : _fileName (fileName), _source(data), _objects(0)
+{
+}
+
+TypeDescriptionReader::~TypeDescriptionReader()
+{
+}
+
+bool TypeDescriptionReader::operator()(
+ QHash<QString, FakeMetaObject::ConstPtr> *objects,
+ QList<ModuleApiInfo> *moduleApis,
+ QStringList *dependencies)
+{
+ Engine engine;
+
+ Lexer lexer(&engine);
+ Parser parser(&engine);
+
+ lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true);
+
+ if (!parser.parse()) {
+ _errorMessage = QString::fromLatin1("%1:%2: %3").arg(
+ QString::number(parser.errorLineNumber()),
+ QString::number(parser.errorColumnNumber()),
+ parser.errorMessage());
+ return false;
+ }
+
+ _objects = objects;
+ _moduleApis = moduleApis;
+ _dependencies = dependencies;
+ readDocument(parser.ast());
+
+ return _errorMessage.isEmpty();
+}
+
+QString TypeDescriptionReader::errorMessage() const
+{
+ return _errorMessage;
+}
+
+QString TypeDescriptionReader::warningMessage() const
+{
+ return _warningMessage;
+}
+
+void TypeDescriptionReader::readDocument(UiProgram *ast)
+{
+ if (!ast) {
+ addError(SourceLocation(), tr("Could not parse document."));
+ return;
+ }
+
+ if (!ast->headers || ast->headers->next || !AST::cast<AST::UiImport *>(ast->headers->headerItem)) {
+ addError(SourceLocation(), tr("Expected a single import."));
+ return;
+ }
+
+ UiImport *import = AST::cast<AST::UiImport *>(ast->headers->headerItem);
+ if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
+ addError(import->importToken, tr("Expected import of QtQuick.tooling."));
+ return;
+ }
+
+ ComponentVersion version;
+ const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
+ const int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1) {
+ version = ComponentVersion(versionString.leftRef(dotIdx).toInt(),
+ versionString.midRef(dotIdx + 1).toInt());
+ }
+ if (version.majorVersion() != 1) {
+ addError(import->versionToken, tr("Major version different from 1 not supported."));
+ return;
+ }
+
+ if (!ast->members || !ast->members->member || ast->members->next) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ UiObjectDefinition *module = AST::cast<UiObjectDefinition *>(ast->members->member);
+ if (!module) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
+ addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
+ return;
+ }
+
+ readModule(module);
+}
+
+void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
+{
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
+ readDependencies(script);
+ continue;
+ }
+
+ QString typeName;
+ if (component)
+ typeName = toString(component->qualifiedTypeNameId);
+
+ if (!component || (typeName != QLatin1String("Component") && typeName != QLatin1String("ModuleApi"))) {
+ continue;
+ }
+
+ if (typeName == QLatin1String("Component"))
+ readComponent(component);
+ else if (typeName == QLatin1String("ModuleApi"))
+ readModuleApi(component);
+ }
+}
+
+void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
+{
+ _errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
+{
+ _warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
+{
+ ExpressionStatement *stmt = AST::cast<ExpressionStatement*>(ast->statement);
+ if (!stmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ ArrayPattern *exp = AST::cast<ArrayPattern *>(stmt->expression);
+ if (!exp) {
+ addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ for (PatternElementList *l = exp->elements; l; l = l->next) {
+ //StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
+ StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
+ *_dependencies << str->value.toString();
+ }
+}
+
+void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
+{
+ FakeMetaObject::Ptr fmo(new FakeMetaObject);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Property"))
+ readProperty(component, fmo);
+ else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
+ readSignalOrMethod(component, name == QLatin1String("Method"), fmo);
+ else if (name == QLatin1String("Enum"))
+ readEnum(component, fmo);
+ else
+ addWarning(component->firstSourceLocation(),
+ tr("Expected only Property, Method, Signal and Enum object definitions, not \"%1\".")
+ .arg(name));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name")) {
+ fmo->setClassName(readStringBinding(script));
+ } else if (name == QLatin1String("prototype")) {
+ fmo->setSuperclassName(readStringBinding(script));
+ } else if (name == QLatin1String("defaultProperty")) {
+ fmo->setDefaultPropertyName(readStringBinding(script));
+ } else if (name == QLatin1String("exports")) {
+ readExports(script, fmo);
+ } else if (name == QLatin1String("exportMetaObjectRevisions")) {
+ readMetaObjectRevisions(script, fmo);
+ } else if (name == QLatin1String("attachedType")) {
+ fmo->setAttachedTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("isSingleton")) {
+ fmo->setIsSingleton(readBoolBinding(script));
+ } else if (name == QLatin1String("isCreatable")) {
+ fmo->setIsCreatable(readBoolBinding(script));
+ } else if (name == QLatin1String("isComposite")) {
+ fmo->setIsComposite(readBoolBinding(script));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name, prototype, defaultProperty, attachedType, exports, "
+ "isSingleton, isCreatable, isComposite and exportMetaObjectRevisions "
+ "script bindings, not \"%1\".").arg(name));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (fmo->className().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
+ return;
+ }
+
+ // ### add implicit export into the package of c++ types
+ fmo->addExport(fmo->className(), QStringLiteral("<cpp>"), ComponentVersion());
+ fmo->updateFingerprint();
+ _objects->insert(fmo->className(), fmo);
+}
+
+void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
+{
+ ModuleApiInfo apiInfo;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+
+ if (script) {
+ const QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("uri")) {
+ apiInfo.uri = readStringBinding(script);
+ } else if (name == QLatin1String("version")) {
+ apiInfo.version = readNumericVersionBinding(script);
+ } else if (name == QLatin1String("name")) {
+ apiInfo.cppName = readStringBinding(script);
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only uri, version and name script bindings."));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings."));
+ }
+ }
+
+ if (!apiInfo.version.isValid()) {
+ addError(ast->firstSourceLocation(), tr("ModuleApi definition has no or invalid version binding."));
+ return;
+ }
+
+ if (_moduleApis)
+ _moduleApis->append(apiInfo);
+}
+
+void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
+{
+ FakeMetaMethod fmm;
+ // ### confusion between Method and Slot. Method should be removed.
+ if (isMethod)
+ fmm.setMethodType(FakeMetaMethod::Slot);
+ else
+ fmm.setMethodType(FakeMetaMethod::Signal);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Parameter"))
+ readParameter(component, &fmm);
+ else
+ addWarning(component->firstSourceLocation(), tr("Expected only Parameter object definitions."));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name"))
+ fmm.setMethodName(readStringBinding(script));
+ else if (name == QLatin1String("type"))
+ fmm.setReturnType(readStringBinding(script));
+ else if (name == QLatin1String("revision"))
+ fmm.setRevision(readIntBinding(script));
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
+
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (fmm.methodName().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Method or signal is missing a name script binding."));
+ return;
+ }
+
+ fmo->addMethod(fmm);
+}
+
+void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
+{
+ QString name;
+ QString type;
+ bool isPointer = false;
+ bool isReadonly = false;
+ bool isList = false;
+ int revision = 0;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name"))
+ name = readStringBinding(script);
+ else if (id == QLatin1String("type"))
+ type = readStringBinding(script);
+ else if (id == QLatin1String("isPointer"))
+ isPointer = readBoolBinding(script);
+ else if (id == QLatin1String("isReadonly"))
+ isReadonly = readBoolBinding(script);
+ else if (id == QLatin1String("isList"))
+ isList = readBoolBinding(script);
+ else if (id == QLatin1String("revision"))
+ revision = readIntBinding(script);
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only type, name, revision, isPointer, isReadonly and isList script bindings."));
+ }
+
+ if (name.isEmpty() || type.isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Property object is missing a name or type script binding."));
+ return;
+ }
+
+ fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision));
+}
+
+void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
+{
+ FakeMetaEnum fme;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name"))
+ fme.setName(readStringBinding(script));
+ else if (name == QLatin1String("values"))
+ readEnumValues(script, &fme);
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only name and values script bindings."));
+ }
+
+ fmo->addEnum(fme);
+}
+
+void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm)
+{
+ QString name;
+ QString type;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ const QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name")) {
+ name = readStringBinding(script);
+ } else if (id == QLatin1String("type")) {
+ type = readStringBinding(script);
+ } else if (id == QLatin1String("isPointer")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isReadonly")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isList")) {
+ // ### unhandled
+ } else {
+ addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
+ }
+ }
+
+ fmm->addParameter(name, type);
+}
+
+QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return QString());
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected string after colon."));
+ return QString();
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ StringLiteral *stringLit = AST::cast<StringLiteral *>(expStmt->expression);
+ if (!stringLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ return stringLit->value.toString();
+}
+
+bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return false);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected boolean after colon."));
+ return false;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
+ return false;
+ }
+
+ TrueLiteral *trueLit = AST::cast<TrueLiteral *>(expStmt->expression);
+ FalseLiteral *falseLit = AST::cast<FalseLiteral *>(expStmt->expression);
+ if (!trueLit && !falseLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
+ return false;
+ }
+
+ return trueLit;
+}
+
+double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return qQNaN());
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ return numericLit->value;
+}
+
+ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
+{
+ ComponentVersion invalidVersion;
+
+ if (!ast || !ast->statement) {
+ addError((ast ? ast->colonToken : SourceLocation()), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length));
+}
+
+int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
+{
+ double v = readNumericBinding(ast);
+ int i = static_cast<int>(v);
+
+ if (i != v) {
+ addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
+ return 0;
+ }
+
+ return i;
+}
+
+void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
+{
+ QTC_ASSERT(ast, return);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of strings after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ StringLiteral *stringLit = AST::cast<StringLiteral *>(it->element->initializer);
+ if (!stringLit) {
+ addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only string literal members."));
+ return;
+ }
+ QString exp = stringLit->value.toString();
+ int slashIdx = exp.indexOf(QLatin1Char('/'));
+ int spaceIdx = exp.indexOf(QLatin1Char(' '));
+ ComponentVersion version(exp.mid(spaceIdx + 1));
+
+ if (spaceIdx == -1 || !version.isValid()) {
+ addError(stringLit->firstSourceLocation(), tr("Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'."));
+ continue;
+ }
+ QString package;
+ if (slashIdx != -1)
+ package = exp.left(slashIdx);
+ QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
+
+ // ### relocatable exports where package is empty?
+ fmo->addExport(name, package, version);
+ }
+}
+
+void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
+{
+ QTC_ASSERT(ast, return);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ int exportIndex = 0;
+ const int exportCount = fmo->exports().size();
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
+ NumericLiteral *numberLit = cast<NumericLiteral *>(it->element->initializer);
+ if (!numberLit) {
+ addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only number literal members."));
+ return;
+ }
+
+ if (exportIndex >= exportCount) {
+ addError(numberLit->firstSourceLocation(), tr("Meta object revision without matching export."));
+ return;
+ }
+
+ const double v = numberLit->value;
+ const int metaObjectRevision = static_cast<int>(v);
+ if (metaObjectRevision != v) {
+ addError(numberLit->firstSourceLocation(), tr("Expected integer."));
+ return;
+ }
+
+ fmo->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
+ }
+}
+
+void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
+{
+ if (!ast)
+ return;
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected object literal after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
+ return;
+ }
+
+ ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
+ if (!objectLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
+ return;
+ }
+
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
+ if (assignement) {
+ StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
+ NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
+ UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
+ if (minus)
+ value = AST::cast<NumericLiteral *>(minus->expression);
+ if (!propName || !value) {
+ addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
+ continue;
+ }
+
+ double v = value->value;
+ if (minus)
+ v = -v;
+ fme->addKey(propName->id.toString(), v);
+ continue;
+ }
+ PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
+ if (getterSetter)
+ addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
+ }
+}