qmllint: Cleanup

qmllint needed to be refactored before we can add any new functionality:
* Fix all the (C++) linter warnings
* Remove pointless namespaces
* Merge ScopeTree and FakeMetaObject into one class
* Remove the "Fake" from class and variable names
* Remove dead code
* Add "We mean it" warnings everywhere
* Unify #include style

This also "accidentally" fixes the automatic matching of signal handlers
in Connections elements to signals in their parent scopes.

Change-Id: Idf8daae34dfd6c0ee00da28b017f921da3eba25c
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Ulf Hermann 2019-11-11 18:18:04 +01:00
parent 61d7713ead
commit 6fb335ebce
18 changed files with 1057 additions and 1663 deletions

View File

@ -128,10 +128,10 @@ void TestQmllint::dirtyQmlCode_data()
<< QStringLiteral("failure1.js") << QStringLiteral("failure1.js")
<< QStringLiteral("failure1.js:4 : Expected token `;'") << QStringLiteral("failure1.js:4 : Expected token `;'")
<< QString(); << QString();
QTest::newRow("UnmatchedSignalHandler") QTest::newRow("AutomatchedSignalHandler")
<< QStringLiteral("UnmatchedSignalHandler.qml") << QStringLiteral("AutomatchedSignalHandler.qml")
<< QString("Warning: no matching signal found for handler \"onClicked\" at 12:13") << QString("Warning: unqualified access at 12:36")
<< QStringLiteral("onMouseXChanged"); << QStringLiteral("no matching signal found");
} }
void TestQmllint::dirtyQmlCode() void TestQmllint::dirtyQmlCode()

View File

@ -27,67 +27,24 @@
****************************************************************************/ ****************************************************************************/
#include "componentversion.h" #include "componentversion.h"
#include <QtCore/qstring.h>
#include <QString>
#include <QCryptographicHash>
#include <limits>
using namespace LanguageUtils;
const int ComponentVersion::NoVersion = -1;
const int ComponentVersion::MaxVersion = std::numeric_limits<int>::max();
ComponentVersion::ComponentVersion()
: _major(NoVersion), _minor(NoVersion)
{
}
ComponentVersion::ComponentVersion(int major, int minor)
: _major(major), _minor(minor)
{
}
ComponentVersion::ComponentVersion(const QString &versionString) ComponentVersion::ComponentVersion(const QString &versionString)
: _major(NoVersion), _minor(NoVersion)
{ {
int dotIdx = versionString.indexOf(QLatin1Char('.')); const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1) if (dotIdx == -1)
return; return;
bool ok = false; bool ok = false;
int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok); const int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok);
if (!ok) if (!ok)
return; return;
int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok); const int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok);
if (!ok) if (!ok)
return; return;
_major = maybeMajor; m_major = maybeMajor;
_minor = maybeMinor; m_minor = maybeMinor;
} }
ComponentVersion::~ComponentVersion()
{
}
bool ComponentVersion::isValid() const
{
return _major >= 0 && _minor >= 0;
}
QString ComponentVersion::toString() const
{
return QString::fromLatin1("%1.%2").arg(QString::number(_major),
QString::number(_minor));
}
void ComponentVersion::addToHash(QCryptographicHash &hash) const
{
hash.addData(reinterpret_cast<const char *>(&_major), sizeof(_major));
hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
}
namespace LanguageUtils {
bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs) bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
{ {
return lhs.majorVersion() < rhs.majorVersion() return lhs.majorVersion() < rhs.majorVersion()
@ -97,7 +54,8 @@ bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs) bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
{ {
return lhs.majorVersion() < rhs.majorVersion() return lhs.majorVersion() < rhs.majorVersion()
|| (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion()); || (lhs.majorVersion() == rhs.majorVersion()
&& lhs.minorVersion() <= rhs.minorVersion());
} }
bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs) bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
@ -112,12 +70,11 @@ bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs) bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
{ {
return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion(); return lhs.majorVersion() == rhs.majorVersion()
&& lhs.minorVersion() == rhs.minorVersion();
} }
bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs) bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
}

View File

@ -29,36 +29,35 @@
#ifndef COMPONENTVERSION_H #ifndef COMPONENTVERSION_H
#define COMPONENTVERSION_H #define COMPONENTVERSION_H
#include <qglobal.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.
QT_BEGIN_NAMESPACE #include <QtCore/qglobal.h>
class QCryptographicHash;
QT_END_NAMESPACE
namespace LanguageUtils {
class ComponentVersion class ComponentVersion
{ {
int _major;
int _minor;
public: public:
static const int NoVersion; static const int NoVersion = -1;
static const int MaxVersion;
ComponentVersion(); ComponentVersion() = default;
ComponentVersion(int major, int minor); ComponentVersion(int major, int minor) : m_major(major), m_minor(minor) {}
explicit ComponentVersion(const QString &versionString); explicit ComponentVersion(const QString &versionString);
~ComponentVersion();
int majorVersion() const int majorVersion() const { return m_major; }
{ return _major; } int minorVersion() const { return m_minor; }
int minorVersion() const
{ return _minor; }
bool isValid() const; bool isValid() const { return m_major >= 0 && m_minor >= 0; }
QString toString() const;
void addToHash(QCryptographicHash &hash) const; private:
int m_major = NoVersion;
int m_minor = NoVersion;
}; };
bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs); bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
@ -68,6 +67,4 @@ 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);
} // namespace LanguageUtils
#endif // COMPONENTVERSION_H #endif // COMPONENTVERSION_H

View File

