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:
parent
61d7713ead
commit
6fb335ebce
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
Loading…
Reference in New Issue