diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2020-03-30 17:42:48 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2020-04-01 10:29:29 +0200 |
commit | c38ea80c5dd71a20eade7b3a3b619c1996c6af0b (patch) | |
tree | bd7eafa6e0e19f0aca3911c74ab99c7b8062a14a /tools/shared | |
parent | 4cf0962dc4d8d48aa600c5b56b160c8553782140 (diff) |
Move qmllint's metatype support to tools/shared
We want to read qmltypes files and analyze scopes also from other tools.
Furthermore, restructure the shared directory, so that each tool only
includes what it needs.
Change-Id: I96a2dcc8b1c5fac613592fb1867bf51fa5ef3a6e
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'tools/shared')
-rw-r--r-- | tools/shared/componentversion.cpp | 80 | ||||
-rw-r--r-- | tools/shared/componentversion.h | 66 | ||||
-rw-r--r-- | tools/shared/metatypes.h | 155 | ||||
-rw-r--r-- | tools/shared/scopetree.cpp | 172 | ||||
-rw-r--r-- | tools/shared/scopetree.h | 215 | ||||
-rw-r--r-- | tools/shared/shared.pri | 26 | ||||
-rw-r--r-- | tools/shared/typedescriptionreader.cpp | 658 | ||||
-rw-r--r-- | tools/shared/typedescriptionreader.h | 95 |
8 files changed, 1463 insertions, 4 deletions
diff --git a/tools/shared/componentversion.cpp b/tools/shared/componentversion.cpp new file mode 100644 index 0000000000..95403ec15f --- /dev/null +++ b/tools/shared/componentversion.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 "componentversion.h" +#include <QtCore/qstring.h> + +ComponentVersion::ComponentVersion(const QString &versionString) +{ + const int dotIdx = versionString.indexOf(QLatin1Char('.')); + if (dotIdx == -1) + return; + bool ok = false; + const int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok); + if (!ok) + return; + const int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok); + if (!ok) + return; + m_version = QTypeRevision::fromVersion(maybeMajor, maybeMinor); +} + +bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs) +{ + return lhs.version().majorVersion() < rhs.version().majorVersion() + || (lhs.version().majorVersion() == rhs.version().majorVersion() + && lhs.version().minorVersion() < rhs.version().minorVersion()); +} + +bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs) +{ + return lhs.version().majorVersion() < rhs.version().majorVersion() + || (lhs.version().majorVersion() == rhs.version().majorVersion() + && lhs.version().minorVersion() <= rhs.version().minorVersion()); +} + +bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs) +{ + return rhs < lhs; +} + +bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs) +{ + return rhs <= lhs; +} + +bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs) +{ + return lhs.version().majorVersion() == rhs.version().majorVersion() + && lhs.version().minorVersion() == rhs.version().minorVersion(); +} + +bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs) +{ + return !(lhs == rhs); +} diff --git a/tools/shared/componentversion.h b/tools/shared/componentversion.h new file mode 100644 index 0000000000..bbb039fc40 --- /dev/null +++ b/tools/shared/componentversion.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef COMPONENTVERSION_H +#define COMPONENTVERSION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include <QtCore/qglobal.h> +#include <QtCore/qversionnumber.h> + +class ComponentVersion +{ +public: + ComponentVersion() = default; + ComponentVersion(QTypeRevision version) : m_version(version) {} + explicit ComponentVersion(const QString &versionString); + + QTypeRevision version() const { return m_version; } + bool isValid() const { return m_version.hasMajorVersion() && m_version.hasMinorVersion(); } + +private: + QTypeRevision m_version; +}; + +bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs); +bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs); +bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs); +bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs); +bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs); +bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs); + +#endif // COMPONENTVERSION_H diff --git a/tools/shared/metatypes.h b/tools/shared/metatypes.h new file mode 100644 index 0000000000..d67de2edcd --- /dev/null +++ b/tools/shared/metatypes.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef METATYPES_H +#define METATYPES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> + +class MetaEnum +{ + QString m_name; + QStringList m_keys; + +public: + MetaEnum() = default; + explicit MetaEnum(QString name) : m_name(std::move(name)) {} + + bool isValid() const { return !m_name.isEmpty(); } + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; } + + void addKey(const QString &key) { m_keys.append(key); } + QStringList keys() const { return m_keys; } +}; + +class MetaMethod +{ +public: + enum Type { + Signal, + Slot, + Method + }; + + enum Access { + Private, + Protected, + Public + }; + + MetaMethod() = default; + explicit MetaMethod(QString name, QString returnType = QString()) + : m_name(std::move(name)) + , m_returnType(std::move(returnType)) + , m_methodType(Method) + , m_methodAccess(Public) + {} + + QString methodName() const { return m_name; } + void setMethodName(const QString &name) { m_name = name; } + + void setReturnType(const QString &type) { m_returnType = type; } + + QStringList parameterNames() const { return m_paramNames; } + QStringList parameterTypes() const { return m_paramTypes; } + void addParameter(const QString &name, const QString &type) + { + m_paramNames.append(name); + m_paramTypes.append(type); + } + + int methodType() const { return m_methodType; } + void setMethodType(Type methodType) { m_methodType = methodType; } + + Access access() const { return m_methodAccess; } + + int revision() const { return m_revision; } + void setRevision(int r) { m_revision = r; } + +private: + QString m_name; + QString m_returnType; + QStringList m_paramNames; + QStringList m_paramTypes; + Type m_methodType = Signal; + Access m_methodAccess = Private; + int m_revision = 0; +}; + +class ScopeTree; +class MetaProperty +{ + QString m_propertyName; + QString m_typeName; + const ScopeTree *m_type = nullptr; + bool m_isList; + bool m_isWritable; + bool m_isPointer; + bool m_isAlias; + int m_revision; + +public: + MetaProperty(QString propertyName, QString typeName, + bool isList, bool isWritable, bool isPointer, bool isAlias, + int revision) + : m_propertyName(std::move(propertyName)) + , m_typeName(std::move(typeName)) + , m_isList(isList) + , m_isWritable(isWritable) + , m_isPointer(isPointer) + , m_isAlias(isAlias) + , m_revision(revision) + {} + + QString propertyName() const { return m_propertyName; } + QString typeName() const { return m_typeName; } + + void setType(const ScopeTree *type) { m_type = type; } + const ScopeTree *type() const { return m_type; } + + bool isList() const { return m_isList; } + bool isWritable() const { return m_isWritable; } + bool isPointer() const { return m_isPointer; } + bool isAlias() const { return m_isAlias; } + int revision() const { return m_revision; } +}; + +#endif // METATYPES_H diff --git a/tools/shared/scopetree.cpp b/tools/shared/scopetree.cpp new file mode 100644 index 0000000000..870ba13fc4 --- /dev/null +++ b/tools/shared/scopetree.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** 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 "scopetree.h" + +#include <QtCore/qqueue.h> +#include <QtCore/qsharedpointer.h> + +#include <algorithm> + +ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope) + : m_parentScope(parentScope), m_name(std::move(name)), m_scopeType(type) {} + +ScopeTree::Ptr ScopeTree::createNewChildScope(ScopeType type, const QString &name) +{ + Q_ASSERT(type != ScopeType::QMLScope + || !m_parentScope + || m_parentScope->m_scopeType == ScopeType::QMLScope + || m_parentScope->m_name == QLatin1String("global")); + auto childScope = ScopeTree::Ptr(new ScopeTree{type, name, this}); + m_childScopes.push_back(childScope); + return childScope; +} + +void ScopeTree::insertJSIdentifier(const QString &id, ScopeType scope) +{ + Q_ASSERT(m_scopeType != ScopeType::QMLScope); + Q_ASSERT(scope != ScopeType::QMLScope); + if (scope == ScopeType::JSFunctionScope) { + auto targetScope = this; + while (targetScope->scopeType() != ScopeType::JSFunctionScope) + targetScope = targetScope->m_parentScope; + targetScope->m_jsIdentifiers.insert(id); + } else { + m_jsIdentifiers.insert(id); + } +} + +void ScopeTree::insertSignalIdentifier(const QString &id, const MetaMethod &method, + const QQmlJS::SourceLocation &loc, + bool hasMultilineHandlerBody) +{ + Q_ASSERT(m_scopeType == ScopeType::QMLScope); + m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody}); +} + +void ScopeTree::insertPropertyIdentifier(const MetaProperty &property) +{ + addProperty(property); + MetaMethod method(property.propertyName() + QLatin1String("Changed"), QLatin1String("void")); + addMethod(method); +} + +void ScopeTree::addUnmatchedSignalHandler(const QString &handler, + const QQmlJS::SourceLocation &location) +{ + m_unmatchedSignalHandlers.append(qMakePair(handler, location)); +} + +bool ScopeTree::isIdInCurrentScope(const QString &id) const +{ + return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id); +} + +void ScopeTree::addIdToAccessed(const QString &id, const QQmlJS::SourceLocation &location) { + m_memberAccessChains.append(QVector<FieldMember>()); + m_memberAccessChains.last().append(FieldMember {id, QString(), location}); +} + +void ScopeTree::accessMember(const QString &name, const QString &parentType, + const QQmlJS::SourceLocation &location) +{ + Q_ASSERT(!m_memberAccessChains.last().isEmpty()); + m_memberAccessChains.last().append(FieldMember {name, parentType, location }); +} + +bool ScopeTree::isVisualRootScope() const +{ + return m_parentScope && m_parentScope->m_parentScope + && m_parentScope->m_parentScope->m_parentScope == nullptr; +} + +bool ScopeTree::isIdInCurrentQMlScopes(const QString &id) const +{ + const auto *qmlScope = currentQMLScope(); + return qmlScope->m_properties.contains(id) + || qmlScope->m_methods.contains(id) + || qmlScope->m_enums.contains(id); +} + +bool ScopeTree::isIdInCurrentJSScopes(const QString &id) const +{ + auto jsScope = this; + while (jsScope) { + if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_jsIdentifiers.contains(id)) + return true; + jsScope = jsScope->m_parentScope; + } + return false; +} + +bool ScopeTree::isIdInjectedFromSignal(const QString &id) const +{ + return currentQMLScope()->m_injectedSignalIdentifiers.contains(id); +} + +const ScopeTree *ScopeTree::currentQMLScope() const +{ + auto qmlScope = this; + while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) + qmlScope = qmlScope->m_parentScope; + return qmlScope; +} + +void ScopeTree::addExport(const QString &name, const QString &package, + const ComponentVersion &version) +{ + m_exports.append(Export(package, name, version, 0)); +} + +void ScopeTree::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision) +{ + m_exports[exportIndex].setMetaObjectRevision(metaObjectRevision); +} + +void ScopeTree::updateParentProperty(const ScopeTree *scope) +{ + auto it = m_properties.find(QLatin1String("parent")); + if (it != m_properties.end() + && scope->name() != QLatin1String("Component") + && scope->name() != QLatin1String("program")) + it->setType(scope); +} + +ScopeTree::Export::Export(QString package, QString type, const ComponentVersion &version, + int metaObjectRevision) : + m_package(std::move(package)), + m_type(std::move(type)), + m_version(version), + m_metaObjectRevision(metaObjectRevision) +{ +} + +bool ScopeTree::Export::isValid() const +{ + return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty(); +} diff --git a/tools/shared/scopetree.h b/tools/shared/scopetree.h new file mode 100644 index 0000000000..e4e6a59ac2 --- /dev/null +++ b/tools/shared/scopetree.h @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SCOPETREE_H +#define SCOPETREE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include "metatypes.h" +#include "componentversion.h" + +#include <QtQml/private/qqmljssourcelocation_p.h> + +#include <QtCore/qset.h> +#include <QtCore/qhash.h> +#include <QtCore/qstring.h> + +enum class ScopeType +{ + JSFunctionScope, + JSLexicalScope, + QMLScope +}; + +struct MethodUsage +{ + MetaMethod method; + QQmlJS::SourceLocation loc; + bool hasMultilineHandlerBody; +}; + +class ScopeTree +{ + Q_DISABLE_COPY_MOVE(ScopeTree) +public: + using Ptr = QSharedPointer<ScopeTree>; + using ConstPtr = QSharedPointer<const ScopeTree>; + + class Export { + public: + Export() = default; + Export(QString package, QString type, const ComponentVersion &version, + int metaObjectRevision); + + bool isValid() const; + + int metaObjectRevision() const { return m_metaObjectRevision; } + void setMetaObjectRevision(int metaObjectRevision) + { + m_metaObjectRevision = metaObjectRevision; + } + + QString package() const { return m_package; } + QString type() const { return m_type; } + + private: + QString m_package; + QString m_type; + ComponentVersion m_version; + int m_metaObjectRevision = 0; + }; + + ScopeTree(ScopeType type, QString name = QString(), + ScopeTree *parentScope = nullptr); + + ScopeTree::Ptr createNewChildScope(ScopeType type, const QString &name); + ScopeTree *parentScope() const { return m_parentScope; } + + void insertJSIdentifier(const QString &id, ScopeType scope); + void insertSignalIdentifier(const QString &id, const MetaMethod &method, + const QQmlJS::SourceLocation &loc, bool hasMultilineHandlerBody); + // inserts property as qml identifier as well as the corresponding + void insertPropertyIdentifier(const MetaProperty &prop); + void addUnmatchedSignalHandler(const QString &handler, + const QQmlJS::SourceLocation &location); + + bool isIdInCurrentScope(const QString &id) const; + void addIdToAccessed(const QString &id, const QQmlJS::SourceLocation &location); + void accessMember(const QString &name, const QString &parentType, + const QQmlJS::SourceLocation &location); + + bool isVisualRootScope() const; + QString name() const { return m_name; } + + ScopeType scopeType() const { return m_scopeType; } + + void addMethods(const QHash<QString, MetaMethod> &methods) { m_methods.insert(methods); } + void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); } + QHash<QString, MetaMethod> methods() const { return m_methods; } + + void addEnum(const MetaEnum &fakeEnum) { m_enums.insert(fakeEnum.name(), fakeEnum); } + QHash<QString, MetaEnum> enums() const { return m_enums; } + + QString className() const { return m_className; } + void setClassName(const QString &name) { m_className = name; } + + void addExport(const QString &name, const QString &package, const ComponentVersion &version); + void setExportMetaObjectRevision(int exportIndex, int metaObjectRevision); + QList<Export> exports() const { return m_exports; } + + void setSuperclassName(const QString &superclass) { m_superName = superclass; } + QString superclassName() const { return m_superName; } + + void addProperty(const MetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); } + QHash<QString, MetaProperty> properties() const { return m_properties; } + void updateParentProperty(const ScopeTree *scope); + + QString defaultPropertyName() const { return m_defaultPropertyName; } + void setDefaultPropertyName(const QString &name) { m_defaultPropertyName = name; } + + QString attachedTypeName() const { return m_attachedTypeName; } + void setAttachedTypeName(const QString &name) { m_attachedTypeName = name; } + + bool isSingleton() const { return m_isSingleton; } + bool isCreatable() const { return m_isCreatable; } + bool isComposite() const { return m_isComposite; } + void setIsSingleton(bool value) { m_isSingleton = value; } + void setIsCreatable(bool value) { m_isCreatable = value; } + void setIsComposite(bool value) { m_isSingleton = value; } + + struct FieldMember + { + QString m_name; + QString m_parentType; + QQmlJS::SourceLocation m_location; + }; + + QVector<QPair<QString, QQmlJS::SourceLocation>> unmatchedSignalHandlers() const + { + return m_unmatchedSignalHandlers; + } + + QVector<QVector<FieldMember>> memberAccessChains() const + { + return m_memberAccessChains; + } + + bool isIdInCurrentQMlScopes(const QString &id) const; + bool isIdInCurrentJSScopes(const QString &id) const; + bool isIdInjectedFromSignal(const QString &id) const; + const ScopeTree *currentQMLScope() const; + + QVector<ScopeTree::Ptr> childScopes() const + { + return m_childScopes; + } + + QMultiHash<QString, MethodUsage> injectedSignalIdentifiers() const + { + return m_injectedSignalIdentifiers; + } + +private: + + QSet<QString> m_jsIdentifiers; + QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers; + + QHash<QString, MetaMethod> m_methods; + QHash<QString, MetaProperty> m_properties; + QHash<QString, MetaEnum> m_enums; + + QVector<QVector<FieldMember>> m_memberAccessChains; + QVector<QPair<QString, QQmlJS::SourceLocation>> m_unmatchedSignalHandlers; + + QVector<ScopeTree::Ptr> m_childScopes; + ScopeTree *m_parentScope; + + QString m_name; + QString m_className; + QString m_superName; + + ScopeType m_scopeType = ScopeType::QMLScope; + QList<Export> m_exports; + + QString m_defaultPropertyName; + QString m_attachedTypeName; + bool m_isSingleton = false; + bool m_isCreatable = true; + bool m_isComposite = false; +}; + +#endif // SCOPETREE_H diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri index 1438c3b3da..1b3cd4d37b 100644 --- a/tools/shared/shared.pri +++ b/tools/shared/shared.pri @@ -1,9 +1,27 @@ INCLUDEPATH += $$PWD -SOURCES += \ - $$PWD/resourcefilemapper.cpp \ +# The relevant tools need different bits and pieces. +# Furthermore, some of the classes require devtools, some not. + +RESOURCEFILEMAPPER_SOURCES = \ + $$PWD/resourcefilemapper.cpp + +RESOURCEFILEMAPPER_HEADERS = \ + $$PWD/resourcefilemapper.h + +METATYPEREADER_SOURCES = \ + $$PWD/componentversion.cpp \ + $$PWD/scopetree.cpp \ + $$PWD/typedescriptionreader.cpp + +METATYPEREADER_HEADERS = \ + $$PWD/componentversion.h \ + $$PWD/metatypes.h \ + $$PWD/scopetree.h \ + $$PWD/typedescriptionreader.h + +QMLSTREAMWRITER_SOURCES = \ $$PWD/qmlstreamwriter.cpp -HEADERS += \ - $$PWD/resourcefilemapper.h \ +QMLSTREAMWRITER_HEADERS = \ $$PWD/qmlstreamwriter.h diff --git a/tools/shared/typedescriptionreader.cpp b/tools/shared/typedescriptionreader.cpp new file mode 100644 index 0000000000..cc623b8288 --- /dev/null +++ b/tools/shared/typedescriptionreader.cpp @@ -0,0 +1,658 @@ +/**************************************************************************** +** +** 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 "typedescriptionreader.h" + +#include <QtQml/private/qqmljsparser_p.h> +#include <QtQml/private/qqmljslexer_p.h> +#include <QtQml/private/qqmljsengine_p.h> + +#include <QtCore/qdir.h> + +using namespace QQmlJS; +using namespace QQmlJS::AST; + +QString toString(const 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; +} + +bool TypeDescriptionReader::operator()( + QHash<QString, ScopeTree::ConstPtr> *objects, + QStringList *dependencies) +{ + Engine engine; + + Lexer lexer(&engine); + Parser parser(&engine); + + lexer.setCode(m_source, /*lineno = */ 1, /*qmlMode = */true); + + if (!parser.parse()) { + m_errorMessage = QString::fromLatin1("%1:%2: %3").arg( + QString::number(parser.errorLineNumber()), + QString::number(parser.errorColumnNumber()), + parser.errorMessage()); + return false; + } + + m_objects = objects; + m_dependencies = dependencies; + readDocument(parser.ast()); + + return m_errorMessage.isEmpty(); +} + +void TypeDescriptionReader::readDocument(UiProgram *ast) +{ + if (!ast) { + addError(SourceLocation(), tr("Could not parse document.")); + return; + } + + if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast->headers->headerItem)) { + addError(SourceLocation(), tr("Expected a single import.")); + return; + } + + auto *import = cast<UiImport *>(ast->headers->headerItem); + if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) { + addError(import->importToken, tr("Expected import of QtQuick.tooling.")); + return; + } + + if (!import->version) { + addError(import->firstSourceLocation(), tr("Import statement without version.")); + return; + } + + if (import->version->version.majorVersion() != 1) { + addError(import->version->firstSourceLocation(), + 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; + } + + auto *module = 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; + auto *component = cast<UiObjectDefinition *>(member); + + auto *script = 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")) { + continue; + } + + if (typeName == QLatin1String("Component")) + readComponent(component); + } +} + +void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message) +{ + m_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg( + QDir::toNativeSeparators(m_fileName), + QString::number(loc.startLine), + QString::number(loc.startColumn), + message); +} + +void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message) +{ + m_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg( + QDir::toNativeSeparators(m_fileName), + QString::number(loc.startLine), + QString::number(loc.startColumn), + message); +} + +void TypeDescriptionReader::readDependencies(UiScriptBinding *ast) +{ + auto *stmt = cast<ExpressionStatement*>(ast->statement); + if (!stmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions")); + return; + } + auto *exp = cast<ArrayPattern *>(stmt->expression); + if (!exp) { + addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions")); + return; + } + for (PatternElementList *l = exp->elements; l; l = l->next) { + auto *str = cast<StringLiteral *>(l->element->initializer); + *m_dependencies << str->value.toString(); + } +} + +void TypeDescriptionReader::readComponent(UiObjectDefinition *ast) +{ + ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope)); + + for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { + UiObjectMember *member = it->member; + auto *component = cast<UiObjectDefinition *>(member); + auto *script = cast<UiScriptBinding *>(member); + if (component) { + QString name = toString(component->qualifiedTypeNameId); + if (name == QLatin1String("Property")) + readProperty(component, scope); + else if (name == QLatin1String("Method") || name == QLatin1String("Signal")) + readSignalOrMethod(component, name == QLatin1String("Method"), scope); + else if (name == QLatin1String("Enum")) + readEnum(component, scope); + 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")) { + scope->setClassName(readStringBinding(script)); + } else if (name == QLatin1String("prototype")) { + scope->setSuperclassName(readStringBinding(script)); + } else if (name == QLatin1String("defaultProperty")) { + scope->setDefaultPropertyName(readStringBinding(script)); + } else if (name == QLatin1String("exports")) { + readExports(script, scope); + } else if (name == QLatin1String("exportMetaObjectRevisions")) { + readMetaObjectRevisions(script, scope); + } else if (name == QLatin1String("attachedType")) { + scope->setAttachedTypeName(readStringBinding(script)); + } else if (name == QLatin1String("isSingleton")) { + scope->setIsSingleton(readBoolBinding(script)); + } else if (name == QLatin1String("isCreatable")) { + scope->setIsCreatable(readBoolBinding(script)); + } else if (name == QLatin1String("isComposite")) { + scope->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 (scope->className().isEmpty()) { + addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding.")); + return; + } + + // ### add implicit export into the package of c++ types + scope->addExport(scope->className(), QStringLiteral("<cpp>"), ComponentVersion()); + m_objects->insert(scope->className(), scope); +} + +void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, + const ScopeTree::Ptr &scope) +{ + MetaMethod metaMethod; + // ### confusion between Method and Slot. Method should be removed. + if (isMethod) + metaMethod.setMethodType(MetaMethod::Slot); + else + metaMethod.setMethodType(MetaMethod::Signal); + + for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { + UiObjectMember *member = it->member; + auto *component = cast<UiObjectDefinition *>(member); + auto *script = cast<UiScriptBinding *>(member); + if (component) { + QString name = toString(component->qualifiedTypeNameId); + if (name == QLatin1String("Parameter")) { + readParameter(component, &metaMethod); + } else { + addWarning(component->firstSourceLocation(), + tr("Expected only Parameter object definitions.")); + } + } else if (script) { + QString name = toString(script->qualifiedId); + if (name == QLatin1String("name")) { + metaMethod.setMethodName(readStringBinding(script)); + } else if (name == QLatin1String("type")) { + metaMethod.setReturnType(readStringBinding(script)); + } else if (name == QLatin1String("revision")) { + metaMethod.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 (metaMethod.methodName().isEmpty()) { + addError(ast->firstSourceLocation(), + tr("Method or signal is missing a name script binding.")); + return; + } + + scope->addMethod(metaMethod); +} + +void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, const ScopeTree::Ptr &scope) +{ + 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; + auto *script = 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; + } + + scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, false, revision)); +} + +void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, const ScopeTree::Ptr &scope) +{ + MetaEnum metaEnum; + + for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { + UiObjectMember *member = it->member; + auto *script = cast<UiScriptBinding *>(member); + if (!script) { + addWarning(member->firstSourceLocation(), tr("Expected script binding.")); + continue; + } + + QString name = toString(script->qualifiedId); + if (name == QLatin1String("name")) { + metaEnum.setName(readStringBinding(script)); + } else if (name == QLatin1String("values")) { + readEnumValues(script, &metaEnum); + } else { + addWarning(script->firstSourceLocation(), + tr("Expected only name and values script bindings.")); + } + } + + scope->addEnum(metaEnum); +} + +void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, MetaMethod *metaMethod) +{ + QString name; + QString type; + + for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { + UiObjectMember *member = it->member; + auto *script = 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.")); + } + } + + metaMethod->addParameter(name, type); +} + +QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast) +{ + Q_ASSERT(ast); + + if (!ast->statement) { + addError(ast->colonToken, tr("Expected string after colon.")); + return QString(); + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected string after colon.")); + return QString(); + } + + auto *stringLit = cast<StringLiteral *>(expStmt->expression); + if (!stringLit) { + addError(expStmt->firstSourceLocation(), tr("Expected string after colon.")); + return QString(); + } + + return stringLit->value.toString(); +} + +bool TypeDescriptionReader::readBoolBinding(UiScriptBinding *ast) +{ + Q_ASSERT(ast); + + if (!ast->statement) { + addError(ast->colonToken, tr("Expected boolean after colon.")); + return false; + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon.")); + return false; + } + + auto *trueLit = cast<TrueLiteral *>(expStmt->expression); + auto *falseLit = cast<FalseLiteral *>(expStmt->expression); + if (!trueLit && !falseLit) { + addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon.")); + return false; + } + + return trueLit; +} + +double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast) +{ + Q_ASSERT(ast); + + if (!ast->statement) { + addError(ast->colonToken, tr("Expected numeric literal after colon.")); + return 0; + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected numeric literal after colon.")); + return 0; + } + + auto *numericLit = 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; + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected numeric literal after colon.")); + return invalidVersion; + } + + auto *numericLit = cast<NumericLiteral *>(expStmt->expression); + if (!numericLit) { + addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon.")); + return invalidVersion; + } + + return ComponentVersion(m_source.mid(numericLit->literalToken.begin(), + numericLit->literalToken.length)); +} + +int TypeDescriptionReader::readIntBinding(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, const ScopeTree::Ptr &scope) +{ + Q_ASSERT(ast); + + if (!ast->statement) { + addError(ast->colonToken, tr("Expected array of strings after colon.")); + return; + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected array of strings after colon.")); + return; + } + + auto *arrayLit = 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) { + auto *stringLit = 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? + scope->addExport(name, package, version); + } +} + +void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, + const ScopeTree::Ptr &scope) +{ + Q_ASSERT(ast); + + if (!ast->statement) { + addError(ast->colonToken, tr("Expected array of numbers after colon.")); + return; + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected array of numbers after colon.")); + return; + } + + auto *arrayLit = cast<ArrayPattern *>(expStmt->expression); + if (!arrayLit) { + addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon.")); + return; + } + + int exportIndex = 0; + const int exportCount = scope->exports().size(); + for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) { + auto *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; + } + + scope->setExportMetaObjectRevision(exportIndex, metaObjectRevision); + } +} + +void TypeDescriptionReader::readEnumValues(UiScriptBinding *ast, MetaEnum *metaEnum) +{ + if (!ast) + return; + if (!ast->statement) { + addError(ast->colonToken, tr("Expected object literal after colon.")); + return; + } + + auto *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon.")); + return; + } + + if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) { + for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { + if (PatternProperty *assignement = it->property) { + if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) { + metaEnum->addKey(name->id.toString()); + continue; + } + } + addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); + } + } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) { + for (PatternElementList *it = arrayLit->elements; it; it = it->next) { + if (PatternElement *element = it->element) { + if (auto *name = cast<StringLiteral *>(element->initializer)) { + metaEnum->addKey(name->value.toString()); + continue; + } + } + addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); + } + } else { + addError(ast->statement->firstSourceLocation(), + tr("Expected either array or object literal as enum definition.")); + } +} diff --git a/tools/shared/typedescriptionreader.h b/tools/shared/typedescriptionreader.h new file mode 100644 index 0000000000..2c86282163 --- /dev/null +++ b/tools/shared/typedescriptionreader.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef TYPEDESCRIPTIONREADER_H +#define TYPEDESCRIPTIONREADER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include "scopetree.h" + +#include <QtQml/private/qqmljsastfwd_p.h> + +// for Q_DECLARE_TR_FUNCTIONS +#include <QtCore/qcoreapplication.h> + +class TypeDescriptionReader +{ + Q_DECLARE_TR_FUNCTIONS(TypeDescriptionReader) +public: + TypeDescriptionReader() = default; + explicit TypeDescriptionReader(QString fileName, QString data) + : m_fileName(std::move(fileName)), m_source(std::move(data)) {} + + bool operator()( + QHash<QString, ScopeTree::ConstPtr> *objects, + QStringList *dependencies); + + QString errorMessage() const { return m_errorMessage; } + QString warningMessage() const { return m_warningMessage; } + +private: + void readDocument(QQmlJS::AST::UiProgram *ast); + void readModule(QQmlJS::AST::UiObjectDefinition *ast); + void readDependencies(QQmlJS::AST::UiScriptBinding *ast); + void readComponent(QQmlJS::AST::UiObjectDefinition *ast); + void readSignalOrMethod(QQmlJS::AST::UiObjectDefinition *ast, bool isMethod, + const ScopeTree::Ptr &scope); + void readProperty(QQmlJS::AST::UiObjectDefinition *ast, const ScopeTree::Ptr &scope); + void readEnum(QQmlJS::AST::UiObjectDefinition *ast, const ScopeTree::Ptr &scope); + void readParameter(QQmlJS::AST::UiObjectDefinition *ast, MetaMethod *metaMethod); + + QString readStringBinding(QQmlJS::AST::UiScriptBinding *ast); + bool readBoolBinding(QQmlJS::AST::UiScriptBinding *ast); + double readNumericBinding(QQmlJS::AST::UiScriptBinding *ast); + ComponentVersion readNumericVersionBinding(QQmlJS::AST::UiScriptBinding *ast); + int readIntBinding(QQmlJS::AST::UiScriptBinding *ast); + void readExports(QQmlJS::AST::UiScriptBinding *ast, const ScopeTree::Ptr &scope); + void readMetaObjectRevisions(QQmlJS::AST::UiScriptBinding *ast, const ScopeTree::Ptr &scope); + void readEnumValues(QQmlJS::AST::UiScriptBinding *ast, MetaEnum *metaEnum); + + void addError(const QQmlJS::SourceLocation &loc, const QString &message); + void addWarning(const QQmlJS::SourceLocation &loc, const QString &message); + + QString m_fileName; + QString m_source; + QString m_errorMessage; + QString m_warningMessage; + QHash<QString, ScopeTree::ConstPtr> *m_objects = nullptr; + QStringList *m_dependencies = nullptr; +}; + +#endif // TYPEDESCRIPTIONREADER_H |