@ -1,594 +0,0 @@
/****************************************************************************
**
** 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 "fakemetaobject.h"
#include <QCryptographicHash>
using namespace LanguageUtils;
FakeMetaEnum::FakeMetaEnum()
{}
FakeMetaEnum::FakeMetaEnum(const QString &name)
: m_name(name)
{}
bool FakeMetaEnum::isValid() const
{ return !m_name.isEmpty(); }
QString FakeMetaEnum::name() const
{ return m_name; }
void FakeMetaEnum::setName(const QString &name)
{ m_name = name; }
void FakeMetaEnum::addKey(const QString &key)
{ m_keys.append(key); }
QString FakeMetaEnum::key(int index) const
{ return m_keys.at(index); }
int FakeMetaEnum::keyCount() const
{ return m_keys.size(); }
QStringList FakeMetaEnum::keys() const
{ return m_keys; }
bool FakeMetaEnum::hasKey(const QString &key) const
{ return m_keys.contains(key); }
void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
{
int len = m_name.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
len = m_keys.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QString &key, m_keys) {
len = key.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
}
}
QString FakeMetaEnum::describe(int baseIndent) const
{
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Enum ");
res += name();
res += QLatin1String(": [");
for (int i = 0; i < keyCount(); ++i) {
res += newLine;
res += QLatin1String(" ");
res += key(i);
}
res += newLine;
res += QLatin1Char(']');
return res;
}
QString FakeMetaEnum::toString() const
{
return describe();
}
FakeMetaMethod::FakeMetaMethod(const QString &name, const QString &returnType)
: m_name(name)
, m_returnType(returnType)
, m_methodTy(FakeMetaMethod::Method)
, m_methodAccess(FakeMetaMethod::Public)
, m_revision(0)
{}
FakeMetaMethod::FakeMetaMethod()
: m_methodTy(FakeMetaMethod::Method)
, m_methodAccess(FakeMetaMethod::Public)
, m_revision(0)
{}
QString FakeMetaMethod::methodName() const
{ return m_name; }
void FakeMetaMethod::setMethodName(const QString &name)
{ m_name = name; }
void FakeMetaMethod::setReturnType(const QString &type)
{ m_returnType = type; }
QStringList FakeMetaMethod::parameterNames() const
{ return m_paramNames; }
QStringList FakeMetaMethod::parameterTypes() const
{ return m_paramTypes; }
void FakeMetaMethod::addParameter(const QString &name, const QString &type)
{ m_paramNames.append(name); m_paramTypes.append(type); }
int FakeMetaMethod::methodType() const
{ return m_methodTy; }
void FakeMetaMethod::setMethodType(int methodType)
{ m_methodTy = methodType; }
int FakeMetaMethod::access() const
{ return m_methodAccess; }
int FakeMetaMethod::revision() const
{ return m_revision; }
void FakeMetaMethod::setRevision(int r)
{ m_revision = r; }
void FakeMetaMethod::addToHash(QCryptographicHash &hash) const
{
int len = m_name.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
hash.addData(reinterpret_cast<const char *>(&m_methodAccess), sizeof(m_methodAccess));
hash.addData(reinterpret_cast<const char *>(&m_methodTy), sizeof(m_methodTy));
hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
len = m_paramNames.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QString &pName, m_paramNames) {
len = pName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(pName.constData()), len * sizeof(QChar));
}
len = m_paramTypes.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QString &pType, m_paramTypes) {
len = pType.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(pType.constData()), len * sizeof(QChar));
}
len = m_returnType.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_returnType.constData()), len * sizeof(QChar));
}
QString FakeMetaMethod::describe(int baseIndent) const
{
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Method {");
res += newLine;
res += QLatin1String(" methodName:");
res += methodName();
res += newLine;
res += QLatin1String(" methodType:");
res += methodType();
res += newLine;
res += QLatin1String(" parameterNames:[");
foreach (const QString &pName, parameterNames()) {
res += newLine;
res += QLatin1String(" ");
res += pName;
}
res += QLatin1Char(']');
res += newLine;
res += QLatin1String(" parameterTypes:[");
foreach (const QString &pType, parameterTypes()) {
res += newLine;
res += QLatin1String(" ");
res += pType;
}
res += QLatin1Char(']');
res += newLine;
res += QLatin1Char('}');
return res;
}
QString FakeMetaMethod::toString() const
{
return describe();
}
FakeMetaProperty::FakeMetaProperty(const QString &name, const QString &type, bool isList,
bool isWritable, bool isPointer, int revision)
: m_propertyName(name)
, m_type(type)
, m_isList(isList)
, m_isWritable(isWritable)
, m_isPointer(isPointer)
, m_revision(revision)
{}
QString FakeMetaProperty::name() const
{ return m_propertyName; }
QString FakeMetaProperty::typeName() const
{ return m_type; }
bool FakeMetaProperty::isList() const
{ return m_isList; }
bool FakeMetaProperty::isWritable() const
{ return m_isWritable; }
bool FakeMetaProperty::isPointer() const
{ return m_isPointer; }
int FakeMetaProperty::revision() const
{ return m_revision; }
void FakeMetaProperty::addToHash(QCryptographicHash &hash) const
{
int len = m_propertyName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_propertyName.constData()), len * sizeof(QChar));
hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
int flags = (m_isList ? (1 << 0) : 0)
+ (m_isPointer ? (1 << 1) : 0)
+ (m_isWritable ? (1 << 2) : 0);
hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags));
len = m_type.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_type.constData()), len * sizeof(QChar));
}
QString FakeMetaProperty::describe(int baseIndent) const
{
auto boolStr = [] (bool v) { return v ? QLatin1String("true") : QLatin1String("false"); };
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Property {");
res += newLine;
res += QLatin1String(" name:");
res += name();
res += newLine;
res += QLatin1String(" typeName:");
res += typeName();
res += newLine;
res += QLatin1String(" typeName:");
res += QString::number(revision());
res += newLine;
res += QLatin1String(" isList:");
res += boolStr(isList());
res += newLine;
res += QLatin1String(" isPointer:");
res += boolStr(isPointer());
res += newLine;
res += QLatin1String(" isWritable:");
res += boolStr(isWritable());
res += newLine;
res += QLatin1Char('}');
return res;
}
QString FakeMetaProperty::toString() const
{
return describe();
}
FakeMetaObject::FakeMetaObject() : m_isSingleton(false), m_isCreatable(true), m_isComposite(false)
{
}
QString FakeMetaObject::className() const
{ return m_className; }
void FakeMetaObject::setClassName(const QString &name)
{ m_className = name; }
void FakeMetaObject::addExport(const QString &name, const QString &package, ComponentVersion version)
{
Export exp;
exp.type = name;
exp.package = package;
exp.version = version;
m_exports.append(exp);
}
void FakeMetaObject::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
{
m_exports[exportIndex].metaObjectRevision = metaObjectRevision;
}
QList<FakeMetaObject::Export> FakeMetaObject::exports() const
{ return m_exports; }
FakeMetaObject::Export FakeMetaObject::exportInPackage(const QString &package) const
{
foreach (const Export &exp, m_exports) {
if (exp.package == package)
return exp;
}
return Export();
}
void FakeMetaObject::setSuperclassName(const QString &superclass)
{ m_superName = superclass; }
QString FakeMetaObject::superclassName() const
{ return m_superName; }
void FakeMetaObject::addEnum(const FakeMetaEnum &fakeEnum)
{ m_enumNameToIndex.insert(fakeEnum.name(), m_enums.size()); m_enums.append(fakeEnum); }
int FakeMetaObject::enumeratorCount() const
{ return m_enums.size(); }
int FakeMetaObject::enumeratorOffset() const
{ return 0; }
FakeMetaEnum FakeMetaObject::enumerator(int index) const
{ return m_enums.at(index); }
int FakeMetaObject::enumeratorIndex(const QString &name) const
{ return m_enumNameToIndex.value(name, -1); }
void FakeMetaObject::addProperty(const FakeMetaProperty &property)
{ m_propNameToIdx.insert(property.name(), m_props.size()); m_props.append(property); }
int FakeMetaObject::propertyCount() const
{ return m_props.size(); }
int FakeMetaObject::propertyOffset() const
{ return 0; }
FakeMetaProperty FakeMetaObject::property(int index) const
{ return m_props.at(index); }
int FakeMetaObject::propertyIndex(const QString &name) const
{ return m_propNameToIdx.value(name, -1); }
void FakeMetaObject::addMethod(const FakeMetaMethod &method)
{ m_methods.append(method); }
int FakeMetaObject::methodCount() const
{ return m_methods.size(); }
int FakeMetaObject::methodOffset() const
{ return 0; }
FakeMetaMethod FakeMetaObject::method(int index) const
{ return m_methods.at(index); }
int FakeMetaObject::methodIndex(const QString &name) const //If performances becomes an issue, just use a nameToIdx hash
{
for (int i=0; i<m_methods.count(); i++)
if (m_methods[i].methodName() == name)
return i;
return -1;
}
QString FakeMetaObject::defaultPropertyName() const
{ return m_defaultPropertyName; }
void FakeMetaObject::setDefaultPropertyName(const QString &defaultPropertyName)
{ m_defaultPropertyName = defaultPropertyName; }
QString FakeMetaObject::attachedTypeName() const
{ return m_attachedTypeName; }
void FakeMetaObject::setAttachedTypeName(const QString &name)
{ m_attachedTypeName = name; }
QByteArray FakeMetaObject::calculateFingerprint() const
{
QCryptographicHash hash(QCryptographicHash::Sha1);
int len = m_className.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_className.constData()), len * sizeof(QChar));
len = m_attachedTypeName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_attachedTypeName.constData()), len * sizeof(QChar));
len = m_defaultPropertyName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_defaultPropertyName.constData()), len * sizeof(QChar));
len = m_enumNameToIndex.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
{
QStringList keys(m_enumNameToIndex.keys());
keys.sort();
foreach (const QString &key, keys) {
len = key.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
int value = m_enumNameToIndex.value(key);
hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
m_enums.at(value).addToHash(hash);
}
}
len = m_exports.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const Export &e, m_exports)
e.addToHash(hash); // normalize order?
len = m_exports.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const FakeMetaMethod &m, m_methods)
m.addToHash(hash); // normalize order?
{
QStringList keys(m_propNameToIdx.keys());
keys.sort();
foreach (const QString &key, keys) {
len = key.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
int value = m_propNameToIdx.value(key);
hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
m_props.at(value).addToHash(hash);
}
}
len = m_superName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_superName.constData()), len * sizeof(QChar));
QByteArray res = hash.result();
res.append('F');
return res;
}
void FakeMetaObject::updateFingerprint()
{
m_fingerprint = calculateFingerprint();
}
QByteArray FakeMetaObject::fingerprint() const
{
return m_fingerprint;
}
bool FakeMetaObject::isSingleton() const
{
return m_isSingleton;
}
bool FakeMetaObject::isCreatable() const
{
return m_isCreatable;
}
bool FakeMetaObject::isComposite() const
{
return m_isComposite;
}
void FakeMetaObject::setIsSingleton(bool value)
{
m_isSingleton = value;
}
void FakeMetaObject::setIsCreatable(bool value)
{
m_isCreatable = value;
}
void FakeMetaObject::setIsComposite(bool value)
{
m_isSingleton = value;
}
QString FakeMetaObject::toString() const
{
return describe();
}
QString FakeMetaObject::describe(bool printDetails, int baseIndent) const
{
QString res = QString::fromLatin1("FakeMetaObject@%1")
.arg((quintptr)(void *)this, 0, 16);
if (!printDetails)
return res;
auto boolStr = [] (bool v) { return v ? QLatin1String("true") : QLatin1String("false"); };
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
res += QLatin1Char('{');
res += newLine;
res += QLatin1String("className:");
res += className();
res += newLine;
res += QLatin1String("superClassName:");
res += superclassName();
res += newLine;
res += QLatin1String("isSingleton:");
res += boolStr(isSingleton());
res += newLine;
res += QLatin1String("isCreatable:");
res += boolStr(isCreatable());
res += newLine;
res += QLatin1String("isComposite:");
res += boolStr(isComposite());
res += newLine;
res += QLatin1String("defaultPropertyName:");
res += defaultPropertyName();
res += newLine;
res += QLatin1String("attachedTypeName:");
res += attachedTypeName();
res += newLine;
res += QLatin1String("fingerprint:");
res += QString::fromUtf8(fingerprint());
res += newLine;
res += QLatin1String("exports:[");
foreach (const Export &e, exports()) {
res += newLine;
res += QLatin1String(" ");
res += e.describe(baseIndent + 2);
}
res += QLatin1Char(']');
res += newLine;
res += QLatin1String("enums:[");
for (int iEnum = 0; iEnum < enumeratorCount() ; ++ iEnum) {
FakeMetaEnum e = enumerator(enumeratorOffset() + iEnum);
res += newLine;
res += QLatin1String(" ");
res += e.describe(baseIndent + 2);
}
res += QLatin1Char(']');
res += newLine;
res += QLatin1String("properties:[");
for (int iProp = 0; iProp < propertyCount() ; ++ iProp) {
FakeMetaProperty prop = property(propertyOffset() + iProp);
res += newLine;
res += QLatin1String(" ");
res += prop.describe(baseIndent + 2);
}
res += QLatin1Char(']');
res += QLatin1String("methods:[");
for (int iMethod = 0; iMethod < methodOffset() ; ++ iMethod) {
FakeMetaMethod m = method(methodOffset() + iMethod);
res += newLine;
res += QLatin1String(" ");
m.describe(baseIndent + 2);
}
res += QLatin1Char(']');
res += newLine;
res += QLatin1Char('}');
return res;
}
FakeMetaObject::Export::Export()
: metaObjectRevision(0)
{}
bool FakeMetaObject::Export::isValid() const
{ return version.isValid() || !package.isEmpty() || !type.isEmpty(); }
void FakeMetaObject::Export::addToHash(QCryptographicHash &hash) const
{
int len = package.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(package.constData()), len * sizeof(QChar));
len = type.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(type.constData()), len * sizeof(QChar));
version.addToHash(hash);
hash.addData(reinterpret_cast<const char *>(&metaObjectRevision), sizeof(metaObjectRevision));
}
QString FakeMetaObject::Export::describe(int baseIndent) const
{
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Export {");
res += newLine;
res += QLatin1String(" package:");
res += package;
res += newLine;
res += QLatin1String(" type:");
res += type;
res += newLine;
res += QLatin1String(" version:");
res += version.toString();
res += newLine;
res += QLatin1String(" metaObjectRevision:");
res += QString::number(metaObjectRevision);
res += newLine;
res += QLatin1String(" isValid:");
res += QString::number(isValid());
res += newLine;
res += QLatin1Char('}');
return res;
}
QString FakeMetaObject::Export::toString() const
{
return describe();
}

View File

@ -1,235 +0,0 @@
/****************************************************************************
**
** 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 FAKEMETAOBJECT_H
#define FAKEMETAOBJECT_H
#include <QString>
#include <QStringList>
#include <QList>
#include <QHash>
#include <QSharedPointer>
#include "componentversion.h"
QT_BEGIN_NAMESPACE
class QCryptographicHash;
QT_END_NAMESPACE
namespace LanguageUtils {
class FakeMetaEnum {
QString m_name;
QStringList m_keys;
public:
FakeMetaEnum();
explicit FakeMetaEnum(const QString &name);
bool isValid() const;
QString name() const;
void setName(const QString &name);
void addKey(const QString &key);
QString key(int index) const;
int keyCount() const;
QStringList keys() const;
bool hasKey(const QString &key) const;
void addToHash(QCryptographicHash &hash) const;
QString describe(int baseIndent = 0) const;
QString toString() const;
};
class FakeMetaMethod {
public:
enum {
Signal,
Slot,
Method
};
enum {
Private,
Protected,
Public
};
public:
FakeMetaMethod();
explicit FakeMetaMethod(const QString &name, const QString &returnType = QString());
QString methodName() const;
void setMethodName(const QString &name);
void setReturnType(const QString &type);
QStringList parameterNames() const;
QStringList parameterTypes() const;
void addParameter(const QString &name, const QString &type);
int methodType() const;
void setMethodType(int methodType);
int access() const;
int revision() const;
void setRevision(int r);
void addToHash(QCryptographicHash &hash) const;
QString describe(int baseIndent = 0) const;
QString toString() const;
private:
QString m_name;
QString m_returnType;
QStringList m_paramNames;
QStringList m_paramTypes;
int m_methodTy;
int m_methodAccess;
int m_revision;
};
class FakeMetaProperty {
QString m_propertyName;
QString m_type;
bool m_isList;
bool m_isWritable;
bool m_isPointer;
int m_revision;
public:
FakeMetaProperty(const QString &name, const QString &type, bool isList, bool isWritable, bool isPointer, int revision);
QString name() const;
QString typeName() const;
bool isList() const;
bool isWritable() const;
bool isPointer() const;
int revision() const;
void addToHash(QCryptographicHash &hash) const;
QString describe(int baseIndent = 0) const;
QString toString() const;
};
class FakeMetaObject {
Q_DISABLE_COPY(FakeMetaObject);
public:
typedef QSharedPointer<FakeMetaObject> Ptr;
typedef QSharedPointer<const FakeMetaObject> ConstPtr;
class Export {
public:
Export();
QString package;
QString type;
ComponentVersion version;
int metaObjectRevision;
bool isValid() const;
void addToHash(QCryptographicHash &hash) const;
QString describe(int baseIndent = 0) const;
QString toString() const;
};
private:
QString m_className;
QList<Export> m_exports;
QString m_superName;
QList<FakeMetaEnum> m_enums;
QHash<QString, int> m_enumNameToIndex;
QList<FakeMetaProperty> m_props;
QHash<QString, int> m_propNameToIdx;
QList<FakeMetaMethod> m_methods;
QString m_defaultPropertyName;
QString m_attachedTypeName;
QByteArray m_fingerprint;
bool m_isSingleton;
bool m_isCreatable;
bool m_isComposite;
public:
FakeMetaObject();
QString className() const;
void setClassName(const QString &name);
void addExport(const QString &name, const QString &package, ComponentVersion version);
void setExportMetaObjectRevision(int exportIndex, int metaObjectRevision);
QList<Export> exports() const;
Export exportInPackage(const QString &package) const;
void setSuperclassName(const QString &superclass);
QString superclassName() const;
void addEnum(const FakeMetaEnum &fakeEnum);
int enumeratorCount() const;
int enumeratorOffset() const;
FakeMetaEnum enumerator(int index) const;
int enumeratorIndex(const QString &name) const;
void addProperty(const FakeMetaProperty &property);
int propertyCount() const;
int propertyOffset() const;
FakeMetaProperty property(int index) const;
int propertyIndex(const QString &name) const;
void addMethod(const FakeMetaMethod &method);
int methodCount() const;
int methodOffset() const;
FakeMetaMethod method(int index) const;
int methodIndex(const QString &name) const; // Note: Returns any method with that name in case of overloads
QString defaultPropertyName() const;
void setDefaultPropertyName(const QString &defaultPropertyName);
QString attachedTypeName() const;
void setAttachedTypeName(const QString &name);
QByteArray calculateFingerprint() const;
void updateFingerprint();
QByteArray fingerprint() const;
bool isSingleton() const;
bool isCreatable() const;
bool isComposite() const;
void setIsSingleton(bool value);
void setIsCreatable(bool value);
void setIsComposite(bool value);
QString describe(bool printDetails = true, int baseIndent = 0) const;
QString toString() const;
};
} // namespace LanguageUtils
#endif // FAKEMETAOBJECT_H

View File

@ -28,18 +28,17 @@
#include "findunqualified.h" #include "findunqualified.h"
#include "scopetree.h" #include "scopetree.h"
#include "typedescriptionreader.h"
#include "qmljstypedescriptionreader.h" #include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
#include <QtQml/private/qv4codegen_p.h>
#include <QtQml/private/qqmldirparser_p.h>
#include <QFile> #include <QtCore/qfile.h>
#include <QDirIterator> #include <QtCore/qdiriterator.h>
#include <QScopedValueRollback> #include <QtCore/qscopedvaluerollback.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmldirparser_p.h>
static QQmlDirParser createQmldirParserForFile(const QString &filename) static QQmlDirParser createQmldirParserForFile(const QString &filename)
{ {
@ -50,17 +49,17 @@ static QQmlDirParser createQmldirParserForFile(const QString &filename)
return parser; return parser;
} }
static QQmlJS::TypeDescriptionReader createQmltypesReaderForFile(QString const &filename) static TypeDescriptionReader createQmltypesReaderForFile(const QString &filename)
{ {
QFile f(filename); QFile f(filename);
f.open(QFile::ReadOnly); f.open(QFile::ReadOnly);
QQmlJS::TypeDescriptionReader reader { filename, f.readAll() }; TypeDescriptionReader reader { filename, f.readAll() };
return reader; return reader;
} }
void FindUnqualifiedIDVisitor::enterEnvironment(ScopeType type, QString name) void FindUnqualifiedIDVisitor::enterEnvironment(ScopeType type, QString name)
{ {
m_currentScope = m_currentScope->createNewChildScope(type, name); m_currentScope = m_currentScope->createNewChildScope(type, std::move(name));
} }
void FindUnqualifiedIDVisitor::leaveEnvironment() void FindUnqualifiedIDVisitor::leaveEnvironment()
@ -70,7 +69,8 @@ void FindUnqualifiedIDVisitor::leaveEnvironment()
enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath }; enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath };
QStringList completeImportPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin) QStringList completeImportPaths(const QString &uri, const QStringList &basePaths,
int vmaj, int vmin)
{ {
static const QLatin1Char Slash('/'); static const QLatin1Char Slash('/');
static const QLatin1Char Backslash('\\'); static const QLatin1Char Backslash('\\');
@ -85,15 +85,16 @@ QStringList completeImportPaths(const QString &uri, const QStringList &basePaths
{ {
if (version == FullyVersioned) { if (version == FullyVersioned) {
// extension with fully encoded version number (eg. MyModule.3.2) // extension with fully encoded version number (eg. MyModule.3.2)
return QString::asprintf(".%d.%d", vmaj, vmin); return QString::fromLatin1(".%1.%2").arg(vmaj).arg(vmin);
} else if (version == PartiallyVersioned) { }
if (version == PartiallyVersioned) {
// extension with encoded version major (eg. MyModule.3) // extension with encoded version major (eg. MyModule.3)
return QString::asprintf(".%d", vmaj); return QString::fromLatin1(".%1").arg(vmaj);
} // else extension without version number (eg. MyModule) }
// else extension without version number (eg. MyModule)
return QString(); return QString();
}; };
auto joinStringRefs = [](const QVector<QStringRef> &refs, const QChar &sep) auto joinStringRefs = [](const QVector<QStringRef> &refs, const QChar &sep) {
{
QString str; QString str;
for (auto it = refs.cbegin(); it != refs.cend(); ++it) { for (auto it = refs.cbegin(); it != refs.cend(); ++it) {
if (it != refs.cbegin()) if (it != refs.cbegin())
@ -131,19 +132,19 @@ QStringList completeImportPaths(const QString &uri, const QStringList &basePaths
return qmlDirPathsPaths; return qmlDirPathsPaths;
} }
void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int major, int minor) void FindUnqualifiedIDVisitor::importHelper(QString id, const QString &prefix, int major, int minor)
{ {
QPair<QString, QString> importId { id, prefix }; QPair<QString, QString> importId { id, prefix };
if (m_alreadySeenImports.contains(importId)) { if (m_alreadySeenImports.contains(importId))
return; return;
} else {
m_alreadySeenImports.insert(importId); m_alreadySeenImports.insert(importId);
}
id = id.replace(QLatin1String("/"), QLatin1String(".")); id = id.replace(QLatin1String("/"), QLatin1String("."));
auto qmltypesPaths = completeImportPaths(id, m_qmltypeDirs, major, minor); auto qmltypesPaths = completeImportPaths(id, m_qmltypeDirs, major, minor);
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects; QHash<QString, ScopeTree::ConstPtr> objects;
QList<QQmlJS::ModuleApiInfo> moduleApis; QList<ModuleApiInfo> moduleApis;
QStringList dependencies; QStringList dependencies;
static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes"); static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes");
static const QLatin1String SlashQmldir("/qmldir"); static const QLatin1String SlashQmldir("/qmldir");
@ -154,7 +155,7 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo
for (const QString &import : imports) for (const QString &import : imports)
importHelper(import, prefix, major, minor); importHelper(import, prefix, major, minor);
QHash<QString, LanguageUtils::FakeMetaObject *> qmlComponents; QHash<QString, ScopeTree *> qmlComponents;
const auto components = reader.components(); const auto components = reader.components();
for (auto it = components.begin(), end = components.end(); it != end; ++it) { for (auto it = components.begin(), end = components.end(); it != end; ++it) {
const QString filePath = qmltypesPath + QLatin1Char('/') + it->fileName; const QString filePath = qmltypesPath + QLatin1Char('/') + it->fileName;
@ -168,15 +169,15 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo
auto mo = qmlComponents.find(it.key()); auto mo = qmlComponents.find(it.key());
if (mo == qmlComponents.end()) if (mo == qmlComponents.end())
mo = qmlComponents.insert(it.key(), localQmlFile2FakeMetaObject(filePath)); mo = qmlComponents.insert(it.key(), localQmlFile2ScopeTree(filePath));
(*mo)->addExport( (*mo)->addExport(
it.key(), reader.typeNamespace(), it.key(), reader.typeNamespace(),
LanguageUtils::ComponentVersion(it->majorVersion, it->minorVersion)); ComponentVersion(it->majorVersion, it->minorVersion));
} }
for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) { for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) {
objects.insert(it.key(), objects.insert(it.key(),
QSharedPointer<const LanguageUtils::FakeMetaObject>(it.value())); QSharedPointer<const ScopeTree>(it.value()));
} }
} }
if (QFile::exists(qmltypesPath + SlashPluginsDotQmltypes)) { if (QFile::exists(qmltypesPath + SlashPluginsDotQmltypes)) {
@ -188,7 +189,7 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo
} }
for (auto const &dependency : qAsConst(dependencies)) { for (auto const &dependency : qAsConst(dependencies)) {
auto const split = dependency.split(" "); auto const split = dependency.split(" ");
auto const id = split.at(0); auto const &id = split.at(0);
auto const major = split.at(1).split('.').at(0).toInt(); auto const major = split.at(1).split('.').at(0).toInt();
auto const minor = split.at(1).split('.').at(1).toInt(); auto const minor = split.at(1).split('.').at(1).toInt();
importHelper(id, QString(), major, minor); importHelper(id, QString(), major, minor);
@ -196,27 +197,28 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo
// add objects // add objects
for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) { for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) {
auto val = ob_it.value(); auto val = ob_it.value();
m_exportedName2MetaObject[prefix + val->className()] = val; m_exportedName2Scope[prefix + val->className()] = val;
for (auto export_ : val->exports()) {
m_exportedName2MetaObject[prefix + export_.type] = val; const auto exports = val->exports();
} for (const auto &valExport : exports)
for (auto enumCount = 0; enumCount < val->enumeratorCount(); ++enumCount) { m_exportedName2Scope[prefix + valExport.type()] = val;
m_currentScope->insertQMLIdentifier(val->enumerator(enumCount).name());
} const auto enums = val->enums();
for (const auto &valEnum : enums)
m_currentScope->addEnum(valEnum);
} }
} }
LanguageUtils::FakeMetaObject * ScopeTree *FindUnqualifiedIDVisitor::localQmlFile2ScopeTree(const QString &filePath)
FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
{ {
using namespace QQmlJS::AST; using namespace QQmlJS::AST;
auto fake = new LanguageUtils::FakeMetaObject; auto scope = new ScopeTree(ScopeType::QMLScope);
QString baseName = QFileInfo { filePath }.baseName(); QString baseName = QFileInfo { filePath }.baseName();
fake->setClassName(baseName.endsWith(".ui") ? baseName.chopped(3) : baseName); scope->setClassName(baseName.endsWith(".ui") ? baseName.chopped(3) : baseName);
QFile file(filePath); QFile file(filePath);
if (!file.open(QFile::ReadOnly)) { if (!file.open(QFile::ReadOnly))
return fake; return scope;
}
QString code = file.readAll(); QString code = file.readAll();
file.close(); file.close();
@ -226,7 +228,7 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
lexer.setCode(code, 1, true); lexer.setCode(code, 1, true);
QQmlJS::Parser parser(&engine); QQmlJS::Parser parser(&engine);
if (!parser.parse()) { if (!parser.parse()) {
return fake; return scope;
} }
QQmlJS::AST::UiProgram *program = parser.ast(); QQmlJS::AST::UiProgram *program = parser.ast();
auto header = program->headers; auto header = program->headers;
@ -245,7 +247,8 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
if (import->asToken.isValid()) { if (import->asToken.isValid()) {
prefix += import->importId + QLatin1Char('.'); prefix += import->importId + QLatin1Char('.');
} }
importHelper(path, prefix, import->version->majorVersion, import->version->minorVersion); importHelper(path, prefix, import->version->majorVersion,
import->version->minorVersion);
} }
} }
header = header->next; header = header->next;
@ -254,12 +257,12 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
// member should be the sole element // member should be the sole element
Q_ASSERT(!member->next); Q_ASSERT(!member->next);
Q_ASSERT(member && member->member->kind == UiObjectMember::Kind_UiObjectDefinition); Q_ASSERT(member && member->member->kind == UiObjectMember::Kind_UiObjectDefinition);
auto definition = static_cast<UiObjectDefinition *>(member->member); auto definition = cast<UiObjectDefinition *>(member->member);
auto qualifiedId = definition->qualifiedTypeNameId; auto qualifiedId = definition->qualifiedTypeNameId;
while (qualifiedId && qualifiedId->next) { while (qualifiedId && qualifiedId->next) {
qualifiedId = qualifiedId->next; qualifiedId = qualifiedId->next;
} }
fake->setSuperclassName(qualifiedId->name.toString()); scope->setSuperclassName(qualifiedId->name.toString());
UiObjectMemberList *initMembers = definition->initializer->members; UiObjectMemberList *initMembers = definition->initializer->members;
while (initMembers) { while (initMembers) {
switch (initMembers->member->kind) { switch (initMembers->member->kind) {
@ -280,28 +283,30 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
break; break;
} }
case UiObjectMember::Kind_UiPublicMember: { case UiObjectMember::Kind_UiPublicMember: {
auto publicMember = static_cast<UiPublicMember *>(initMembers->member); auto publicMember = cast<UiPublicMember *>(initMembers->member);
switch (publicMember->type) { switch (publicMember->type) {
case UiPublicMember::Signal: { case UiPublicMember::Signal: {
UiParameterList *param = publicMember->parameters; UiParameterList *param = publicMember->parameters;
LanguageUtils::FakeMetaMethod method; MetaMethod method;
method.setMethodType(LanguageUtils::FakeMetaMethod::Signal); method.setMethodType(MetaMethod::Signal);
method.setMethodName(publicMember->name.toString()); method.setMethodName(publicMember->name.toString());
while (param) { while (param) {
method.addParameter(param->name.toString(), param->type->name.toString()); method.addParameter(param->name.toString(), param->type->name.toString());
param = param->next; param = param->next;
} }
fake->addMethod(method); scope->addMethod(method);
break; break;
} }
case UiPublicMember::Property: { case UiPublicMember::Property: {
LanguageUtils::FakeMetaProperty fakeprop { publicMember->name.toString(), const MetaProperty property {
publicMember->typeModifier.toString(), publicMember->name.toString(),
false, publicMember->typeModifier.toString(),
false, false,
false, false,
0 }; false,
fake->addProperty(fakeprop); 0
};
scope->addProperty(property);
break; break;
} }
} }
@ -312,24 +317,21 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
break; break;
} }
case UiObjectMember::Kind_UiSourceElement: { case UiObjectMember::Kind_UiSourceElement: {
auto sourceElement = static_cast<UiSourceElement *>(initMembers->member); auto sourceElement = cast<UiSourceElement *>(initMembers->member);
if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) { if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
LanguageUtils::FakeMetaMethod method; MetaMethod method;
method.setMethodName(fexpr->name.toString()); method.setMethodName(fexpr->name.toString());
method.setMethodType(LanguageUtils::FakeMetaMethod::Method); method.setMethodType(MetaMethod::Method);
FormalParameterList *parameters = fexpr->formals; FormalParameterList *parameters = fexpr->formals;
while (parameters) { while (parameters) {
method.addParameter(parameters->element->bindingIdentifier.toString(), method.addParameter(parameters->element->bindingIdentifier.toString(), "");
"");
parameters = parameters->next; parameters = parameters->next;
} }
fake->addMethod(method); scope->addMethod(method);
} else if (ClassExpression *clexpr = } else if (ClassExpression *clexpr =
sourceElement->sourceElement->asClassDefinition()) { sourceElement->sourceElement->asClassDefinition()) {
LanguageUtils::FakeMetaProperty prop { const MetaProperty prop { clexpr->name.toString(), "", false, false, false, 1 };
clexpr->name.toString(), "", false, false, false, 1 scope->addProperty(prop);
};
fake->addProperty(prop);
} else if (cast<VariableStatement *>(sourceElement->sourceElement)) { } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
// nothing to do // nothing to do
} else { } else {
@ -348,7 +350,7 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
} }
initMembers = initMembers->next; initMembers = initMembers->next;
} }
return fake; return scope;
} }
void FindUnqualifiedIDVisitor::importDirectory(const QString &directory, const QString &prefix) void FindUnqualifiedIDVisitor::importDirectory(const QString &directory, const QString &prefix)
@ -360,34 +362,30 @@ void FindUnqualifiedIDVisitor::importDirectory(const QString &directory, const Q
QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter }; QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
while (it.hasNext()) { while (it.hasNext()) {
LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next()); const ScopeTree *scope = localQmlFile2ScopeTree(it.next());
m_exportedName2MetaObject.insert( m_exportedName2Scope.insert(prefix + scope->className(), ScopeTree::ConstPtr(scope));
prefix + fake->className(),
QSharedPointer<const LanguageUtils::FakeMetaObject>(fake));
} }
} }
void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString name) void FindUnqualifiedIDVisitor::importExportedNames(const QStringRef &prefix, QString name)
{ {
for (;;) { for (;;) {
auto metaObject = m_exportedName2MetaObject[m_exportedName2MetaObject.contains(name) auto scope = m_exportedName2Scope[m_exportedName2Scope.contains(name)
? name ? name
: prefix + QLatin1Char('.') + name]; : prefix + QLatin1Char('.') + name];
if (metaObject) { if (scope) {
auto propertyCount = metaObject->propertyCount(); const auto properties = scope->properties();
for (auto i = 0; i < propertyCount; ++i) { for (const auto &property : properties)
m_currentScope->insertPropertyIdentifier(metaObject->property(i).name()); m_currentScope->insertPropertyIdentifier(property);
}
m_currentScope->addMethodsFromMetaObject(metaObject); m_currentScope->addMethods(scope->methods());
name = scope->superclassName();
name = metaObject->superclassName(); if (name.isEmpty() || name == QLatin1String("QObject"))
if (name.isEmpty() || name == QLatin1String("QObject")) {
break; break;
}
} else { } else {
m_colorOut.write(QLatin1String("warning: "), Warning); m_colorOut.write(QLatin1String("warning: "), Warning);
m_colorOut.write(name + QLatin1String(" was not found. Did you add all import paths?\n")); m_colorOut.write(name + QLatin1String(" was not found."
" Did you add all import paths?\n"));
m_unknownImports.insert(name); m_unknownImports.insert(name);
break; break;
} }
@ -404,8 +402,8 @@ void FindUnqualifiedIDVisitor::throwRecursionDepthError()
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
{ {
enterEnvironment(ScopeType::QMLScope, "program"); enterEnvironment(ScopeType::QMLScope, "program");
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects; QHash<QString, ScopeTree::ConstPtr> objects;
QList<QQmlJS::ModuleApiInfo> moduleApis; QList<ModuleApiInfo> moduleApis;
QStringList dependencies; QStringList dependencies;
for (auto const &dir : m_qmltypeDirs) { for (auto const &dir : m_qmltypeDirs) {
QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter, QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter,
@ -420,25 +418,27 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
// add builtins // add builtins
for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) { for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) {
auto val = ob_it.value(); auto val = ob_it.value();
for (auto export_ : val->exports()) {
m_exportedName2MetaObject[export_.type] = val; const auto exports = val->exports();
} for (const auto &valExport : exports)
for (auto enumCount = 0; enumCount < val->enumeratorCount(); ++enumCount) { m_exportedName2Scope[valExport.type()] = val;
m_currentScope->insertQMLIdentifier(val->enumerator(enumCount).name());
} const auto enums = val->enums();
for (const auto &valEnum : enums)
m_currentScope->addEnum(valEnum);
} }
// add "self" (as we only ever check the first part of a qualified identifier, we get away with // add "self" (as we only ever check the first part of a qualified identifier, we get away with
// using an empty FakeMetaObject // using an empty ScopeTree
m_exportedName2MetaObject[QFileInfo { m_filePath }.baseName()] = {}; m_exportedName2Scope[QFileInfo { m_filePath }.baseName()] = {};
// add QML builtins // add QML builtins
m_exportedName2MetaObject["QtObject"] = {}; // QtObject contains nothing of interest m_exportedName2Scope["QtObject"] = {}; // QtObject contains nothing of interest
LanguageUtils::FakeMetaObject *meta = new LanguageUtils::FakeMetaObject{}; ScopeTree *scope = new ScopeTree(ScopeType::QMLScope);
meta->addProperty(LanguageUtils::FakeMetaProperty {"enabled", "bool", false, false, false, 0}); scope->addProperty(MetaProperty {"enabled", "bool", false, false, false, 0});
meta->addProperty(LanguageUtils::FakeMetaProperty {"ignoreUnknownSignals", "bool", false, false, false, 0}); scope->addProperty(MetaProperty {"ignoreUnknownSignals", "bool", false, false, false, 0});
meta->addProperty(LanguageUtils::FakeMetaProperty {"target", "QObject", false, false, false, 0}); scope->addProperty(MetaProperty {"target", "QObject", false, false, false, 0});
m_exportedName2MetaObject["Connections"] = LanguageUtils::FakeMetaObject::ConstPtr { meta }; m_exportedName2Scope["Connections"] = ScopeTree::ConstPtr { scope };
importDirectory(".", QString()); importDirectory(".", QString());
return true; return true;
@ -518,7 +518,8 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::CaseBlock *)
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *catchStatement) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *catchStatement)
{ {
enterEnvironment(ScopeType::JSLexicalScope, "catch"); enterEnvironment(ScopeType::JSLexicalScope, "catch");
m_currentScope->insertJSIdentifier(catchStatement->patternElement->bindingIdentifier.toString(), QQmlJS::AST::VariableScope::Let); m_currentScope->insertJSIdentifier(catchStatement->patternElement->bindingIdentifier.toString(),
QQmlJS::AST::VariableScope::Let);
return true; return true;
} }
@ -529,8 +530,13 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Catch *)
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::WithStatement *withStatement) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::WithStatement *withStatement)
{ {
m_colorOut.write(QString::asprintf("Warning: "), Warning); m_colorOut.write(QString::fromLatin1("Warning: "), Warning);
m_colorOut.write(QString::asprintf("%d:%d: with statements are strongly discouraged in QML and might cause false positives when analysing unqalified identifiers\n", withStatement->firstSourceLocation().startLine, withStatement->firstSourceLocation().startColumn), Normal); m_colorOut.write(QString::fromLatin1(
"%1:%2: with statements are strongly discouraged in QML "
"and might cause false positives when analysing unqalified identifiers\n")
.arg(withStatement->firstSourceLocation().startLine)
.arg(withStatement->firstSourceLocation().startColumn),
Normal);
enterEnvironment(ScopeType::JSLexicalScope, "with"); enterEnvironment(ScopeType::JSLexicalScope, "with");
return true; return true;
} }
@ -563,13 +569,12 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
auto name = uisb->qualifiedId->name; auto name = uisb->qualifiedId->name;
if (name == QLatin1String("id")) { if (name == QLatin1String("id")) {
// found id // found id
auto expstat = static_cast<ExpressionStatement *>(uisb->statement); auto expstat = cast<ExpressionStatement *>(uisb->statement);
auto identexp = static_cast<IdentifierExpression *>(expstat->expression); auto identexp = cast<IdentifierExpression *>(expstat->expression);
QString elementName = m_currentScope->name(); QString elementName = m_currentScope->name();
m_qmlid2meta.insert(identexp->name.toString(), m_exportedName2MetaObject[elementName]); m_qmlid2scope.insert(identexp->name.toString(), m_exportedName2Scope[elementName]);
if (m_currentScope->isVisualRootScope()) { if (m_currentScope->isVisualRootScope())
m_rootId = identexp->name.toString(); m_rootId = identexp->name.toString();
}
} else { } else {
const QString signal = signalName(name); const QString signal = signalName(name);
if (signal.isEmpty()) if (signal.isEmpty())
@ -582,7 +587,7 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
const auto statement = uisb->statement; const auto statement = uisb->statement;
if (statement->kind == Node::Kind::Kind_ExpressionStatement) { if (statement->kind == Node::Kind::Kind_ExpressionStatement) {
if (static_cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) { if (cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) {
// functions are already handled // functions are already handled
// they do not get names inserted according to the signal, but access their formal // they do not get names inserted according to the signal, but access their formal
// parameters // parameters
@ -607,37 +612,42 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiPublicMember *uipm)
{ {
// property bool inactive: !active // property bool inactive: !active
// extract name inactive // extract name inactive
m_currentScope->insertPropertyIdentifier(uipm->name.toString()); m_currentScope->insertPropertyIdentifier(MetaProperty(
uipm->name.toString(),
// TODO: signals, complex types etc.
uipm->memberType ? uipm->memberType->name.toString() : QString(),
uipm->typeModifier == QLatin1String("list"),
!uipm->isReadonlyMember,
false, 0));
return true; return true;
} }
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
{ {
auto name = idexp->name; auto name = idexp->name;
if (!m_exportedName2MetaObject.contains(name.toString())) { if (!m_exportedName2Scope.contains(name.toString())) {
m_currentScope->addIdToAccssedIfNotInParentScopes( m_currentScope->addIdToAccssedIfNotInParentScopes(
{ name.toString(), idexp->firstSourceLocation() }, m_unknownImports); { name.toString(), idexp->firstSourceLocation() }, m_unknownImports);
} }
return true; return true;
} }
FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QString code,
const QString &code, const QString &fileName, QString fileName, bool silent)
bool silent)
: m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }), : m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }),
m_currentScope(m_rootScope.get()), m_currentScope(m_rootScope.get()),
m_qmltypeDirs(qmltypeDirs), m_qmltypeDirs(std::move(qmltypeDirs)),
m_code(code), m_code(std::move(code)),
m_rootId(QLatin1String("<id>")), m_rootId(QLatin1String("<id>")),
m_filePath(fileName), m_filePath(std::move(fileName)),
m_colorOut(silent) m_colorOut(silent)
{ {
// setup color output // setup color output
m_colorOut.insertColorMapping(Error, ColorOutput::RedForeground); m_colorOut.insertMapping(Error, ColorOutput::RedForeground);
m_colorOut.insertColorMapping(Warning, ColorOutput::PurpleForeground); m_colorOut.insertMapping(Warning, ColorOutput::PurpleForeground);
m_colorOut.insertColorMapping(Info, ColorOutput::BlueForeground); m_colorOut.insertMapping(Info, ColorOutput::BlueForeground);
m_colorOut.insertColorMapping(Normal, ColorOutput::DefaultColor); m_colorOut.insertMapping(Normal, ColorOutput::DefaultColor);
m_colorOut.insertColorMapping(Hint, ColorOutput::GreenForeground); m_colorOut.insertMapping(Hint, ColorOutput::GreenForeground);
QLatin1String jsGlobVars[] = { QLatin1String jsGlobVars[] = {
/* Not listed on the MDN page; browser and QML extensions: */ /* Not listed on the MDN page; browser and QML extensions: */
// console/debug api // console/debug api
@ -645,32 +655,36 @@ FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDir
// garbage collector // garbage collector
QLatin1String("gc"), QLatin1String("gc"),
// i18n // i18n
QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"), QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"), QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"),
QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"),
// XMLHttpRequest // XMLHttpRequest
QLatin1String("XMLHttpRequest") QLatin1String("XMLHttpRequest")
}; };
for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr; ++globalName) { for (const char **globalName = QV4::Compiler::Codegen::s_globalNames;
m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), QQmlJS::AST::VariableScope::Const); *globalName != nullptr;
++globalName) {
m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName),
QQmlJS::AST::VariableScope::Const);
} }
for (const auto& jsGlobVar: jsGlobVars) for (const auto& jsGlobVar: jsGlobVars)
m_currentScope->insertJSIdentifier(jsGlobVar, QQmlJS::AST::VariableScope::Const); m_currentScope->insertJSIdentifier(jsGlobVar, QQmlJS::AST::VariableScope::Const);
} }
FindUnqualifiedIDVisitor::~FindUnqualifiedIDVisitor() = default;
bool FindUnqualifiedIDVisitor::check() bool FindUnqualifiedIDVisitor::check()
{ {
if (m_visitFailed) if (m_visitFailed)
return false; return false;
// now that all ids are known, revisit any Connections whose target were perviously unknown // now that all ids are known, revisit any Connections whose target were perviously unknown
for (auto const& outstandingConnection: m_outstandingConnections) { for (auto const &outstandingConnection: m_outstandingConnections) {
auto metaObject = m_qmlid2meta[outstandingConnection.targetName]; auto targetScope = m_qmlid2scope[outstandingConnection.targetName];
outstandingConnection.scope->addMethodsFromMetaObject(metaObject); if (outstandingConnection.scope)
outstandingConnection.scope->addMethods(targetScope->methods());
QScopedValueRollback<ScopeTree*> rollback(m_currentScope, outstandingConnection.scope); QScopedValueRollback<ScopeTree*> rollback(m_currentScope, outstandingConnection.scope);
outstandingConnection.uiod->initializer->accept(this); outstandingConnection.uiod->initializer->accept(this);
} }
return m_rootScope->recheckIdentifiers(m_code, m_qmlid2meta, m_rootScope.get(), m_rootId, m_colorOut); return m_rootScope->recheckIdentifiers(m_code, m_qmlid2scope, m_rootScope.get(), m_rootId,
m_colorOut);
} }
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
@ -686,18 +700,16 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
void FindUnqualifiedIDVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr) void FindUnqualifiedIDVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
{ {
using namespace QQmlJS::AST; using namespace QQmlJS::AST;
if (!fexpr->name.isEmpty()) { auto name = fexpr->name.toString();
auto name = fexpr->name.toString(); if (!name.isEmpty()) {
if (m_currentScope->scopeType() == ScopeType::QMLScope) { if (m_currentScope->scopeType() == ScopeType::QMLScope)
m_currentScope->insertQMLIdentifier(name); m_currentScope->addMethod(MetaMethod(name, QLatin1String("void")));
} else { else
m_currentScope->insertJSIdentifier(name, VariableScope::Const); m_currentScope->insertJSIdentifier(name, VariableScope::Const);
} enterEnvironment(ScopeType::JSFunctionScope, name);
} else {
enterEnvironment(ScopeType::JSFunctionScope, QLatin1String("<anon>"));
} }
QString name = fexpr->name.toString();
if (name.isEmpty())
name = "<anon>";
enterEnvironment(ScopeType::JSFunctionScope, name);
} }
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
@ -743,7 +755,8 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
QString path {}; QString path {};
if (!import->importId.isEmpty()) { if (!import->importId.isEmpty()) {
m_qmlid2meta.insert(import->importId.toString(), {}); // TODO: do not put imported ids into the same space as qml IDs // TODO: do not put imported ids into the same space as qml IDs
m_qmlid2scope.insert(import->importId.toString(), {});
} }
if (import->version) { if (import->version) {
auto uri = import->importUri; auto uri = import->importUri;
@ -761,14 +774,17 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
{ {
m_currentScope->insertQMLIdentifier(uied->name.toString()); MetaEnum qmlEnum(uied->name.toString());
for (const auto *member = uied->members; member; member = member->next)
qmlEnum.addKey(member->member.toString());
m_currentScope->addEnum(qmlEnum);
return true; return true;
} }
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
{ {
// property QtObject __styleData: QtObject {...} // property QtObject __styleData: QtObject {...}
m_currentScope->insertPropertyIdentifier(uiob->qualifiedId->name.toString());
QString name {}; QString name {};
auto id = uiob->qualifiedTypeNameId; auto id = uiob->qualifiedTypeNameId;
QStringRef prefix = uiob->qualifiedTypeNameId->name; QStringRef prefix = uiob->qualifiedTypeNameId->name;
@ -777,8 +793,13 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
id = id->next; id = id->next;
} }
name.chop(1); name.chop(1);
const MetaProperty prop(uiob->qualifiedId->name.toString(), name, false, true, true, 0);
m_currentScope->addProperty(prop);
enterEnvironment(ScopeType::QMLScope, name); enterEnvironment(ScopeType::QMLScope, name);
if (name == QLatin1String("Component") || name == QLatin1String("QtObject")) // there is no typeinfo for Component and QtObject, but they also have no interesting properties // there is no typeinfo for Component and QtObject, but they also have no interesting properties
if (name == QLatin1String("Component") || name == QLatin1String("QtObject"))
return true; return true;
importExportedNames(prefix, name); importExportedNames(prefix, name);
return true; return true;
@ -791,6 +812,8 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectBinding *)
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
{ {
using namespace QQmlJS::AST;
QString name {}; QString name {};
auto id = uiod->qualifiedTypeNameId; auto id = uiod->qualifiedTypeNameId;
QStringRef prefix = uiod->qualifiedTypeNameId->name; QStringRef prefix = uiod->qualifiedTypeNameId->name;
@ -802,8 +825,11 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
enterEnvironment(ScopeType::QMLScope, name); enterEnvironment(ScopeType::QMLScope, name);
if (name.isLower()) if (name.isLower())
return false; // Ignore grouped properties for now return false; // Ignore grouped properties for now
if (name == QLatin1String("Component") || name == QLatin1String("QtObject")) // there is no typeinfo for Component
// there is no typeinfo for Component
if (name == QLatin1String("Component") || name == QLatin1String("QtObject"))
return true; return true;
importExportedNames(prefix, name); importExportedNames(prefix, name);
if (name.endsWith("Connections")) { if (name.endsWith("Connections")) {
QString target; QString target;
@ -825,25 +851,26 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
} }
member = member->next; member = member->next;
} }
LanguageUtils::FakeMetaObject::ConstPtr metaObject {}; ScopeTree::ConstPtr targetScope;
if (target.isEmpty()) { if (target.isEmpty()) {
// no target set, connection comes from parentF // no target set, connection comes from parentF
ScopeTree* scope = m_currentScope; ScopeTree* scope = m_currentScope;
do { do {
scope = scope->parentScope(); // TODO: rename method scope = scope->parentScope(); // TODO: rename method
} while (scope->scopeType() != ScopeType::QMLScope); } while (scope->scopeType() != ScopeType::QMLScope);
auto metaObject = m_exportedName2MetaObject[scope->name()]; targetScope = m_exportedName2Scope[scope->name()];
} else { } else {
// there was a target, check if we already can find it // there was a target, check if we already can find it
auto metaObjectIt = m_qmlid2meta.find(target); auto scopeIt = m_qmlid2scope.find(target);
if (metaObjectIt != m_qmlid2meta.end()) { if (scopeIt != m_qmlid2scope.end()) {
metaObject = *metaObjectIt; targetScope = *scopeIt;
} else { } else {
m_outstandingConnections.push_back({target, m_currentScope, uiod}); m_outstandingConnections.push_back({target, m_currentScope, uiod});
return false; // visit children later once target is known return false; // visit children later once target is known
} }
} }
m_currentScope->addMethodsFromMetaObject(metaObject); if (targetScope)
m_currentScope->addMethods(targetScope->methods());
} }
return true; return true;
} }

View File

@ -29,32 +29,41 @@
#ifndef FINDUNQUALIFIED_H #ifndef FINDUNQUALIFIED_H
#define FINDUNQUALIFIED_H #define FINDUNQUALIFIED_H
#include "qmljstypedescriptionreader.h" //
#include "qcoloroutput_p.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 <private/qqmljsastvisitor_p.h> #include "typedescriptionreader.h"
#include <private/qqmljsast_p.h> #include "scopetree.h"
#include "qcoloroutput.h"
#include <QScopedPointer> #include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
class ScopeTree; #include <QtCore/qscopedpointer.h>
enum class ScopeType;
class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor {
class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor
{
Q_DISABLE_COPY_MOVE(FindUnqualifiedIDVisitor)
public: public:
explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, explicit FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QString code,
const QString& fileName, bool silent); QString fileName, bool silent);
~FindUnqualifiedIDVisitor() override; ~FindUnqualifiedIDVisitor() override = default;
bool check(); bool check();
private: private:
QScopedPointer<ScopeTree> m_rootScope; QScopedPointer<ScopeTree> m_rootScope;
ScopeTree *m_currentScope; ScopeTree *m_currentScope = nullptr;
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_exportedName2MetaObject; QHash<QString, ScopeTree::ConstPtr> m_exportedName2Scope;
QStringList m_qmltypeDirs; QStringList m_qmltypeDirs;
const QString& m_code; QString m_code;
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_qmlid2meta; QHash<QString, ScopeTree::ConstPtr> m_qmlid2scope;
QString m_rootId; QString m_rootId;
QString m_filePath; QString m_filePath;
QSet<QPair<QString, QString>> m_alreadySeenImports; QSet<QPair<QString, QString>> m_alreadySeenImports;
@ -62,17 +71,22 @@ private:
ColorOutput m_colorOut; ColorOutput m_colorOut;
bool m_visitFailed = false; bool m_visitFailed = false;
struct OutstandingConnection {QString targetName; ScopeTree *scope; QQmlJS::AST::UiObjectDefinition *uiod;}; struct OutstandingConnection
{
QString targetName;
ScopeTree *scope;
QQmlJS::AST::UiObjectDefinition *uiod;
};
QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
void enterEnvironment(ScopeType type, QString name); void enterEnvironment(ScopeType type, QString name);
void leaveEnvironment(); void leaveEnvironment();
void importHelper(QString id, QString prefix, int major, int minor); void importHelper(QString id, const QString &prefix, int major, int minor);
LanguageUtils::FakeMetaObject* localQmlFile2FakeMetaObject(QString filePath); ScopeTree* localQmlFile2ScopeTree(const QString &filePath);
void importDirectory(const QString &directory, const QString &prefix); void importDirectory(const QString &directory, const QString &prefix);
void importExportedNames(QStringRef prefix, QString name); void importExportedNames(const QStringRef &prefix, QString name);
void throwRecursionDepthError() override; void throwRecursionDepthError() override;
@ -130,5 +144,4 @@ private:
bool visit(QQmlJS::AST::PatternElement *) override; bool visit(QQmlJS::AST::PatternElement *) override;
}; };
#endif // FINDUNQUALIFIED_H #endif // FINDUNQUALIFIED_H

View File

@ -26,27 +26,29 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#if QT_CONFIG(commandlineparser)
#include <QCommandLineParser>
#endif
#include <QCoreApplication>
#ifndef QT_BOOTSTRAPPED
#include <QLibraryInfo>
#endif
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsengine_p.h>
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
#include "findunqualified.h" #include "findunqualified.h"
static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied, QStringList const &qmltypeDirs) #include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
#include <QtQml/private/qqmljsengine_p.h>
#include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qcoreapplication.h>
#if QT_CONFIG(commandlineparser)
#include <QtCore/qcommandlineparser.h>
#endif
#ifndef QT_BOOTSTRAPPED
#include <QtCore/qlibraryinfo.h>
#endif
static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied,
const QStringList &qmltypeDirs)
{ {
QFile file(filename); QFile file(filename);
if (!file.open(QFile::ReadOnly)) { if (!file.open(QFile::ReadOnly)) {
@ -63,17 +65,20 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
QFileInfo info(filename); QFileInfo info(filename);
const QString lowerSuffix = info.suffix().toLower(); const QString lowerSuffix = info.suffix().toLower();
const bool isJavaScript = (lowerSuffix == QLatin1String("js") || lowerSuffix == QLatin1String("mjs"));
const bool isESModule = lowerSuffix == QLatin1String("mjs"); const bool isESModule = lowerSuffix == QLatin1String("mjs");
lexer.setCode(code, /*line = */ 1, /*qmlMode=*/ !isJavaScript); const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/ !isJavaScript);
QQmlJS::Parser parser(&engine); QQmlJS::Parser parser(&engine);
bool success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram()) : parser.parse(); bool success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram())
: parser.parse();
if (!success && !silent) { if (!success && !silent) {
const auto diagnosticMessages = parser.diagnosticMessages(); const auto diagnosticMessages = parser.diagnosticMessages();
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
qWarning("%s:%d : %s", qPrintable(filename), m.line, qPrintable(m.message)); qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
.arg(filename).arg(m.line).arg(m.message);
} }
} }
@ -98,10 +103,12 @@ int main(int argv, char *argc[])
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
QCommandLineOption silentOption(QStringList() << "s" << "silent", QLatin1String("Don't output syntax errors")); QCommandLineOption silentOption(QStringList() << "s" << "silent",
QLatin1String("Don't output syntax errors"));
parser.addOption(silentOption); parser.addOption(silentOption);
QCommandLineOption checkUnqualified(QStringList() << "U" << "check-unqualified", QLatin1String("Warn about unqualified identifiers")); QCommandLineOption checkUnqualified(QStringList() << "U" << "check-unqualified",
QLatin1String("Warn about unqualified identifiers"));
parser.addOption(checkUnqualified); parser.addOption(checkUnqualified);
QCommandLineOption qmltypesDirsOption( QCommandLineOption qmltypesDirsOption(
@ -111,7 +118,8 @@ int main(int argv, char *argc[])
QLatin1String("directory")); QLatin1String("directory"));
parser.addOption(qmltypesDirsOption); parser.addOption(qmltypesDirsOption);
parser.addPositionalArgument(QLatin1String("files"), QLatin1String("list of qml or js files to verify")); parser.addPositionalArgument(QLatin1String("files"),
QLatin1String("list of qml or js files to verify"));
parser.process(app); parser.process(app);
@ -123,12 +131,14 @@ int main(int argv, char *argc[])
bool silent = parser.isSet(silentOption); bool silent = parser.isSet(silentOption);
bool warnUnqualified = parser.isSet(checkUnqualified); bool warnUnqualified = parser.isSet(checkUnqualified);
// use host qml import path as a sane default if nothing else has been provided // use host qml import path as a sane default if nothing else has been provided
QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption) ? parser.values(qmltypesDirsOption) QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption)
#ifndef QT_BOOTSTRAPPED ? parser.values(qmltypesDirsOption)
: QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath), QLatin1String(".")}; # ifndef QT_BOOTSTRAPPED
#else : QStringList { QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath),
: QStringList{QLatin1String(".")}; QLatin1String(".") };
#endif # else
: QStringList { QLatin1String(".") };
# endif
#else #else
bool silent = false; bool silent = false;
bool warnUnqualified = false; bool warnUnqualified = false;

146
tools/qmllint/metatypes.h Normal file
View File

@ -0,0 +1,146 @@
/****************************************************************************
**
** 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 MetaProperty
{
QString m_propertyName;
QString m_type;
bool m_isList;
bool m_isWritable;
bool m_isPointer;
int m_revision;
public:
MetaProperty(QString name, QString type,
bool isList, bool isWritable, bool isPointer, int revision)
: m_propertyName(std::move(name))
, m_type(std::move(type))
, m_isList(isList)
, m_isWritable(isWritable)
, m_isPointer(isPointer)
, m_revision(revision)
{}
QString name() const { return m_propertyName; }
QString typeName() const { return m_type; }
bool isList() const { return m_isList; }
bool isWritable() const { return m_isWritable; }
bool isPointer() const { return m_isPointer; }
int revision() const { return m_revision; }
};
#endif // METATYPES_H

View File

@ -26,72 +26,76 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QFile> #include "qcoloroutput.h"
#include <QHash>
#include <QTextCodec> #include <QtCore/qfile.h>
#include <QtCore/qhash.h>
#include <QtCore/qtextcodec.h>
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "qcoloroutput_p.h"
class ColorOutputPrivate class ColorOutputPrivate
{ {
public: public:
ColorOutputPrivate(bool silent) : currentColorID(-1), silent(silent) ColorOutputPrivate(bool silent) : m_currentColorID(-1), m_silent(silent)
{ {
/* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance /* - QIODevice::Unbuffered because we want it to appear when the user actually calls,
* is considered of lower priority. * performance is considered of lower priority.
*/ */
m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered); m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
m_coloringEnabled = isColoringPossible();
coloringEnabled = isColoringPossible();
} }
ColorOutput::ColorMapping colorMapping;
int currentColorID;
bool coloringEnabled;
bool silent;
static const char *const foregrounds[]; static const char *const foregrounds[];
static const char *const backgrounds[]; static const char *const backgrounds[];
inline void write(const QString &msg) inline void write(const QString &msg) { m_out.write(msg.toLocal8Bit()); }
{
m_out.write(msg.toLocal8Bit());
}
static QString escapeCode(const QString &in) static QString escapeCode(const QString &in)
{ {
const ushort escapeChar = 0x1B;
QString result; QString result;
result.append(QChar(0x1B)); result.append(QChar(escapeChar));
result.append(QLatin1Char('[')); result.append(QLatin1Char('['));
result.append(in); result.append(in);
result.append(QLatin1Char('m')); result.append(QLatin1Char('m'));
return result; return result;
} }
void insertColor(int id, ColorOutput::ColorCode code) { m_colorMapping.insert(id, code); }
ColorOutput::ColorCode color(int id) const { return m_colorMapping.value(id); }
bool containsColor(int id) const { return m_colorMapping.contains(id); }
bool isSilent() const { return m_silent; }
void setCurrentColorID(int colorId) { m_currentColorID = colorId; }
bool coloringEnabled() const { return m_coloringEnabled; }
private: private:
QFile m_out; QFile m_out;
ColorOutput::ColorMapping m_colorMapping;
int m_currentColorID;
bool m_coloringEnabled;
bool m_silent;
/*! /*!
Returns true if it's suitable to send colored output to \c stderr. Returns true if it's suitable to send colored output to \c stderr.
*/ */
inline bool isColoringPossible() const inline bool isColoringPossible() const
{ {
# if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
/* Windows doesn't at all support ANSI escape codes, unless /* Windows doesn't at all support ANSI escape codes, unless
* the user install a "device driver". See the Wikipedia links in the * the user install a "device driver". See the Wikipedia links in the
* class documentation for details. */ * class documentation for details. */
return false; return false;
# else #else
/* We use QFile::handle() to get the file descriptor. It's a bit unsure /* We use QFile::handle() to get the file descriptor. It's a bit unsure
* whether it's 2 on all platforms and in all cases, so hopefully this layer * whether it's 2 on all platforms and in all cases, so hopefully this layer
* of abstraction helps handle such cases. */ * of abstraction helps handle such cases. */
return isatty(m_out.handle()); return isatty(m_out.handle());
# endif #endif
} }
}; };
@ -128,7 +132,6 @@ const char *const ColorOutputPrivate::backgrounds[] =
/*! /*!
\class ColorOutput \class ColorOutput
\since 4.4
\nonreentrant \nonreentrant
\brief Outputs colored messages to \c stderr. \brief Outputs colored messages to \c stderr.
\internal \internal
@ -214,41 +217,18 @@ const char *const ColorOutputPrivate::backgrounds[] =
on the settings of the user's terminal. on the settings of the user's terminal.
*/ */
/*!
Sets the color mapping to be \a cMapping.
Negative values are disallowed.
\sa colorMapping(), insertMapping()
*/
void ColorOutput::setColorMapping(const ColorMapping &cMapping)
{
d->colorMapping = cMapping;
}
/*!
Returns the color mappings in use.
\sa setColorMapping(), insertMapping()
*/
ColorOutput::ColorMapping ColorOutput::colorMapping() const
{
return d->colorMapping;
}
/*! /*!
Constructs a ColorOutput instance, ready for use. Constructs a ColorOutput instance, ready for use.
*/ */
ColorOutput::ColorOutput(bool silent) : d(new ColorOutputPrivate(silent)) ColorOutput::ColorOutput(bool silent) : d(new ColorOutputPrivate(silent)) {}
{
}
ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer has access to the complete type // must be here so that QScopedPointer has access to the complete type
ColorOutput::~ColorOutput() = default;
/*! /*!
Sends \a message to \c stderr, using the color looked up in colorMapping() using \a colorID. Sends \a message to \c stderr, using the color looked up in the color mapping using \a colorID.
If \a color isn't available in colorMapping(), result and behavior is undefined. If \a color isn't available in the color mapping, result and behavior is undefined.
If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput
is initialized to not color at all. is initialized to not color at all.
@ -259,7 +239,7 @@ ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer ha
*/ */
void ColorOutput::write(const QString &message, int colorID) void ColorOutput::write(const QString &message, int colorID)
{ {
if (!d->silent) if (!d->isSilent())
d->write(colorify(message, colorID)); d->write(colorify(message, colorID));
} }
@ -271,7 +251,7 @@ void ColorOutput::write(const QString &message, int colorID)
*/ */
void ColorOutput::writeUncolored(const QString &message) void ColorOutput::writeUncolored(const QString &message)
{ {
if (!d->silent) if (!d->isSilent())
d->write(message + QLatin1Char('\n')); d->write(message + QLatin1Char('\n'));
} }
@ -285,61 +265,56 @@ void ColorOutput::writeUncolored(const QString &message)
*/ */
QString ColorOutput::colorify(const QString &message, int colorID) const QString ColorOutput::colorify(const QString &message, int colorID) const
{ {
Q_ASSERT_X(colorID == -1 || d->colorMapping.contains(colorID), Q_FUNC_INFO, Q_ASSERT_X(colorID == -1 || d->containsColor(colorID), Q_FUNC_INFO,
qPrintable(QString::fromLatin1("There is no color registered by id %1").arg(colorID))); qPrintable(QString::fromLatin1("There is no color registered by id %1")
Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO, "It makes no sense to attempt to print an empty string."); .arg(colorID)));
Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO,
"It makes no sense to attempt to print an empty string.");
if (colorID != -1) if (colorID != -1)
d->currentColorID = colorID; d->setCurrentColorID(colorID);
if (d->coloringEnabled && colorID != -1) if (d->coloringEnabled() && colorID != -1) {
{ const int color = d->color(colorID);
const int color(d->colorMapping.value(colorID));
/* If DefaultColor is set, we don't want to color it. */ /* If DefaultColor is set, we don't want to color it. */
if (color & DefaultColor) if (color & DefaultColor)
return message; return message;
const int foregroundCode = (int(color) & ForegroundMask) >> ForegroundShift; const int foregroundCode = (color & ForegroundMask) >> ForegroundShift;
const int backgroundCode = (int(color) & BackgroundMask) >> BackgroundShift; const int backgroundCode = (color & BackgroundMask) >> BackgroundShift;
QString finalMessage; QString finalMessage;
bool closureNeeded = false; bool closureNeeded = false;
if (foregroundCode) if (foregroundCode > 0) {
{ finalMessage.append(
finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1]))); ColorOutputPrivate::escapeCode(
QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
closureNeeded = true; closureNeeded = true;
} }
if (backgroundCode) if (backgroundCode > 0) {
{ finalMessage.append(
finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1]))); ColorOutputPrivate::escapeCode(
QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
closureNeeded = true; closureNeeded = true;
} }
finalMessage.append(message); finalMessage.append(message);
if (closureNeeded) if (closureNeeded)
{ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String("0")));
finalMessage.append(QChar(0x1B));
finalMessage.append(QLatin1String("[0m"));
}
return finalMessage; return finalMessage;
} }
else
return message; return message;
} }
/*! /*!
Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance. Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance.
This is a convenience function for creating a ColorOutput::ColorMapping instance and
calling setColorMapping().
\sa colorMapping(), setColorMapping()
*/ */
void ColorOutput::insertColorMapping(int colorID, const ColorCode colorCode) void ColorOutput::insertMapping(int colorID, const ColorCode colorCode)
{ {
d->colorMapping.insert(colorID, colorCode); d->insertColor(colorID, colorCode);
} }

View File

@ -26,6 +26,9 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef QCOLOROUTPUT_H
#define QCOLOROUTPUT_H
// //
// W A R N I N G // W A R N I N G
// ------------- // -------------
@ -36,12 +39,8 @@
// //
// We mean it. // We mean it.
#ifndef QCOLOROUTPUT_P_H #include <QtCore/qglobal.h>
#define QCOLOROUTPUT_P_H #include <QtCore/qscopedpointer.h>
#include <QtCore/QtGlobal>
#include <QtCore/QHash>
#include <QScopedPointer>
class ColorOutputPrivate; class ColorOutputPrivate;
@ -86,15 +85,13 @@ public:
DefaultColor = 1 << SpecialShift DefaultColor = 1 << SpecialShift
}; };
typedef QFlags<ColorCodeComponent> ColorCode; using ColorCode = QFlags<ColorCodeComponent>;
typedef QHash<int, ColorCode> ColorMapping; using ColorMapping = QHash<int, ColorCode>;
ColorOutput(bool silent); ColorOutput(bool silent);
~ColorOutput(); ~ColorOutput();
void setColorMapping(const ColorMapping &cMapping); void insertMapping(int colorID, ColorCode colorCode);
ColorMapping colorMapping() const;
void insertColorMapping(int colorID, const ColorCode colorCode);
void writeUncolored(const QString &message); void writeUncolored(const QString &message);
void write(const QString &message, int color = -1); void write(const QString &message, int color = -1);
@ -102,9 +99,9 @@ public:
private: private:
QScopedPointer<ColorOutputPrivate> d; QScopedPointer<ColorOutputPrivate> d;
Q_DISABLE_COPY(ColorOutput) Q_DISABLE_COPY_MOVE(ColorOutput)
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(ColorOutput::ColorCode) Q_DECLARE_OPERATORS_FOR_FLAGS(ColorOutput::ColorCode)
#endif #endif // QCOLOROUTPUT_H

View File

@ -1,103 +0,0 @@
/****************************************************************************
**
** 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 QMLJSTYPEDESCRIPTIONREADER_H
#define QMLJSTYPEDESCRIPTIONREADER_H
#include <private/qqmljsastfwd_p.h>
#include "fakemetaobject.h"
// for Q_DECLARE_TR_FUNCTIONS
#include <QCoreApplication>
QT_BEGIN_NAMESPACE
class QIODevice;
class QBuffer;
namespace QQmlJS {
class ModuleApiInfo
{
public:
QString uri;
LanguageUtils::ComponentVersion version;
QString cppName;
};
class TypeDescriptionReader
{
Q_DECLARE_TR_FUNCTIONS(QQmlJS::TypeDescriptionReader)
public:
explicit TypeDescriptionReader(const QString &fileName, const QString &data);
~TypeDescriptionReader();
bool operator()(
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *objects,
QList<ModuleApiInfo> *moduleApis,
QStringList *dependencies);
QString errorMessage() const;
QString warningMessage() const;
private:
void readDocument(AST::UiProgram *ast);
void readModule(AST::UiObjectDefinition *ast);
void readDependencies(AST::UiScriptBinding *ast);
void readComponent(AST::UiObjectDefinition *ast);
void readModuleApi(AST::UiObjectDefinition *ast);
void readSignalOrMethod(AST::UiObjectDefinition *ast, bool isMethod, LanguageUtils::FakeMetaObject::Ptr fmo);
void readProperty(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
void readEnum(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
void readParameter(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaMethod *fmm);
QString readStringBinding(AST::UiScriptBinding *ast);
bool readBoolBinding(AST::UiScriptBinding *ast);
double readNumericBinding(AST::UiScriptBinding *ast);
LanguageUtils::ComponentVersion readNumericVersionBinding(AST::UiScriptBinding *ast);
int readIntBinding(AST::UiScriptBinding *ast);
void readExports(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
void readMetaObjectRevisions(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
void readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme);
void addError(const AST::SourceLocation &loc, const QString &message);
void addWarning(const AST::SourceLocation &loc, const QString &message);
QString _fileName;
QString _source;
QString _errorMessage;
QString _warningMessage;
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *_objects;
QList<ModuleApiInfo> *_moduleApis = nullptr;
QStringList *_dependencies = nullptr;
};
} // namespace QQmlJS
QT_END_NAMESPACE
#endif // QMLJSTYPEDESCRIPTIONREADER_H

View File

@ -4,11 +4,10 @@ QT = core qmldevtools-private
SOURCES += main.cpp \ SOURCES += main.cpp \
componentversion.cpp \ componentversion.cpp \
fakemetaobject.cpp \
findunqualified.cpp \ findunqualified.cpp \
qmljstypedescriptionreader.cpp \
qcoloroutput.cpp \ qcoloroutput.cpp \
scopetree.cpp scopetree.cpp \
typedescriptionreader.cpp
QMAKE_TARGET_DESCRIPTION = QML Syntax Verifier QMAKE_TARGET_DESCRIPTION = QML Syntax Verifier
@ -16,8 +15,8 @@ load(qt_tool)
HEADERS += \ HEADERS += \
componentversion.h \ componentversion.h \
fakemetaobject.h \
findunqualified.h \ findunqualified.h \
qmljstypedescriptionreader.h \ metatypes.h \
qcoloroutput_p.h \ qcoloroutput.h \
scopetree.h scopetree.h \
typedescriptionreader.h

View File

@ -27,28 +27,27 @@
****************************************************************************/ ****************************************************************************/
#include "scopetree.h" #include "scopetree.h"
#include "qcoloroutput.h"
#include "qcoloroutput_p.h" #include <QtCore/qqueue.h>
#include <algorithm> #include <algorithm>
#include <QQueue>
ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope) ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope)
: m_parentScope(parentScope), m_name(name), m_scopeType(type) {} : m_parentScope(parentScope), m_name(std::move(name)), m_scopeType(type) {}
ScopeTree *ScopeTree::createNewChildScope(ScopeType type, QString name) { ScopeTree *ScopeTree::createNewChildScope(ScopeType type, QString name)
Q_ASSERT(type != ScopeType::QMLScope|| !m_parentScope || m_parentScope->m_scopeType == ScopeType::QMLScope || m_parentScope->m_name == "global"); {
auto childScope = new ScopeTree{type, name, this}; Q_ASSERT(type != ScopeType::QMLScope
|| !m_parentScope
|| m_parentScope->m_scopeType == ScopeType::QMLScope
|| m_parentScope->m_name == "global");
auto childScope = new ScopeTree{type, std::move(name), this};
m_childScopes.push_back(childScope); m_childScopes.push_back(childScope);
return childScope; return childScope;
} }
ScopeTree *ScopeTree::parentScope() { void ScopeTree::insertJSIdentifier(const QString &id, QQmlJS::AST::VariableScope scope)
return m_parentScope;
}
void ScopeTree::insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope)
{ {
Q_ASSERT(m_scopeType != ScopeType::QMLScope); Q_ASSERT(m_scopeType != ScopeType::QMLScope);
if (scope == QQmlJS::AST::VariableScope::Var) { if (scope == QQmlJS::AST::VariableScope::Var) {
@ -56,29 +55,25 @@ void ScopeTree::insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope)
while (targetScope->scopeType() != ScopeType::JSFunctionScope) { while (targetScope->scopeType() != ScopeType::JSFunctionScope) {
targetScope = targetScope->m_parentScope; targetScope = targetScope->m_parentScope;
} }
targetScope->m_currentScopeJSIdentifiers.insert(id); targetScope->m_jsIdentifiers.insert(id);
} else { } else {
m_currentScopeJSIdentifiers.insert(id); m_jsIdentifiers.insert(id);
} }
} }
void ScopeTree::insertQMLIdentifier(QString id) void ScopeTree::insertSignalIdentifier(const QString &id, const MetaMethod &method,
{ const QQmlJS::AST::SourceLocation &loc,
Q_ASSERT(m_scopeType == ScopeType::QMLScope); bool hasMultilineHandlerBody)
m_currentScopeQMLIdentifiers.insert(id);
}
void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody)
{ {
Q_ASSERT(m_scopeType == ScopeType::QMLScope); Q_ASSERT(m_scopeType == ScopeType::QMLScope);
m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody}); m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
} }
void ScopeTree::insertPropertyIdentifier(QString id) void ScopeTree::insertPropertyIdentifier(const MetaProperty &property)
{ {
this->insertQMLIdentifier(id); addProperty(property);
LanguageUtils::FakeMetaMethod method( id + QLatin1String("Changed"), "void"); MetaMethod method(property.name() + QLatin1String("Changed"), "void");
this->addMethod(method); addMethod(method);
} }
void ScopeTree::addUnmatchedSignalHandler(const QString &handler, void ScopeTree::addUnmatchedSignalHandler(const QString &handler,
@ -92,52 +87,62 @@ bool ScopeTree::isIdInCurrentScope(const QString &id) const
return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id); return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
} }
void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair, const QSet<QString>& unknownImports) { void ScopeTree::addIdToAccssedIfNotInParentScopes(
const QPair<QString, QQmlJS::AST::SourceLocation> &idLocationPair,
const QSet<QString> &unknownImports)
{
// also do not add id if it is parent // also do not add id if it is parent
// parent is almost always defined valid in QML, and if we could not find a definition for the current QML component // parent is almost always defined valid in QML, and if we could not find a definition for the current QML component
// not skipping "parent" will lead to many false positives // not skipping "parent" will lead to many false positives
// Moreover, if the top level item is Item or inherits from it, it will have a parent property to which we would point the user // Moreover, if the top level item is Item or inherits from it, it will have a parent property to which we would point the user
// which makes for a very nonsensical warning // which makes for a very nonsensical warning
auto qmlScope = getCurrentQMLScope(); const auto *qmlScope = currentQMLScope();
if (!isIdInCurrentScope(id_loc_pair.first) && !(id_loc_pair.first == QLatin1String("parent") && qmlScope && unknownImports.contains(qmlScope->name()))) { if (!isIdInCurrentScope(idLocationPair.first)
m_accessedIdentifiers.push_back(id_loc_pair); && !(idLocationPair.first == QLatin1String("parent")
&& qmlScope && unknownImports.contains(qmlScope->name()))) {
m_accessedIdentifiers.push_back(idLocationPair);
} }
} }
bool ScopeTree::isVisualRootScope() const bool ScopeTree::isVisualRootScope() const
{ {
return m_parentScope && m_parentScope->m_parentScope && m_parentScope->m_parentScope->m_parentScope == nullptr; return m_parentScope && m_parentScope->m_parentScope
&& m_parentScope->m_parentScope->m_parentScope == nullptr;
} }
QString ScopeTree::name() const class IssueLocationWithContext
{ {
return m_name; public:
} IssueLocationWithContext(const QString &code, const QQmlJS::AST::SourceLocation &location) {
struct IssueLocationWithContext
{
IssueLocationWithContext(const QString& code, QQmlJS::AST::SourceLocation location) {
int before = std::max(0,code.lastIndexOf('\n', location.offset)); int before = std::max(0,code.lastIndexOf('\n', location.offset));
beforeText = code.midRef(before+1, location.offset - (before+1) ); m_beforeText = code.midRef(before + 1, int(location.offset - (before + 1)));
issueText = code.midRef(location.offset, location.length); m_issueText = code.midRef(location.offset, location.length);
int after = code.indexOf('\n', location.offset + location.length); int after = code.indexOf('\n', int(location.offset + location.length));
afterText = code.midRef(location.offset+location.length, after - (location.offset+location.length)); m_afterText = code.midRef(int(location.offset + location.length),
int(after - (location.offset+location.length)));
} }
QStringRef beforeText; QStringRef beforeText() const { return m_beforeText; }
QStringRef issueText; QStringRef issueText() const { return m_issueText; }
QStringRef afterText; QStringRef afterText() const { return m_afterText; }
private:
QStringRef m_beforeText;
QStringRef m_issueText;
QStringRef m_afterText;
}; };
bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> &qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput& colorOut) const bool ScopeTree::recheckIdentifiers(
const QString &code, const QHash<QString, ScopeTree::ConstPtr> &qmlIDs,
const ScopeTree *root, const QString &rootId, ColorOutput &colorOut) const
{ {
bool noUnqualifiedIdentifier = true; bool noUnqualifiedIdentifier = true;
// revisit all scopes // revisit all scopes
QQueue<const ScopeTree*> workQueue; QQueue<const ScopeTree *> workQueue;
workQueue.enqueue(this); workQueue.enqueue(this);
while (!workQueue.empty()) { while (!workQueue.empty()) {
const ScopeTree* currentScope = workQueue.dequeue(); const ScopeTree *currentScope = workQueue.dequeue();
for (const auto &handler : currentScope->m_unmatchedSignalHandlers) { for (const auto &handler : currentScope->m_unmatchedSignalHandlers) {
colorOut.write("Warning: ", Warning); colorOut.write("Warning: ", Warning);
colorOut.write(QString::fromLatin1( colorOut.write(QString::fromLatin1(
@ -147,7 +152,7 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
printContext(colorOut, code, handler.second); printContext(colorOut, code, handler.second);
} }
for (auto idLocationPair : currentScope->m_accessedIdentifiers) { for (const auto &idLocationPair : qAsConst(currentScope->m_accessedIdentifiers)) {
if (qmlIDs.contains(idLocationPair.first)) if (qmlIDs.contains(idLocationPair.first))
continue; continue;
if (currentScope->isIdInCurrentScope(idLocationPair.first)) { if (currentScope->isIdInCurrentScope(idLocationPair.first)) {
@ -156,17 +161,17 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
noUnqualifiedIdentifier = false; noUnqualifiedIdentifier = false;
colorOut.write("Warning: ", Warning); colorOut.write("Warning: ", Warning);
auto location = idLocationPair.second; auto location = idLocationPair.second;
colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine, colorOut.write(QString::fromLatin1("unqualified access at %1:%2\n")
location.startColumn), Normal); .arg(location.startLine).arg(location.startColumn),
Normal);
printContext(colorOut, code, location); printContext(colorOut, code, location);
// root(JS) --> program(qml) --> (first element) // root(JS) --> program(qml) --> (first element)
if (root->m_childScopes[0]->m_childScopes[0]->m_currentScopeQMLIdentifiers.contains(idLocationPair.first)) { const auto firstElement = root->m_childScopes[0]->m_childScopes[0];
ScopeTree *parentScope = currentScope->m_parentScope; if (firstElement->m_properties.contains(idLocationPair.first)
while (parentScope && parentScope->scopeType() != ScopeType::QMLScope) { || firstElement->m_methods.contains(idLocationPair.first)
parentScope = parentScope->m_parentScope; || firstElement->m_enums.contains(idLocationPair.first)) {
}
colorOut.write("Note: ", Info); colorOut.write("Note: ", Info);
colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal ); colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal );
colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal); colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal);
@ -175,92 +180,87 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
colorOut.write(("You first have to give the root element an id\n")); colorOut.write(("You first have to give the root element an id\n"));
} }
IssueLocationWithContext issueLocationWithContext {code, location}; IssueLocationWithContext issueLocationWithContext {code, location};
colorOut.write(issueLocationWithContext.beforeText.toString(), Normal); colorOut.write(issueLocationWithContext.beforeText().toString(), Normal);
colorOut.write(rootId + QLatin1Char('.'), Hint); colorOut.write(rootId + QLatin1Char('.'), Hint);
colorOut.write(issueLocationWithContext.issueText.toString(), Normal); colorOut.write(issueLocationWithContext.issueText().toString(), Normal);
colorOut.write(issueLocationWithContext.afterText + QLatin1Char('\n'), Normal); colorOut.write(issueLocationWithContext.afterText() + QLatin1Char('\n'), Normal);
} else if (currentScope->isIdInjectedFromSignal(idLocationPair.first)) { } else if (currentScope->isIdInjectedFromSignal(idLocationPair.first)) {
auto qmlScope = currentScope->getCurrentQMLScope(); auto methodUsages = currentScope->currentQMLScope()->m_injectedSignalIdentifiers
auto methodUsages = qmlScope->m_injectedSignalIdentifiers.values(idLocationPair.first); .values(idLocationPair.first);
auto location = idLocationPair.second; auto location = idLocationPair.second;
// sort the list of signal handlers by their occurrence in the source code // sort the list of signal handlers by their occurrence in the source code
// then, we select the first one whose location is after the unqualified id // then, we select the first one whose location is after the unqualified id
// and go one step backwards to get the one which we actually need // and go one step backwards to get the one which we actually need
std::sort(methodUsages.begin(), methodUsages.end(), [](const MethodUsage m1, const MethodUsage m2) { std::sort(methodUsages.begin(), methodUsages.end(),
return m1.loc.startLine < m2.loc.startLine || (m1.loc.startLine == m2.loc.startLine && m1.loc.startColumn < m2.loc.startColumn); [](const MethodUsage &m1, const MethodUsage &m2) {
return m1.loc.startLine < m2.loc.startLine
|| (m1.loc.startLine == m2.loc.startLine
&& m1.loc.startColumn < m2.loc.startColumn);
}); });
auto oneBehindIt = std::find_if(methodUsages.begin(), methodUsages.end(), [&location](MethodUsage methodUsage) { auto oneBehindIt = std::find_if(methodUsages.begin(), methodUsages.end(),
return location.startLine < methodUsage.loc.startLine || (location.startLine == methodUsage.loc.startLine && location.startColumn < methodUsage.loc.startColumn); [&location](const MethodUsage &methodUsage) {
return location.startLine < methodUsage.loc.startLine
|| (location.startLine == methodUsage.loc.startLine
&& location.startColumn < methodUsage.loc.startColumn);
}); });
auto methodUsage = *(--oneBehindIt); auto methodUsage = *(--oneBehindIt);
colorOut.write("Note:", Info); colorOut.write("Note:", Info);
colorOut.write(idLocationPair.first + QString::asprintf(" is accessible in this scope because you are handling a signal at %d:%d\n", methodUsage.loc.startLine, methodUsage.loc.startColumn), Normal); colorOut.write(
idLocationPair.first + QString::fromLatin1(
" is accessible in this scope because "
"you are handling a signal at %1:%2\n")
.arg(methodUsage.loc.startLine).arg(methodUsage.loc.startColumn),
Normal);
colorOut.write("Consider using a function instead\n", Normal); colorOut.write("Consider using a function instead\n", Normal);
IssueLocationWithContext context {code, methodUsage.loc}; IssueLocationWithContext context {code, methodUsage.loc};
colorOut.write(context.beforeText + QLatin1Char(' ')); colorOut.write(context.beforeText() + QLatin1Char(' '));
colorOut.write(methodUsage.hasMultilineHandlerBody ? "function(" : "(", Hint); colorOut.write(methodUsage.hasMultilineHandlerBody ? "function(" : "(", Hint);
const auto parameters = methodUsage.method.parameterNames(); const auto parameters = methodUsage.method.parameterNames();
for (int numParams = parameters.size(); numParams > 0; --numParams) { for (int numParams = parameters.size(); numParams > 0; --numParams) {
colorOut.write(parameters.at(parameters.size() - numParams), Hint); colorOut.write(parameters.at(parameters.size() - numParams), Hint);
if (numParams > 1) { if (numParams > 1)
colorOut.write(", ", Hint); colorOut.write(", ", Hint);
}
} }
colorOut.write(methodUsage.hasMultilineHandlerBody ? ")" : ") => ", Hint); colorOut.write(methodUsage.hasMultilineHandlerBody ? ")" : ") => ", Hint);
colorOut.write(" {...", Normal); colorOut.write(" {...", Normal);
} }
colorOut.write("\n\n\n", Normal); colorOut.write("\n\n\n", Normal);
} }
for (auto const& childScope: currentScope->m_childScopes) { for (auto const& childScope: currentScope->m_childScopes)
workQueue.enqueue(childScope); workQueue.enqueue(childScope);
}
} }
return noUnqualifiedIdentifier; return noUnqualifiedIdentifier;
} }
QMap<QString, LanguageUtils::FakeMetaMethod>const &ScopeTree::methods() const bool ScopeTree::isIdInCurrentQMlScopes(const QString &id) const
{ {
return m_methods; const auto *qmlScope = currentQMLScope();
return qmlScope->m_properties.contains(id)
|| qmlScope->m_methods.contains(id)
|| qmlScope->m_enums.contains(id);
} }
bool ScopeTree::isIdInCurrentQMlScopes(QString id) const bool ScopeTree::isIdInCurrentJSScopes(const QString &id) const
{
auto qmlScope = getCurrentQMLScope();
return qmlScope->m_currentScopeQMLIdentifiers.contains(id) || qmlScope->m_methods.contains(id);
}
bool ScopeTree::isIdInCurrentJSScopes(QString id) const
{ {
auto jsScope = this; auto jsScope = this;
while (jsScope) { while (jsScope) {
if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_currentScopeJSIdentifiers.contains(id)) if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_jsIdentifiers.contains(id))
return true; return true;
jsScope = jsScope->m_parentScope; jsScope = jsScope->m_parentScope;
} }
return false; return false;
} }
bool ScopeTree::isIdInjectedFromSignal(QString id) const bool ScopeTree::isIdInjectedFromSignal(const QString &id) const
{ {
auto qmlScope = getCurrentQMLScope(); return currentQMLScope()->m_injectedSignalIdentifiers.contains(id);
return qmlScope->m_injectedSignalIdentifiers.contains(id);
} }
const ScopeTree *ScopeTree::getCurrentQMLScope() const const ScopeTree *ScopeTree::currentQMLScope() const
{ {
auto qmlScope = this; auto qmlScope = this;
while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) { while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope)
qmlScope = qmlScope->m_parentScope; qmlScope = qmlScope->m_parentScope;
}
return qmlScope;
}
ScopeTree *ScopeTree::getCurrentQMLScope()
{
auto qmlScope = this;
while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
qmlScope = qmlScope->m_parentScope;
}
return qmlScope; return qmlScope;
} }
@ -268,30 +268,37 @@ void ScopeTree::printContext(ColorOutput &colorOut, const QString &code,
const QQmlJS::AST::SourceLocation &location) const const QQmlJS::AST::SourceLocation &location) const
{ {
IssueLocationWithContext issueLocationWithContext {code, location}; IssueLocationWithContext issueLocationWithContext {code, location};
colorOut.write(issueLocationWithContext.beforeText.toString(), Normal); colorOut.write(issueLocationWithContext.beforeText().toString(), Normal);
colorOut.write(issueLocationWithContext.issueText.toString(), Error); colorOut.write(issueLocationWithContext.issueText().toString(), Error);
colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal); colorOut.write(issueLocationWithContext.afterText().toString() + QLatin1Char('\n'), Normal);
int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t')); int tabCount = issueLocationWithContext.beforeText().count(QLatin1Char('\t'));
colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount) colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText().length() - tabCount)
+ QString("\t").repeated(tabCount) + QString("\t").repeated(tabCount)
+ QString("^").repeated(location.length) + QString("^").repeated(location.length)
+ QLatin1Char('\n'), Normal); + QLatin1Char('\n'), Normal);
} }
ScopeType ScopeTree::scopeType() {return m_scopeType;} void ScopeTree::addExport(const QString &name, const QString &package,
const ComponentVersion &version)
void ScopeTree::addMethod(LanguageUtils::FakeMetaMethod method)
{ {
m_methods.insert(method.methodName(), method); m_exports.append(Export(package, name, version, 0));
} }
void ScopeTree::addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject) void ScopeTree::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
{ {
if (metaObject) { m_exports[exportIndex].setMetaObjectRevision(metaObjectRevision);
auto methodCount = metaObject->methodCount(); }
for (auto i = 0; i < methodCount; ++i) {
auto method = metaObject->method(i); ScopeTree::Export::Export(QString package, QString type, const ComponentVersion &version,
this->addMethod(method); 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();
} }

View File

@ -29,15 +29,28 @@
#ifndef SCOPETREE_H #ifndef SCOPETREE_H
#define SCOPETREE_H #define SCOPETREE_H
#include "fakemetaobject.h" //
#include "private/qqmljsast_p.h" // W A R N I N G
#include "private/qqmljssourcelocation_p.h" // -------------
//
// 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 <QSet> #include "metatypes.h"
#include <QString> #include "componentversion.h"
#include <QMap>
enum MessageColors{ #include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljssourcelocation_p.h>
#include <QtCore/qset.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
enum MessageColors
{
Error, Error,
Warning, Warning,
Info, Info,
@ -54,58 +67,137 @@ enum class ScopeType
struct MethodUsage struct MethodUsage
{ {
LanguageUtils::FakeMetaMethod method; MetaMethod method;
QQmlJS::AST::SourceLocation loc; QQmlJS::AST::SourceLocation loc;
bool hasMultilineHandlerBody; bool hasMultilineHandlerBody;
}; };
class ColorOutput; class ColorOutput;
class ScopeTree
class ScopeTree { {
Q_DISABLE_COPY_MOVE(ScopeTree)
public: public:
ScopeTree(ScopeType type, QString name="<none given>", ScopeTree* parentScope=nullptr); using Ptr = QSharedPointer<ScopeTree>;
~ScopeTree() {qDeleteAll(m_childScopes);} using ConstPtr = QSharedPointer<const ScopeTree>;
ScopeTree* createNewChildScope(ScopeType type, QString name); class Export {
ScopeTree* parentScope(); public:
Export() = default;
Export(QString package, QString type, const ComponentVersion &version,
int metaObjectRevision);
void insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope); bool isValid() const;
void insertQMLIdentifier(QString id);
void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody); int metaObjectRevision() const { return m_metaObjectRevision; }
void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding 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="<none given>", ScopeTree *parentScope=nullptr);
~ScopeTree() { qDeleteAll(m_childScopes); }
ScopeTree *createNewChildScope(ScopeType type, QString name);
ScopeTree *parentScope() { return m_parentScope; }
void insertJSIdentifier(const QString &id, QQmlJS::AST::VariableScope scope);
void insertSignalIdentifier(const QString &id, const MetaMethod &method,
const QQmlJS::AST::SourceLocation &loc, bool hasMultilineHandlerBody);
// inserts property as qml identifier as well as the corresponding
void insertPropertyIdentifier(const MetaProperty &prop);
void addUnmatchedSignalHandler(const QString &handler, void addUnmatchedSignalHandler(const QString &handler,
const QQmlJS::AST::SourceLocation &location); const QQmlJS::AST::SourceLocation &location);
bool isIdInCurrentScope(QString const &id) const; bool isIdInCurrentScope(const QString &id) const;
void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports); void addIdToAccssedIfNotInParentScopes(
const QPair<QString, QQmlJS::AST::SourceLocation> &idLocationPair,
const QSet<QString> &unknownImports);
bool isVisualRootScope() const; bool isVisualRootScope() const;
QString name() const; QString name() const { return m_name; }
bool recheckIdentifiers(const QString &code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr>& qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput &colorOut) const; bool recheckIdentifiers(
ScopeType scopeType(); const QString &code, const QHash<QString, ScopeTree::ConstPtr> &qmlIDs,
void addMethod(LanguageUtils::FakeMetaMethod); const ScopeTree *root, const QString &rootId, ColorOutput &colorOut) const;
void addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject);
QMap<QString, LanguageUtils::FakeMetaMethod>const & methods() const; ScopeType scopeType() const { return m_scopeType; }
void addMethods(const QHash<QString, MetaMethod> &methods) { m_methods.unite(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.name(), prop); }
QHash<QString, MetaProperty> properties() const { return m_properties; }
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; }
private: private:
QSet<QString> m_currentScopeJSIdentifiers; QSet<QString> m_jsIdentifiers;
QSet<QString> m_currentScopeQMLIdentifiers;
QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers; QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers;
QMap<QString, LanguageUtils::FakeMetaMethod> m_methods;
QHash<QString, MetaMethod> m_methods;
QHash<QString, MetaProperty> m_properties;
QHash<QString, MetaEnum> m_enums;
QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers; QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers;
QVector<ScopeTree*> m_childScopes;
ScopeTree *m_parentScope;
QString m_name;
ScopeType m_scopeType;
QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_unmatchedSignalHandlers; QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_unmatchedSignalHandlers;
bool isIdInCurrentQMlScopes(QString id) const; QVector<ScopeTree *> m_childScopes;
bool isIdInCurrentJSScopes(QString id) const; ScopeTree *m_parentScope = nullptr;
bool isIdInjectedFromSignal(QString id) const;
const ScopeTree* getCurrentQMLScope() const; QString m_name;
ScopeTree* getCurrentQMLScope(); QString m_className;
void printContext(ColorOutput& colorOut, const QString &code, 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;
bool isIdInCurrentQMlScopes(const QString &id) const;
bool isIdInCurrentJSScopes(const QString &id) const;
bool isIdInjectedFromSignal(const QString &id) const;
const ScopeTree *currentQMLScope() const;
void printContext(ColorOutput &colorOut, const QString &code,
const QQmlJS::AST::SourceLocation &location) const; const QQmlJS::AST::SourceLocation &location) const;
}; };
#endif // SCOPETREE_H #endif // SCOPETREE_H

View File

@ -26,25 +26,18 @@
** **
****************************************************************************/ ****************************************************************************/
#include "qmljstypedescriptionreader.h" #include "typedescriptionreader.h"
#include <private/qqmljsparser_p.h> #include <QtQml/private/qqmljsparser_p.h>
#include <private/qqmljslexer_p.h> #include <QtQml/private/qqmljslexer_p.h>
#include <private/qqmljsengine_p.h> #include <QtQml/private/qqmljsengine_p.h>
#include <QDir> #include <QtCore/qdir.h>
#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;
using namespace QQmlJS::AST; using namespace QQmlJS::AST;
using namespace LanguageUtils;
QString toString(const AST::UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.')) QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
{ {
QString result; QString result;
@ -58,17 +51,8 @@ QString toString(const AST::UiQualifiedId *qualifiedId, QChar delimiter = QLatin
return result; return result;
} }
TypeDescriptionReader::TypeDescriptionReader(const QString &fileName, const QString &data)
: _fileName (fileName), _source(data), _objects(0)
{
}
TypeDescriptionReader::~TypeDescriptionReader()
{
}
bool TypeDescriptionReader::operator()( bool TypeDescriptionReader::operator()(
QHash<QString, FakeMetaObject::ConstPtr> *objects, QHash<QString, ScopeTree::ConstPtr> *objects,
QList<ModuleApiInfo> *moduleApis, QList<ModuleApiInfo> *moduleApis,
QStringList *dependencies) QStringList *dependencies)
{ {
@ -77,32 +61,22 @@ bool TypeDescriptionReader::operator()(
Lexer lexer(&engine); Lexer lexer(&engine);
Parser parser(&engine); Parser parser(&engine);
lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true); lexer.setCode(m_source, /*lineno = */ 1, /*qmlMode = */true);
if (!parser.parse()) { if (!parser.parse()) {
_errorMessage = QString::fromLatin1("%1:%2: %3").arg( m_errorMessage = QString::fromLatin1("%1:%2: %3").arg(
QString::number(parser.errorLineNumber()), QString::number(parser.errorLineNumber()),
QString::number(parser.errorColumnNumber()), QString::number(parser.errorColumnNumber()),
parser.errorMessage()); parser.errorMessage());
return false; return false;
} }
_objects = objects; m_objects = objects;
_moduleApis = moduleApis; m_moduleApis = moduleApis;
_dependencies = dependencies; m_dependencies = dependencies;
readDocument(parser.ast()); readDocument(parser.ast());
return _errorMessage.isEmpty(); return m_errorMessage.isEmpty();
}
QString TypeDescriptionReader::errorMessage() const
{
return _errorMessage;
}
QString TypeDescriptionReader::warningMessage() const
{
return _warningMessage;
} }
void TypeDescriptionReader::readDocument(UiProgram *ast) void TypeDescriptionReader::readDocument(UiProgram *ast)
@ -112,12 +86,12 @@ void TypeDescriptionReader::readDocument(UiProgram *ast)
return; return;
} }
if (!ast->headers || ast->headers->next || !AST::cast<AST::UiImport *>(ast->headers->headerItem)) { if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast->headers->headerItem)) {
addError(SourceLocation(), tr("Expected a single import.")); addError(SourceLocation(), tr("Expected a single import."));
return; return;
} }
UiImport *import = AST::cast<AST::UiImport *>(ast->headers->headerItem); auto *import = cast<UiImport *>(ast->headers->headerItem);
if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) { if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
addError(import->importToken, tr("Expected import of QtQuick.tooling.")); addError(import->importToken, tr("Expected import of QtQuick.tooling."));
return; return;
@ -129,7 +103,8 @@ void TypeDescriptionReader::readDocument(UiProgram *ast)
} }
if (import->version->majorVersion != 1) { if (import->version->majorVersion != 1) {
addError(import->version->firstSourceLocation(), tr("Major version different from 1 not supported.")); addError(import->version->firstSourceLocation(),
tr("Major version different from 1 not supported."));
return; return;
} }
@ -138,7 +113,7 @@ void TypeDescriptionReader::readDocument(UiProgram *ast)
return; return;
} }
UiObjectDefinition *module = AST::cast<UiObjectDefinition *>(ast->members->member); auto *module = cast<UiObjectDefinition *>(ast->members->member);
if (!module) { if (!module) {
addError(SourceLocation(), tr("Expected document to contain a single object definition.")); addError(SourceLocation(), tr("Expected document to contain a single object definition."));
return; return;
@ -156,9 +131,9 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
{ {
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member); auto *component = cast<UiObjectDefinition *>(member);
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) { if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
readDependencies(script); readDependencies(script);
continue; continue;
@ -168,7 +143,8 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
if (component) if (component)
typeName = toString(component->qualifiedTypeNameId); typeName = toString(component->qualifiedTypeNameId);
if (!component || (typeName != QLatin1String("Component") && typeName != QLatin1String("ModuleApi"))) { if (!component || (typeName != QLatin1String("Component")
&& typeName != QLatin1String("ModuleApi"))) {
continue; continue;
} }
@ -181,8 +157,8 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message) void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
{ {
_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg( m_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
QDir::toNativeSeparators(_fileName), QDir::toNativeSeparators(m_fileName),
QString::number(loc.startLine), QString::number(loc.startLine),
QString::number(loc.startColumn), QString::number(loc.startColumn),
message); message);
@ -190,8 +166,8 @@ void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &m
void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message) void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
{ {
_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg( m_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
QDir::toNativeSeparators(_fileName), QDir::toNativeSeparators(m_fileName),
QString::number(loc.startLine), QString::number(loc.startLine),
QString::number(loc.startColumn), QString::number(loc.startColumn),
message); message);
@ -199,83 +175,82 @@ void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString
void TypeDescriptionReader::readDependencies(UiScriptBinding *ast) void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
{ {
ExpressionStatement *stmt = AST::cast<ExpressionStatement*>(ast->statement); auto *stmt = cast<ExpressionStatement*>(ast->statement);
if (!stmt) { if (!stmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions")); addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
return; return;
} }
ArrayPattern *exp = AST::cast<ArrayPattern *>(stmt->expression); auto *exp = cast<ArrayPattern *>(stmt->expression);
if (!exp) { if (!exp) {
addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions")); addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
return; return;
} }
for (PatternElementList *l = exp->elements; l; l = l->next) { for (PatternElementList *l = exp->elements; l; l = l->next) {
//StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer); auto *str = cast<StringLiteral *>(l->element->initializer);
StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer); *m_dependencies << str->value.toString();
*_dependencies << str->value.toString();
} }
} }
void TypeDescriptionReader::readComponent(UiObjectDefinition *ast) void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{ {
FakeMetaObject::Ptr fmo(new FakeMetaObject); ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope));
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member); auto *component = cast<UiObjectDefinition *>(member);
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (component) { if (component) {
QString name = toString(component->qualifiedTypeNameId); QString name = toString(component->qualifiedTypeNameId);
if (name == QLatin1String("Property")) if (name == QLatin1String("Property"))
readProperty(component, fmo); readProperty(component, scope);
else if (name == QLatin1String("Method") || name == QLatin1String("Signal")) else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
readSignalOrMethod(component, name == QLatin1String("Method"), fmo); readSignalOrMethod(component, name == QLatin1String("Method"), scope);
else if (name == QLatin1String("Enum")) else if (name == QLatin1String("Enum"))
readEnum(component, fmo); readEnum(component, scope);
else else
addWarning(component->firstSourceLocation(), addWarning(component->firstSourceLocation(),
tr("Expected only Property, Method, Signal and Enum object definitions, not \"%1\".") tr("Expected only Property, Method, Signal and Enum object definitions, "
.arg(name)); "not \"%1\".").arg(name));
} else if (script) { } else if (script) {
QString name = toString(script->qualifiedId); QString name = toString(script->qualifiedId);
if (name == QLatin1String("name")) { if (name == QLatin1String("name")) {
fmo->setClassName(readStringBinding(script)); scope->setClassName(readStringBinding(script));
} else if (name == QLatin1String("prototype")) { } else if (name == QLatin1String("prototype")) {
fmo->setSuperclassName(readStringBinding(script)); scope->setSuperclassName(readStringBinding(script));
} else if (name == QLatin1String("defaultProperty")) { } else if (name == QLatin1String("defaultProperty")) {
fmo->setDefaultPropertyName(readStringBinding(script)); scope->setDefaultPropertyName(readStringBinding(script));
} else if (name == QLatin1String("exports")) { } else if (name == QLatin1String("exports")) {
readExports(script, fmo); readExports(script, scope);
} else if (name == QLatin1String("exportMetaObjectRevisions")) { } else if (name == QLatin1String("exportMetaObjectRevisions")) {
readMetaObjectRevisions(script, fmo); readMetaObjectRevisions(script, scope);
} else if (name == QLatin1String("attachedType")) { } else if (name == QLatin1String("attachedType")) {
fmo->setAttachedTypeName(readStringBinding(script)); scope->setAttachedTypeName(readStringBinding(script));
} else if (name == QLatin1String("isSingleton")) { } else if (name == QLatin1String("isSingleton")) {
fmo->setIsSingleton(readBoolBinding(script)); scope->setIsSingleton(readBoolBinding(script));
} else if (name == QLatin1String("isCreatable")) { } else if (name == QLatin1String("isCreatable")) {
fmo->setIsCreatable(readBoolBinding(script)); scope->setIsCreatable(readBoolBinding(script));
} else if (name == QLatin1String("isComposite")) { } else if (name == QLatin1String("isComposite")) {
fmo->setIsComposite(readBoolBinding(script)); scope->setIsComposite(readBoolBinding(script));
} else { } else {
addWarning(script->firstSourceLocation(), addWarning(script->firstSourceLocation(),
tr("Expected only name, prototype, defaultProperty, attachedType, exports, " tr("Expected only name, prototype, defaultProperty, attachedType, "
"isSingleton, isCreatable, isComposite and exportMetaObjectRevisions " "exports, isSingleton, isCreatable, isComposite and "
"script bindings, not \"%1\".").arg(name)); "exportMetaObjectRevisions script bindings, not \"%1\".").arg(name));
} }
} else { } else {
addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions.")); addWarning(member->firstSourceLocation(),
tr("Expected only script bindings and object definitions."));
} }
} }
if (fmo->className().isEmpty()) { if (scope->className().isEmpty()) {
addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding.")); addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
return; return;
} }
// ### add implicit export into the package of c++ types // ### add implicit export into the package of c++ types
fmo->addExport(fmo->className(), QStringLiteral("<cpp>"), ComponentVersion()); scope->addExport(scope->className(), QStringLiteral("<cpp>"), ComponentVersion());
fmo->updateFingerprint(); m_objects->insert(scope->className(), scope);
_objects->insert(fmo->className(), fmo);
} }
void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast) void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
@ -284,7 +259,7 @@ void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (script) { if (script) {
const QString name = toString(script->qualifiedId); const QString name = toString(script->qualifiedId);
@ -304,58 +279,65 @@ void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
} }
if (!apiInfo.version.isValid()) { if (!apiInfo.version.isValid()) {
addError(ast->firstSourceLocation(), tr("ModuleApi definition has no or invalid version binding.")); addError(ast->firstSourceLocation(),
tr("ModuleApi definition has no or invalid version binding."));
return; return;
} }
if (_moduleApis) if (m_moduleApis)
_moduleApis->append(apiInfo); m_moduleApis->append(apiInfo);
} }
void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo) void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod,
const ScopeTree::Ptr &scope)
{ {
FakeMetaMethod fmm; MetaMethod metaMethod;
// ### confusion between Method and Slot. Method should be removed. // ### confusion between Method and Slot. Method should be removed.
if (isMethod) if (isMethod)
fmm.setMethodType(FakeMetaMethod::Slot); metaMethod.setMethodType(MetaMethod::Slot);
else else
fmm.setMethodType(FakeMetaMethod::Signal); metaMethod.setMethodType(MetaMethod::Signal);
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member); auto *component = cast<UiObjectDefinition *>(member);
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (component) { if (component) {
QString name = toString(component->qualifiedTypeNameId); QString name = toString(component->qualifiedTypeNameId);
if (name == QLatin1String("Parameter")) if (name == QLatin1String("Parameter")) {
readParameter(component, &fmm); readParameter(component, &metaMethod);
else } else {
addWarning(component->firstSourceLocation(), tr("Expected only Parameter object definitions.")); addWarning(component->firstSourceLocation(),
tr("Expected only Parameter object definitions."));
}
} else if (script) { } else if (script) {
QString name = toString(script->qualifiedId); QString name = toString(script->qualifiedId);
if (name == QLatin1String("name")) if (name == QLatin1String("name")) {
fmm.setMethodName(readStringBinding(script)); metaMethod.setMethodName(readStringBinding(script));
else if (name == QLatin1String("type")) } else if (name == QLatin1String("type")) {
fmm.setReturnType(readStringBinding(script)); metaMethod.setReturnType(readStringBinding(script));
else if (name == QLatin1String("revision")) } else if (name == QLatin1String("revision")) {
fmm.setRevision(readIntBinding(script)); metaMethod.setRevision(readIntBinding(script));
else } else {
addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings.")); addWarning(script->firstSourceLocation(),
tr("Expected only name and type script bindings."));
}
} else { } else {
addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions.")); addWarning(member->firstSourceLocation(),
tr("Expected only script bindings and object definitions."));
} }
} }
if (fmm.methodName().isEmpty()) { if (metaMethod.methodName().isEmpty()) {
addError(ast->firstSourceLocation(), tr("Method or signal is missing a name script binding.")); addError(ast->firstSourceLocation(),
tr("Method or signal is missing a name script binding."));
return; return;
} }
fmo->addMethod(fmm); scope->addMethod(metaMethod);
} }
void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo) void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)
{ {
QString name; QString name;
QString type; QString type;
@ -366,69 +348,75 @@ void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (!script) { if (!script) {
addWarning(member->firstSourceLocation(), tr("Expected script binding.")); addWarning(member->firstSourceLocation(), tr("Expected script binding."));
continue; continue;
} }
QString id = toString(script->qualifiedId); QString id = toString(script->qualifiedId);
if (id == QLatin1String("name")) if (id == QLatin1String("name")) {
name = readStringBinding(script); name = readStringBinding(script);
else if (id == QLatin1String("type")) } else if (id == QLatin1String("type")) {
type = readStringBinding(script); type = readStringBinding(script);
else if (id == QLatin1String("isPointer")) } else if (id == QLatin1String("isPointer")) {
isPointer = readBoolBinding(script); isPointer = readBoolBinding(script);
else if (id == QLatin1String("isReadonly")) } else if (id == QLatin1String("isReadonly")) {
isReadonly = readBoolBinding(script); isReadonly = readBoolBinding(script);
else if (id == QLatin1String("isList")) } else if (id == QLatin1String("isList")) {
isList = readBoolBinding(script); isList = readBoolBinding(script);
else if (id == QLatin1String("revision")) } else if (id == QLatin1String("revision")) {
revision = readIntBinding(script); revision = readIntBinding(script);
else } else {
addWarning(script->firstSourceLocation(), tr("Expected only type, name, revision, isPointer, isReadonly and isList script bindings.")); addWarning(script->firstSourceLocation(),
tr("Expected only type, name, revision, isPointer, isReadonly and"
" isList script bindings."));
}
} }
if (name.isEmpty() || type.isEmpty()) { if (name.isEmpty() || type.isEmpty()) {
addError(ast->firstSourceLocation(), tr("Property object is missing a name or type script binding.")); addError(ast->firstSourceLocation(),
tr("Property object is missing a name or type script binding."));
return; return;
} }
fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision)); scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, revision));
} }
void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo) void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)
{ {
FakeMetaEnum fme; MetaEnum metaEnum;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (!script) { if (!script) {
addWarning(member->firstSourceLocation(), tr("Expected script binding.")); addWarning(member->firstSourceLocation(), tr("Expected script binding."));
continue; continue;
} }
QString name = toString(script->qualifiedId); QString name = toString(script->qualifiedId);
if (name == QLatin1String("name")) if (name == QLatin1String("name")) {
fme.setName(readStringBinding(script)); metaEnum.setName(readStringBinding(script));
else if (name == QLatin1String("values")) } else if (name == QLatin1String("values")) {
readEnumValues(script, &fme); readEnumValues(script, &metaEnum);
else } else {
addWarning(script->firstSourceLocation(), tr("Expected only name and values script bindings.")); addWarning(script->firstSourceLocation(),
tr("Expected only name and values script bindings."));
}
} }
fmo->addEnum(fme); scope->addEnum(metaEnum);
} }
void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm) void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, MetaMethod *metaMethod)
{ {
QString name; QString name;
QString type; QString type;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
UiScriptBinding *script = AST::cast<UiScriptBinding *>(member); auto *script = cast<UiScriptBinding *>(member);
if (!script) { if (!script) {
addWarning(member->firstSourceLocation(), tr("Expected script binding.")); addWarning(member->firstSourceLocation(), tr("Expected script binding."));
continue; continue;
@ -446,29 +434,30 @@ void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMetho
} else if (id == QLatin1String("isList")) { } else if (id == QLatin1String("isList")) {
// ### unhandled // ### unhandled
} else { } else {
addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings.")); addWarning(script->firstSourceLocation(),
tr("Expected only name and type script bindings."));
} }
} }
fmm->addParameter(name, type); metaMethod->addParameter(name, type);
} }
QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast) QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
{ {
QTC_ASSERT(ast, return QString()); Q_ASSERT(ast);
if (!ast->statement) { if (!ast->statement) {
addError(ast->colonToken, tr("Expected string after colon.")); addError(ast->colonToken, tr("Expected string after colon."));
return QString(); return QString();
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected string after colon.")); addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
return QString(); return QString();
} }
StringLiteral *stringLit = AST::cast<StringLiteral *>(expStmt->expression); auto *stringLit = cast<StringLiteral *>(expStmt->expression);
if (!stringLit) { if (!stringLit) {
addError(expStmt->firstSourceLocation(), tr("Expected string after colon.")); addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
return QString(); return QString();
@ -477,23 +466,23 @@ QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
return stringLit->value.toString(); return stringLit->value.toString();
} }
bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast) bool TypeDescriptionReader::readBoolBinding(UiScriptBinding *ast)
{ {
QTC_ASSERT(ast, return false); Q_ASSERT(ast);
if (!ast->statement) { if (!ast->statement) {
addError(ast->colonToken, tr("Expected boolean after colon.")); addError(ast->colonToken, tr("Expected boolean after colon."));
return false; return false;
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon.")); addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
return false; return false;
} }
TrueLiteral *trueLit = AST::cast<TrueLiteral *>(expStmt->expression); auto *trueLit = cast<TrueLiteral *>(expStmt->expression);
FalseLiteral *falseLit = AST::cast<FalseLiteral *>(expStmt->expression); auto *falseLit = cast<FalseLiteral *>(expStmt->expression);
if (!trueLit && !falseLit) { if (!trueLit && !falseLit) {
addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon.")); addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
return false; return false;
@ -502,22 +491,23 @@ bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
return trueLit; return trueLit;
} }
double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast) double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
{ {
QTC_ASSERT(ast, return qQNaN()); Q_ASSERT(ast);
if (!ast->statement) { if (!ast->statement) {
addError(ast->colonToken, tr("Expected numeric literal after colon.")); addError(ast->colonToken, tr("Expected numeric literal after colon."));
return 0; return 0;
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon.")); addError(ast->statement->firstSourceLocation(),
tr("Expected numeric literal after colon."));
return 0; return 0;
} }
NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression); auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
if (!numericLit) { if (!numericLit) {
addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon.")); addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
return 0; return 0;
@ -531,26 +521,29 @@ ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBindin
ComponentVersion invalidVersion; ComponentVersion invalidVersion;
if (!ast || !ast->statement) { if (!ast || !ast->statement) {
addError((ast ? ast->colonToken : SourceLocation()), tr("Expected numeric literal after colon.")); addError((ast ? ast->colonToken : SourceLocation()),
tr("Expected numeric literal after colon."));
return invalidVersion; return invalidVersion;
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon.")); addError(ast->statement->firstSourceLocation(),
tr("Expected numeric literal after colon."));
return invalidVersion; return invalidVersion;
} }
NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression); auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
if (!numericLit) { if (!numericLit) {
addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon.")); addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
return invalidVersion; return invalidVersion;
} }
return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length)); return ComponentVersion(m_source.mid(numericLit->literalToken.begin(),
numericLit->literalToken.length));
} }
int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast) int TypeDescriptionReader::readIntBinding(UiScriptBinding *ast)
{ {
double v = readNumericBinding(ast); double v = readNumericBinding(ast);
int i = static_cast<int>(v); int i = static_cast<int>(v);
@ -563,31 +556,33 @@ int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
return i; return i;
} }
void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Ptr fmo) void TypeDescriptionReader::readExports(UiScriptBinding *ast, const ScopeTree::Ptr &scope)
{ {
QTC_ASSERT(ast, return); Q_ASSERT(ast);
if (!ast->statement) { if (!ast->statement) {
addError(ast->colonToken, tr("Expected array of strings after colon.")); addError(ast->colonToken, tr("Expected array of strings after colon."));
return; return;
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected array of strings after colon.")); addError(ast->statement->firstSourceLocation(),
tr("Expected array of strings after colon."));
return; return;
} }
ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression); auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
if (!arrayLit) { if (!arrayLit) {
addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon.")); addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
return; return;
} }
for (PatternElementList *it = arrayLit->elements; it; it = it->next) { for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
StringLiteral *stringLit = AST::cast<StringLiteral *>(it->element->initializer); auto *stringLit = cast<StringLiteral *>(it->element->initializer);
if (!stringLit) { if (!stringLit) {
addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only string literal members.")); addError(arrayLit->firstSourceLocation(),
tr("Expected array literal with only string literal members."));
return; return;
} }
QString exp = stringLit->value.toString(); QString exp = stringLit->value.toString();
@ -596,7 +591,9 @@ void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Pt
ComponentVersion version(exp.mid(spaceIdx + 1)); ComponentVersion version(exp.mid(spaceIdx + 1));
if (spaceIdx == -1 || !version.isValid()) { if (spaceIdx == -1 || !version.isValid()) {
addError(stringLit->firstSourceLocation(), tr("Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'.")); addError(stringLit->firstSourceLocation(),
tr("Expected string literal to contain 'Package/Name major.minor' "
"or 'Name major.minor'."));
continue; continue;
} }
QString package; QString package;
@ -605,42 +602,46 @@ void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Pt
QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1)); QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
// ### relocatable exports where package is empty? // ### relocatable exports where package is empty?
fmo->addExport(name, package, version); scope->addExport(name, package, version);
} }
} }
void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMetaObject::Ptr fmo) void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast,
const ScopeTree::Ptr &scope)
{ {
QTC_ASSERT(ast, return); Q_ASSERT(ast);
if (!ast->statement) { if (!ast->statement) {
addError(ast->colonToken, tr("Expected array of numbers after colon.")); addError(ast->colonToken, tr("Expected array of numbers after colon."));
return; return;
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected array of numbers after colon.")); addError(ast->statement->firstSourceLocation(),
tr("Expected array of numbers after colon."));
return; return;
} }
ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression); auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
if (!arrayLit) { if (!arrayLit) {
addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon.")); addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
return; return;
} }
int exportIndex = 0; int exportIndex = 0;
const int exportCount = fmo->exports().size(); const int exportCount = scope->exports().size();
for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) { for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
NumericLiteral *numberLit = cast<NumericLiteral *>(it->element->initializer); auto *numberLit = cast<NumericLiteral *>(it->element->initializer);
if (!numberLit) { if (!numberLit) {
addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only number literal members.")); addError(arrayLit->firstSourceLocation(),
tr("Expected array literal with only number literal members."));
return; return;
} }
if (exportIndex >= exportCount) { if (exportIndex >= exportCount) {
addError(numberLit->firstSourceLocation(), tr("Meta object revision without matching export.")); addError(numberLit->firstSourceLocation(),
tr("Meta object revision without matching export."));
return; return;
} }
@ -651,11 +652,11 @@ void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMe
return; return;
} }
fmo->setExportMetaObjectRevision(exportIndex, metaObjectRevision); scope->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
} }
} }
void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme) void TypeDescriptionReader::readEnumValues(UiScriptBinding *ast, MetaEnum *metaEnum)
{ {
if (!ast) if (!ast)
return; return;
@ -664,27 +665,27 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt
return; return;
} }
auto *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon.")); addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
return; return;
} }
if (auto *objectLit = AST::cast<ObjectPattern *>(expStmt->expression)) { if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) {
for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
if (PatternProperty *assignement = it->property) { if (PatternProperty *assignement = it->property) {
if (auto *name = AST::cast<StringLiteralPropertyName *>(assignement->name)) { if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) {
fme->addKey(name->id.toString()); metaEnum->addKey(name->id.toString());
continue; continue;
} }
} }
addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
} }
} else if (auto *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression)) { } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) {
for (PatternElementList *it = arrayLit->elements; it; it = it->next) { for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
if (PatternElement *element = it->element) { if (PatternElement *element = it->element) {
if (auto *name = AST::cast<StringLiteral *>(element->initializer)) { if (auto *name = cast<StringLiteral *>(element->initializer)) {
fme->addKey(name->value.toString()); metaEnum->addKey(name->value.toString());
continue; continue;
} }
} }

View File

@ -0,0 +1,105 @@
/****************************************************************************
**
** 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>
struct ModuleApiInfo
{
QString uri;
ComponentVersion version;
QString cppName;
};
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,
QList<ModuleApiInfo> *moduleApis,
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 readModuleApi(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::AST::SourceLocation &loc, const QString &message);
void addWarning(const QQmlJS::AST::SourceLocation &loc, const QString &message);
QString m_fileName;
QString m_source;
QString m_errorMessage;
QString m_warningMessage;
QHash<QString, ScopeTree::ConstPtr> *m_objects = nullptr;
QList<ModuleApiInfo> *m_moduleApis = nullptr;
QStringList *m_dependencies = nullptr;
};
#endif // TYPEDESCRIPTIONREADER_H