aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/qml/conf.h5
-rw-r--r--tools/qml/main.cpp27
-rw-r--r--tools/qml/qml.pro4
-rw-r--r--tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in10
-rw-r--r--tools/qmlcachegen/generateloader.cpp233
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp20
-rw-r--r--tools/qmlcachegen/qtquickcompiler.prf45
-rw-r--r--tools/qmlcachegen/resourcefilter.cpp51
-rw-r--r--tools/qmleasing/splineeditor.cpp4
-rw-r--r--tools/qmlformat/commentastvisitor.cpp284
-rw-r--r--tools/qmlformat/commentastvisitor.h134
-rw-r--r--tools/qmlformat/dumpastvisitor.cpp1321
-rw-r--r--tools/qmlformat/dumpastvisitor.h149
-rw-r--r--tools/qmlformat/main.cpp218
-rw-r--r--tools/qmlformat/qmlformat.pro17
-rw-r--r--tools/qmlformat/restructureastvisitor.cpp178
-rw-r--r--tools/qmlformat/restructureastvisitor.h50
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in10
-rw-r--r--tools/qmlimportscanner/main.cpp6
-rw-r--r--tools/qmllint/componentversion.cpp63
-rw-r--r--tools/qmllint/componentversion.h43
-rw-r--r--tools/qmllint/fakemetaobject.cpp600
-rw-r--r--tools/qmllint/fakemetaobject.h236
-rw-r--r--tools/qmllint/findunqualified.cpp839
-rw-r--r--tools/qmllint/findunqualified.h87
-rw-r--r--tools/qmllint/importedmembersvisitor.cpp158
-rw-r--r--tools/qmllint/importedmembersvisitor.h73
-rw-r--r--tools/qmllint/main.cpp87
-rw-r--r--tools/qmllint/metatypes.h163
-rw-r--r--tools/qmllint/qcoloroutput.cpp158
-rw-r--r--tools/qmllint/qcoloroutput.h (renamed from tools/qmllint/qcoloroutput_p.h)25
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.h103
-rw-r--r--tools/qmllint/qmllint.pro17
-rw-r--r--tools/qmllint/scopetree.cpp491
-rw-r--r--tools/qmllint/scopetree.h193
-rw-r--r--tools/qmllint/typedescriptionreader.cpp (renamed from tools/qmllint/qmljstypedescriptionreader.cpp)412
-rw-r--r--tools/qmllint/typedescriptionreader.h105
-rw-r--r--tools/qmlmin/main.cpp2
-rw-r--r--tools/qmlplugindump/qmlplugindump.pro10
-rw-r--r--tools/qmlpreview/qmlpreviewapplication.cpp4
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp2
-rw-r--r--tools/qmlscene/main.cpp3
-rw-r--r--tools/qmltime/qmltime.cpp39
-rw-r--r--tools/qmltime/qmltime.h68
-rw-r--r--tools/qmltime/qmltime.pro6
-rw-r--r--tools/shared/qmlstreamwriter.cpp (renamed from tools/qmlplugindump/qmlstreamwriter.cpp)16
-rw-r--r--tools/shared/qmlstreamwriter.h (renamed from tools/qmlplugindump/qmlstreamwriter.h)0
-rw-r--r--tools/shared/shared.pri10
-rw-r--r--tools/tools.pro3
49 files changed, 4538 insertions, 2244 deletions
diff --git a/tools/qml/conf.h b/tools/qml/conf.h
index 0c9d8613a0..4ad45428ed 100644
--- a/tools/qml/conf.h
+++ b/tools/qml/conf.h
@@ -30,6 +30,7 @@
#include <QtQml/QQmlContext>
#include <QtQml/QQmlListProperty>
+#include <QtQml/qqml.h>
#include <QObject>
#include <QUrl>
@@ -38,6 +39,7 @@ class PartialScene : public QObject
Q_OBJECT
Q_PROPERTY(QUrl container READ container WRITE setContainer NOTIFY containerChanged)
Q_PROPERTY(QString itemType READ itemType WRITE setItemType NOTIFY itemTypeChanged)
+ QML_ELEMENT
public:
PartialScene(QObject *parent = 0) : QObject(parent)
{}
@@ -72,13 +74,14 @@ class Config : public QObject
Q_OBJECT
Q_PROPERTY(QQmlListProperty<PartialScene> sceneCompleters READ sceneCompleters)
Q_CLASSINFO("DefaultProperty", "sceneCompleters")
+ QML_NAMED_ELEMENT(Configuration)
public:
Config (QObject* parent=0) : QObject(parent)
{}
QQmlListProperty<PartialScene> sceneCompleters()
{
- return QQmlListProperty<PartialScene>(this, completers);
+ return QQmlListProperty<PartialScene>(this, &completers);
}
QList<PartialScene*> completers;
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 15b3539188..beeec88f07 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -37,6 +37,7 @@
#include <QFileOpenEvent>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
+#include <QSurfaceFormat>
#ifdef QT_WIDGETS_LIB
#include <QApplication>
#endif // QT_WIDGETS_LIB
@@ -59,6 +60,7 @@
#include <QLibraryInfo>
#include <qqml.h>
#include <qqmldebug.h>
+#include <qqmlfileselector.h>
#include <private/qmemory_p.h>
#include <private/qtqmlglobal_p.h>
@@ -434,8 +436,6 @@ int main(int argc, char *argv[])
app->setOrganizationDomain("qt-project.org");
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- qmlRegisterType<Config>("QmlRuntime.Config", 1, 0, "Configuration");
- qmlRegisterType<PartialScene>("QmlRuntime.Config", 1, 0, "PartialScene");
QQmlApplicationEngine e;
QStringList files;
QString confFile;
@@ -512,6 +512,9 @@ int main(int argc, char *argv[])
"Backend is one of: default, vulkan, metal, d3d11, gl"),
QStringLiteral("backend"));
parser.addOption(rhiOption);
+ QCommandLineOption selectorOption(QStringLiteral("S"), QCoreApplication::translate("main",
+ "Add selector to the list of QQmlFileSelectors."), QStringLiteral("selector"));
+ parser.addOption(selectorOption);
// Positional arguments
parser.addPositionalArgument("files",
@@ -551,6 +554,26 @@ int main(int argc, char *argv[])
#endif
for (const QString &importPath : parser.values(importOption))
e.addImportPath(importPath);
+
+ QStringList customSelectors;
+ for (const QString &selector : parser.values(selectorOption))
+ customSelectors.append(selector);
+ if (!customSelectors.isEmpty()) {
+ QQmlFileSelector *selector = QQmlFileSelector::get(&e);
+ selector->setExtraSelectors(customSelectors);
+ }
+
+#if defined(QT_GUI_LIB) && QT_CONFIG(opengl)
+ if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE") || qEnvironmentVariableIsSet("QML_CORE_PROFILE")) {
+ QSurfaceFormat surfaceFormat;
+ surfaceFormat.setStencilBufferSize(8);
+ surfaceFormat.setDepthBufferSize(24);
+ surfaceFormat.setVersion(4, 1);
+ surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
+ QSurfaceFormat::setDefaultFormat(surfaceFormat);
+ }
+#endif
+
files << parser.values(qmlFileOption);
if (parser.isSet(configOption))
confFile = parser.value(configOption);
diff --git a/tools/qml/qml.pro b/tools/qml/qml.pro
index 5dcbb3567a..83e2e1bf80 100644
--- a/tools/qml/qml.pro
+++ b/tools/qml/qml.pro
@@ -5,6 +5,10 @@ qtHaveModule(widgets): QT += widgets
HEADERS += conf.h
SOURCES += main.cpp
RESOURCES += qml.qrc
+CONFIG += qmltypes
+
+QML_IMPORT_NAME = QmlRuntime.Config
+QML_IMPORT_VERSION = 1.0
QMAKE_TARGET_DESCRIPTION = QML Runtime
diff --git a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
index 75fbb0fcf3..be2113b258 100644
--- a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
+++ b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
@@ -50,13 +50,9 @@ but not all the files it references.
get_filename_component(input_resource ${_resource} ABSOLUTE)
- execute_process(COMMAND ${compiler_path} -filter-resource-file ${input_resource} -o ${new_resource_file} OUTPUT_VARIABLE remaining_files)
- if(remaining_files)
- list(APPEND filtered_rcc_files ${new_resource_file})
- list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\")
- else()
- list(APPEND loader_flags \"--resource-file-mapping=${_resource}\")
- endif()
+ execute_process(COMMAND ${compiler_path} --filter-resource-file ${input_resource} -o ${new_resource_file} OUTPUT_VARIABLE remaining_files)
+ list(APPEND filtered_rcc_files ${new_resource_file})
+ list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\")
set(rcc_file_with_compilation_units)
diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp
index 1c8a5a016a..71286137eb 100644
--- a/tools/qmlcachegen/generateloader.cpp
+++ b/tools/qmlcachegen/generateloader.cpp
@@ -100,228 +100,6 @@ QString symbolNamespaceForPath(const QString &relativePath)
return mangledIdentifier(symbol);
}
-struct VirtualDirectoryEntry
-{
- QString name;
- QVector<VirtualDirectoryEntry*> dirEntries;
- int firstChildIndex = -1; // node index inside generated data
- bool isDirectory = true;
-
- VirtualDirectoryEntry()
- {}
-
- ~VirtualDirectoryEntry()
- {
- qDeleteAll(dirEntries);
- }
-
- VirtualDirectoryEntry *append(const QString &name)
- {
- for (QVector<VirtualDirectoryEntry*>::Iterator it = dirEntries.begin(), end = dirEntries.end();
- it != end; ++it) {
- if ((*it)->name == name)
- return *it;
- }
-
- VirtualDirectoryEntry *subEntry = new VirtualDirectoryEntry;
- subEntry->name = name;
- dirEntries.append(subEntry);
- return subEntry;
- }
-
- void appendEmptyFile(const QString &name)
- {
- VirtualDirectoryEntry *subEntry = new VirtualDirectoryEntry;
- subEntry->name = name;
- subEntry->isDirectory = false;
- dirEntries.append(subEntry);
- }
-
- bool isEmpty() const { return dirEntries.isEmpty(); }
-};
-
-struct DataStream
-{
- DataStream(QVector<unsigned char > *data = nullptr)
- : data(data)
- {}
-
- qint64 currentOffset() const { return data->size(); }
-
- DataStream &operator<<(quint16 value)
- {
- unsigned char d[2];
- qToBigEndian(value, d);
- data->append(d[0]);
- data->append(d[1]);
- return *this;
- }
- DataStream &operator<<(quint32 value)
- {
- unsigned char d[4];
- qToBigEndian(value, d);
- data->append(d[0]);
- data->append(d[1]);
- data->append(d[2]);
- data->append(d[3]);
- return *this;
- }
-private:
- QVector<unsigned char> *data;
-};
-
-static bool resource_sort_order(const VirtualDirectoryEntry *lhs, const VirtualDirectoryEntry *rhs)
-{
- return qt_hash(lhs->name) < qt_hash(rhs->name);
-}
-
-struct ResourceTree
-{
- ResourceTree()
- {}
-
- void serialize(VirtualDirectoryEntry &root, QVector<unsigned char> *treeData, QVector<unsigned char> *stringData)
- {
- treeStream = DataStream(treeData);
- stringStream = DataStream(stringData);
-
- QStack<VirtualDirectoryEntry *> directories;
-
- {
- directories.push(&root);
- while (!directories.isEmpty()) {
- VirtualDirectoryEntry *entry = directories.pop();
- registerString(entry->name);
- if (entry->isDirectory)
- directories << entry->dirEntries;
- }
- }
-
- {
- quint32 currentDirectoryIndex = 1;
- directories.push(&root);
- while (!directories.isEmpty()) {
- VirtualDirectoryEntry *entry = directories.pop();
- entry->firstChildIndex = currentDirectoryIndex;
- currentDirectoryIndex += entry->dirEntries.count();
- std::sort(entry->dirEntries.begin(), entry->dirEntries.end(), resource_sort_order);
-
- for (QVector<VirtualDirectoryEntry*>::ConstIterator child = entry->dirEntries.constBegin(), end = entry->dirEntries.constEnd();
- child != end; ++child) {
- if ((*child)->isDirectory)
- directories << *child;
- }
- }
- }
-
- {
- writeTreeEntry(&root);
- directories.push(&root);
- while (!directories.isEmpty()) {
- VirtualDirectoryEntry *entry = directories.pop();
-
- for (QVector<VirtualDirectoryEntry*>::ConstIterator child = entry->dirEntries.constBegin(), end = entry->dirEntries.constEnd();
- child != end; ++child) {
- writeTreeEntry(*child);
- if ((*child)->isDirectory)
- directories << (*child);
- }
- }
- }
- }
-
-private:
- DataStream treeStream;
- DataStream stringStream;
- QHash<QString, qint64> stringOffsets;
-
- void registerString(const QString &name)
- {
- if (stringOffsets.contains(name))
- return;
- const qint64 offset = stringStream.currentOffset();
- stringOffsets.insert(name, offset);
-
- stringStream << quint16(name.length())
- << quint32(qt_hash(name));
- for (int i = 0; i < name.length(); ++i)
- stringStream << quint16(name.at(i).unicode());
- }
-
- void writeTreeEntry(VirtualDirectoryEntry *entry)
- {
- treeStream << quint32(stringOffsets.value(entry->name))
- << quint16(entry->isDirectory ? 0x2 : 0x0); // Flags: File or Directory
-
- if (entry->isDirectory) {
- treeStream << quint32(entry->dirEntries.count())
- << quint32(entry->firstChildIndex);
- } else {
- treeStream << quint16(QLocale::AnyCountry) << quint16(QLocale::C)
- << quint32(0x0);
- }
- }
-};
-
-static QByteArray generateResourceDirectoryTree(QTextStream &code, const QStringList &qrcFiles,
- const QStringList &sortedRetainedFiles)
-{
- QByteArray call;
- if (qrcFiles.isEmpty())
- return call;
-
- VirtualDirectoryEntry resourceDirs;
- resourceDirs.name = QStringLiteral("/");
-
- for (const QString &entry : qrcFiles) {
- const QStringList segments = entry.split(QLatin1Char('/'), QString::SkipEmptyParts);
-
- VirtualDirectoryEntry *dirEntry = &resourceDirs;
-
- for (int i = 0; i < segments.count() - 1; ++i)
- dirEntry = dirEntry->append(segments.at(i));
- if (!std::binary_search(sortedRetainedFiles.begin(), sortedRetainedFiles.end(), entry))
- dirEntry->appendEmptyFile(segments.last());
- }
-
- if (resourceDirs.isEmpty())
- return call;
-
- QVector<unsigned char> names;
- QVector<unsigned char> tree;
- ResourceTree().serialize(resourceDirs, &tree, &names);
-
- code << "static const unsigned char qt_resource_tree[] = {\n";
- for (int i = 0; i < tree.count(); ++i) {
- code << uint(tree.at(i));
- if (i < tree.count() - 1)
- code << ',';
- if (i % 16 == 0)
- code << '\n';
- }
- code << "};\n";
-
- code << "static const unsigned char qt_resource_names[] = {\n";
- for (int i = 0; i < names.count(); ++i) {
- code << uint(names.at(i));
- if (i < names.count() - 1)
- code << ',';
- if (i % 16 == 0)
- code << '\n';
- }
- code << "};\n";
-
- code << "static const unsigned char qt_resource_empty_payout[] = { 0, 0, 0, 0, 0 };\n";
-
- code << "QT_BEGIN_NAMESPACE\n";
- code << "extern Q_CORE_EXPORT bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);\n";
- code << "QT_END_NAMESPACE\n";
-
- call = "QT_PREPEND_NAMESPACE(qRegisterResourceData)(/*version*/0x01, qt_resource_tree, qt_resource_names, qt_resource_empty_payout);\n";
-
- return call;
-}
-
static QString qtResourceNameForFile(const QString &fileName)
{
QFileInfo fi(fileName);
@@ -332,9 +110,8 @@ static QString qtResourceNameForFile(const QString &fileName)
return name;
}
-bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedRetainedFiles,
- const QString &outputFileName, const QStringList &resourceFileMappings,
- QString *errorString)
+bool generateLoader(const QStringList &compiledFiles, const QString &outputFileName,
+ const QStringList &resourceFileMappings, QString *errorString)
{
QByteArray generatedLoaderCode;
@@ -345,9 +122,6 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR
stream << "#include <QtCore/qurl.h>\n";
stream << "\n";
- QByteArray resourceRegisterCall = generateResourceDirectoryTree(stream, compiledFiles,
- sortedRetainedFiles);
-
stream << "namespace QmlCacheGeneratedCode {\n";
for (int i = 0; i < compiledFiles.count(); ++i) {
const QString compiledFile = compiledFiles.at(i);
@@ -385,9 +159,6 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR
stream << " registration.lookupCachedQmlUnit = &lookupCachedUnit;\n";
stream << " QQmlPrivate::qmlregister(QQmlPrivate::QmlUnitCacheHookRegistration, &registration);\n";
- if (!resourceRegisterCall.isEmpty())
- stream << resourceRegisterCall;
-
stream << "}\n\n";
stream << "Registry::~Registry() {\n";
stream << " QQmlPrivate::qmlunregister(QQmlPrivate::QmlUnitCacheHookRegistration, quintptr(&lookupCachedUnit));\n";
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index ac9cf039d3..bc74e7b08e 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -48,9 +48,8 @@
using namespace QQmlJS;
int filterResourceFile(const QString &input, const QString &output);
-bool generateLoader(const QStringList &compiledFiles, const QStringList &retainedFiles,
- const QString &output, const QStringList &resourceFileMappings,
- QString *errorString);
+bool generateLoader(const QStringList &compiledFiles, const QString &output,
+ const QStringList &resourceFileMappings, QString *errorString);
QString symbolNamespaceForPath(const QString &relativePath);
QSet<QString> illegalNames;
@@ -85,9 +84,9 @@ Error Error::augment(const QString &contextErrorMessage) const
QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::DiagnosticMessage &m)
{
QString message;
- message = fileName + QLatin1Char(':') + QString::number(m.line) + QLatin1Char(':');
- if (m.column > 0)
- message += QString::number(m.column) + QLatin1Char(':');
+ message = fileName + QLatin1Char(':') + QString::number(m.loc.startLine) + QLatin1Char(':');
+ if (m.loc.startColumn > 0)
+ message += QString::number(m.loc.startColumn) + QLatin1Char(':');
if (m.isError())
message += QLatin1String(" error: ");
@@ -375,7 +374,7 @@ static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFil
{
QTextStream stream(&hexifiedData);
const uchar *end = begin + size;
- stream << hex;
+ stream << Qt::hex;
int col = 0;
for (const uchar *data = begin; data < end; ++data, ++col) {
if (data > begin)
@@ -425,8 +424,6 @@ int main(int argc, char **argv)
parser.addOption(resourceFileMappingOption);
QCommandLineOption resourceOption(QStringLiteral("resource"), QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name"));
parser.addOption(resourceOption);
- QCommandLineOption retainOption(QStringLiteral("retain"), QCoreApplication::translate("main", "Qt resource file the contents of which should not be replaced by empty stubs"), QCoreApplication::translate("main", "resource-file-name"));
- parser.addOption(retainOption);
QCommandLineOption resourcePathOption(QStringLiteral("resource-path"), QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path"));
parser.addOption(resourcePathOption);
@@ -474,12 +471,9 @@ int main(int argc, char **argv)
if (target == GenerateLoader) {
ResourceFileMapper mapper(sources);
- ResourceFileMapper retain(parser.values(retainOption));
Error error;
- QStringList retainedFiles = retain.qmlCompilerFiles();
- std::sort(retainedFiles.begin(), retainedFiles.end());
- if (!generateLoader(mapper.qmlCompilerFiles(), retainedFiles, outputFileName,
+ if (!generateLoader(mapper.qmlCompilerFiles(), outputFileName,
parser.values(resourceFileMappingOption), &error.message)) {
error.augment(QLatin1String("Error generating loader stub: ")).print();
return EXIT_FAILURE;
diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf
index 17f1cb5a79..7353dfb17a 100644
--- a/tools/qmlcachegen/qtquickcompiler.prf
+++ b/tools/qmlcachegen/qtquickcompiler.prf
@@ -1,5 +1,15 @@
if(qtc_run|lupdate_run): return()
+!contains(QT, qml) {
+ qt_modules = \
+ $$replace(QT, -private$, _private) \
+ $$replace(QT_PRIVATE, -private$, _private)
+ qt_modules = $$resolve_depends(qt_modules, "QT.", ".depends" ".run_depends")
+ !contains(qt_modules, qml): \
+ return()
+ unset(qt_modules)
+}
+
qtPrepareTool(QML_CACHEGEN, qmlcachegen, _FILTER)
qtPrepareTool(QMAKE_RCC, rcc, _DEP)
@@ -16,20 +26,6 @@ defineReplace(qmlCacheResourceFileOutputName) {
return($${name})
}
-defineTest(qtQuickRetainSources) {
- for(retainedRes, QTQUICK_COMPILER_RETAINED_RESOURCES) {
- equals(1, $$retainedRes): return(true)
- }
- return(false)
-}
-
-defineTest(qtQuickSkippedResourceFile) {
- for(skippedRes, QTQUICK_COMPILER_SKIPPED_RESOURCES) {
- equals(1, $$skippedRes): return(true)
- }
- return(false)
-}
-
# Flatten RESOURCES that may contain individual files or objects
load(resources_functions)
qtFlattenResources()
@@ -39,29 +35,14 @@ NEWRESOURCES =
QMLCACHE_RESOURCE_FILES =
for(res, RESOURCES) {
- qtQuickSkippedResourceFile($$res) {
- NEWRESOURCES += $$res
- next()
- }
-
absRes = $$absolute_path($$res, $$_PRO_FILE_PWD_)
rccContents = $$system($$QMAKE_RCC_DEP -list $$system_quote($$absRes),lines)
contains(rccContents,.*\\.js$)|contains(rccContents,.*\\.qml$)|contains(rccContents,.*\\.mjs$) {
new_resource = $$qmlCacheResourceFileOutputName($$res)
mkpath($$dirname(new_resource))
- qtQuickRetainSources($$res) {
- NEWRESOURCES += $$res
- QMLCACHE_LOADER_FLAGS += --retain=$$shell_quote($$absRes)
- } else {
- remaining_files = $$system($$QML_CACHEGEN_FILTER -filter-resource-file \
- -o $$system_quote($$new_resource) $$system_quote($$absRes),lines)
- !isEmpty(remaining_files) {
- NEWRESOURCES += $$new_resource
- QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes=$$new_resource)
- } else {
- QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes)
- }
- }
+ dummy = $$system($$QML_CACHEGEN_FILTER --filter-resource-file -o $$system_quote($$new_resource) $$system_quote($$absRes))
+ NEWRESOURCES += $$new_resource
+ QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes=$$new_resource)
QMLCACHE_RESOURCE_FILES += $$absRes
diff --git a/tools/qmlcachegen/resourcefilter.cpp b/tools/qmlcachegen/resourcefilter.cpp
index 3ad6e9ca0d..261102dcbe 100644
--- a/tools/qmlcachegen/resourcefilter.cpp
+++ b/tools/qmlcachegen/resourcefilter.cpp
@@ -57,8 +57,6 @@ int filterResourceFile(const QString &input, const QString &output)
QXmlStreamWriter writer(&outputString);
writer.setAutoFormatting(true);
- QStringList remainingFiles;
-
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
switch (reader.readNext()) {
@@ -139,47 +137,36 @@ int filterResourceFile(const QString &input, const QString &output)
if (currentFileName.isEmpty())
continue;
- if (!currentFileName.endsWith(QStringLiteral(".qml"))
- && !currentFileName.endsWith(QStringLiteral(".js"))
- && !currentFileName.endsWith(QStringLiteral(".mjs"))) {
- writer.writeStartElement(QStringLiteral("file"));
-
- if (!fileAttributes.hasAttribute(QStringLiteral("alias")))
- fileAttributes.append(QStringLiteral("alias"), currentFileName);
+ writer.writeStartElement(QStringLiteral("file"));
- currentFileName = inputDirectory.absoluteFilePath(currentFileName);
- currentFileName = outputDirectory.relativeFilePath(currentFileName);
+ if (!fileAttributes.hasAttribute(QStringLiteral("alias")))
+ fileAttributes.append(QStringLiteral("alias"), currentFileName);
- remainingFiles << currentFileName;
+ currentFileName = inputDirectory.absoluteFilePath(currentFileName);
+ currentFileName = outputDirectory.relativeFilePath(currentFileName);
- writer.writeAttributes(fileAttributes);
- writer.writeCharacters(currentFileName);
- writer.writeEndElement();
- }
+ writer.writeAttributes(fileAttributes);
+ writer.writeCharacters(currentFileName);
+ writer.writeEndElement();
continue;
default: break;
}
}
- if (!remainingFiles.isEmpty()) {
- QFile outputFile(output);
- if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- fprintf(stderr, "Cannot open %s for writing.\n", qPrintable(output));
- return EXIT_FAILURE;
- }
- const QByteArray outputStringUtf8 = outputString.toUtf8();
- if (outputFile.write(outputStringUtf8) != outputStringUtf8.size())
- return EXIT_FAILURE;
+ QFile outputFile(output);
+ if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ fprintf(stderr, "Cannot open %s for writing.\n", qPrintable(output));
+ return EXIT_FAILURE;
+ }
+ const QByteArray outputStringUtf8 = outputString.toUtf8();
+ if (outputFile.write(outputStringUtf8) != outputStringUtf8.size())
+ return EXIT_FAILURE;
- outputFile.close();
- if (outputFile.error() != QFileDevice::NoError)
- return EXIT_FAILURE;
+ outputFile.close();
+ if (outputFile.error() != QFileDevice::NoError)
+ return EXIT_FAILURE;
- // The build system expects this output if we wrote a qrc file and no output
- // if no files remain.
- fprintf(stdout, "New resource file written with %d files.\n", remainingFiles.count());
- }
return EXIT_SUCCESS;
}
diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp
index 69850dc7a1..c1afbbb441 100644
--- a/tools/qmleasing/splineeditor.cpp
+++ b/tools/qmleasing/splineeditor.cpp
@@ -30,11 +30,13 @@
#include "segmentproperties.h"
#include <QPainter>
+#include <QPainterPath>
#include <QMouseEvent>
#include <QContextMenuEvent>
#include <QDebug>
#include <QApplication>
#include <QVector>
+#include <QPainterPath>
const int canvasWidth = 640;
const int canvasHeight = 320;
@@ -674,7 +676,7 @@ void SplineEditor::setEasingCurve(const QString &code)
return;
if (code.startsWith(QLatin1Char('[')) && code.endsWith(QLatin1Char(']'))) {
const QStringRef cleanCode(&code, 1, code.size() - 2);
- const auto stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
+ const auto stringList = cleanCode.split(QLatin1Char(','), Qt::SkipEmptyParts);
if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
QVector<qreal> realList;
realList.reserve(stringList.count());
diff --git a/tools/qmlformat/commentastvisitor.cpp b/tools/qmlformat/commentastvisitor.cpp
new file mode 100644
index 0000000000..9383fa29aa
--- /dev/null
+++ b/tools/qmlformat/commentastvisitor.cpp
@@ -0,0 +1,284 @@
+/****************************************************************************
+**
+** 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 "commentastvisitor.h"
+
+CommentAstVisitor::CommentAstVisitor(QQmlJS::Engine *engine, Node *rootNode) : m_engine(engine)
+{
+ rootNode->accept(this);
+
+ // Look for complete orphans that have not been attached to *any* node
+ QVector<Comment> completeOrphans;
+
+ for (const auto &comment : m_engine->comments()) {
+ if (isCommentAttached(comment))
+ continue;
+
+ bool found_orphan = false;
+ for (const auto &orphanList : orphanComments().values()) {
+ for (const auto &orphan : orphanList) {
+ if (orphan.contains(comment)) {
+ found_orphan = true;
+ break;
+ }
+ }
+
+ if (found_orphan)
+ break;
+ }
+
+ if (found_orphan)
+ continue;
+
+ completeOrphans.append(Comment(m_engine, Comment::Location::Front, {comment}));
+ }
+
+ m_orphanComments[nullptr] = completeOrphans;
+}
+
+QList<SourceLocation> CommentAstVisitor::findCommentsInLine(quint32 line, bool includePrevious) const
+{
+ QList<SourceLocation> results;
+ if (line == 0)
+ return results;
+
+ for (const auto &location : m_engine->comments()) {
+ if (location.startLine != line)
+ continue;
+
+ if (isCommentAttached(location))
+ continue;
+
+ results.append(location);
+
+ if (includePrevious) {
+ // See if we can find any more comments above this one
+ auto previous = findCommentsInLine(line - 1, true);
+
+ // Iterate it in reverse to restore the correct order
+ for (auto it = previous.rbegin(); it != previous.rend(); it++) {
+ results.prepend(*it);
+ }
+ }
+
+ break;
+ }
+
+ return results;
+}
+
+bool CommentAstVisitor::isCommentAttached(const SourceLocation &location) const
+{
+ for (const auto &value : m_attachedComments.values()) {
+ if (value.contains(location))
+ return true;
+ }
+
+ for (const auto &value : m_listItemComments.values()) {
+ if (value.contains(location))
+ return true;
+ }
+
+ // If a comment is already marked as an orphan of a Node that counts as attached too.
+ for (const auto &orphanList : m_orphanComments.values()) {
+ for (const auto &value : orphanList) {
+ if (value.contains(location))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Comment CommentAstVisitor::findComment(SourceLocation first, SourceLocation last,
+ int locations) const
+{
+ if (locations & Comment::Location::Front) {
+ quint32 searchAt = first.startLine - 1;
+
+ const auto comments = findCommentsInLine(searchAt, true);
+ if (!comments.isEmpty())
+ return Comment(m_engine, Comment::Location::Front, comments);
+ }
+
+ if (locations & Comment::Location::Front_Inline) {
+ quint32 searchAt = first.startLine;
+
+ const auto comments = findCommentsInLine(searchAt);
+ if (!comments.isEmpty())
+ return Comment(m_engine, Comment::Location::Front_Inline, comments);
+ }
+
+ if (locations & Comment::Location::Back_Inline) {
+ quint32 searchAt = last.startLine;
+
+ const auto comments = findCommentsInLine(searchAt);
+ if (!comments.isEmpty())
+ return Comment(m_engine, Comment::Location::Back_Inline, comments);
+ }
+
+ if (locations & Comment::Location::Back) {
+ quint32 searchAt = last.startLine + 1;
+
+ const auto comments = findCommentsInLine(searchAt);
+ if (!comments.isEmpty())
+ return Comment(m_engine, Comment::Location::Back, comments);
+ }
+
+ return Comment();
+
+}
+
+Comment CommentAstVisitor::findComment(Node *node, int locations) const
+{
+ return findComment(node->firstSourceLocation(), node->lastSourceLocation(), locations);
+}
+
+QVector<Comment> CommentAstVisitor::findOrphanComments(Node *node) const
+{
+ QVector<Comment> comments;
+
+ for (auto &comment : m_engine->comments()) {
+ if (isCommentAttached(comment))
+ continue;
+
+ if (comment.begin() <= node->firstSourceLocation().begin()
+ || comment.end() > node->lastSourceLocation().end()) {
+ continue;
+ }
+
+ comments.append(Comment(m_engine, Comment::Location::Front, {comment}));
+ }
+
+ return comments;
+}
+
+void CommentAstVisitor::attachComment(Node *node, int locations)
+{
+ auto comment = findComment(node, locations);
+
+ if (comment.isValid())
+ m_attachedComments[node] = comment;
+}
+
+bool CommentAstVisitor::visit(UiScriptBinding *node)
+{
+ attachComment(node);
+ return true;
+}
+
+bool CommentAstVisitor::visit(StatementList *node)
+{
+ for (auto *item = node; item != nullptr; item = item->next)
+ attachComment(item->statement, Comment::Front | Comment::Back_Inline);
+ return true;
+}
+
+void CommentAstVisitor::endVisit(StatementList *node)
+{
+ m_orphanComments[node] = findOrphanComments(node);
+}
+
+bool CommentAstVisitor::visit(UiObjectBinding *node)
+{
+ attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back);
+ return true;
+}
+
+bool CommentAstVisitor::visit(UiObjectDefinition *node)
+{
+ attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back);
+ return true;
+}
+
+void CommentAstVisitor::endVisit(UiObjectDefinition *node)
+{
+ m_orphanComments[node] = findOrphanComments(node);
+}
+
+bool CommentAstVisitor::visit(UiArrayBinding *node)
+{
+ attachComment(node);
+ return true;
+}
+
+void CommentAstVisitor::endVisit(UiArrayBinding *node)
+{
+ m_orphanComments[node] = findOrphanComments(node);
+}
+
+bool CommentAstVisitor::visit(UiEnumDeclaration *node)
+{
+ attachComment(node);
+ return true;
+}
+
+void CommentAstVisitor::endVisit(UiEnumDeclaration *node)
+{
+ m_orphanComments[node] = findOrphanComments(node);
+}
+
+bool CommentAstVisitor::visit(UiEnumMemberList *node)
+{
+ for (auto *item = node; item != nullptr; item = item->next) {
+ auto comment = findComment(item->memberToken,
+ item->valueToken.isValid() ? item->valueToken : item->memberToken,
+ Comment::Front | Comment::Back_Inline);
+
+ if (comment.isValid())
+ m_listItemComments[item->memberToken.begin()] = comment;
+ }
+
+ m_orphanComments[node] = findOrphanComments(node);
+
+ return true;
+}
+
+bool CommentAstVisitor::visit(UiPublicMember *node)
+{
+ attachComment(node);
+ return true;
+}
+
+bool CommentAstVisitor::visit(FunctionDeclaration *node)
+{
+ attachComment(node);
+ return true;
+}
+
+bool CommentAstVisitor::visit(UiImport *node)
+{
+ attachComment(node);
+ return true;
+}
+
+bool CommentAstVisitor::visit(UiPragma *node)
+{
+ attachComment(node);
+ return true;
+}
diff --git a/tools/qmlformat/commentastvisitor.h b/tools/qmlformat/commentastvisitor.h
new file mode 100644
index 0000000000..21c7eb6416
--- /dev/null
+++ b/tools/qmlformat/commentastvisitor.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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 COMMENTASTVISITOR_H
+#define COMMENTASTVISITOR_H
+
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+
+#include <QHash>
+#include <QString>
+#include <QVector>
+
+using namespace QQmlJS::AST;
+using namespace QQmlJS;
+
+struct Comment
+{
+ enum Location : int
+ {
+ Front = 1,
+ Front_Inline = Front << 1,
+ Back = Front_Inline << 1,
+ Back_Inline = Back << 1,
+ DefaultLocations = Front | Back_Inline,
+ AllLocations = Front | Back | Front_Inline | Back_Inline
+ } m_location = Front;
+
+ Comment() = default;
+ Comment(const QQmlJS::Engine *engine, Location location, QList<SourceLocation> srcLocations)
+ : m_location(location), m_srcLocations(srcLocations) {
+ for (const auto& srcLoc : srcLocations) {
+ m_text += engine->code().mid(static_cast<int>(srcLoc.begin()),
+ static_cast<int>(srcLoc.end() - srcLoc.begin())) + "\n";
+ }
+
+ m_text.chop(1);
+ }
+
+ QList<SourceLocation> m_srcLocations;
+
+ bool isValid() const { return !m_srcLocations.isEmpty(); }
+ bool isMultiline() const { return m_text.contains("\n"); }
+ bool isSyntheticMultiline() const { return m_srcLocations.size() > 1; }
+
+ bool contains(const SourceLocation& location) const {
+ for (const SourceLocation& srcLoc : m_srcLocations) {
+ if (srcLoc.begin() == location.begin() && srcLoc.end() == location.end())
+ return true;
+ }
+
+ return false;
+ }
+
+ QString m_text;
+};
+
+class CommentAstVisitor : protected Visitor
+{
+public:
+ CommentAstVisitor(QQmlJS::Engine *engine, Node *rootNode);
+
+ void throwRecursionDepthError() override {}
+
+ const QHash<Node *, Comment> attachedComments() const { return m_attachedComments; }
+ const QHash<quint32, Comment> listComments() const { return m_listItemComments; }
+ const QHash<Node *, QVector<Comment>> orphanComments() const { return m_orphanComments; }
+
+ bool visit(UiScriptBinding *node) override;
+ bool visit(UiObjectBinding *node) override;
+
+ bool visit(UiArrayBinding *node) override;
+ void endVisit(UiArrayBinding *node) override;
+
+ bool visit(UiObjectDefinition *node) override;
+ void endVisit(UiObjectDefinition *) override;
+
+ bool visit(UiEnumDeclaration *node) override;
+ void endVisit(UiEnumDeclaration *node) override;
+
+ bool visit(UiEnumMemberList *node) override;
+
+ bool visit(StatementList *node) override;
+ void endVisit(StatementList *node) override;
+
+ bool visit(UiImport *node) override;
+ bool visit(UiPragma *node) override;
+ bool visit(UiPublicMember *node) override;
+ bool visit(FunctionDeclaration *node) override;
+private:
+ bool isCommentAttached(const SourceLocation& location) const;
+
+ QList<SourceLocation> findCommentsInLine(quint32 line, bool includePrevious = false) const;
+
+ Comment findComment(SourceLocation first, SourceLocation last,
+ int locations = Comment::DefaultLocations) const;
+
+ Comment findComment(Node *node, int locations = Comment::DefaultLocations) const;
+ QVector<Comment> findOrphanComments(Node *node) const;
+ void attachComment(Node *node, int locations = Comment::DefaultLocations);
+
+ QQmlJS::Engine *m_engine;
+ QHash<Node *, Comment> m_attachedComments;
+ QHash<quint32, Comment> m_listItemComments;
+ QHash<Node *, QVector<Comment>> m_orphanComments;
+};
+
+#endif // COMMENTASTVISITOR_H
diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp
new file mode 100644
index 0000000000..ff0674eded
--- /dev/null
+++ b/tools/qmlformat/dumpastvisitor.cpp
@@ -0,0 +1,1321 @@
+/****************************************************************************
+**
+** 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 "dumpastvisitor.h"
+
+#include <QtQml/private/qqmljslexer_p.h>
+
+DumpAstVisitor::DumpAstVisitor(Node *rootNode, CommentAstVisitor *comment): m_comment(comment)
+{
+ // Add all completely orphaned comments
+ m_result += getOrphanedComments(nullptr);
+
+ m_scope_properties.push(ScopeProperties {});
+
+ rootNode->accept(this);
+
+ // We need to get rid of one new-line so our output doesn't append an empty line
+ m_result.chop(1);
+
+ // Remove trailing whitespace
+ QStringList lines = m_result.split("\n");
+ for (QString& line : lines) {
+ while (line.endsWith(" "))
+ line.chop(1);
+ }
+
+ m_result = lines.join("\n");
+}
+
+bool DumpAstVisitor::preVisit(Node *el)
+{
+ UiObjectMember *m = el->uiObjectMemberCast();
+ if (m != 0)
+ Node::accept(m->annotations, this);
+ return true;
+}
+
+static QString parseUiQualifiedId(UiQualifiedId *id)
+{
+ QString name = id->name.toString();
+ for (auto *item = id->next; item != nullptr; item = item->next) {
+ name += "." + item->name;
+ }
+
+ return name;
+}
+
+static QString operatorToString(int op)
+{
+ switch (op)
+ {
+ case QSOperator::Add: return "+";
+ case QSOperator::And: return "&&";
+ case QSOperator::InplaceAnd: return "&=";
+ case QSOperator::Assign: return "=";
+ case QSOperator::BitAnd: return "&";
+ case QSOperator::BitOr: return "|";
+ case QSOperator::BitXor: return "^";
+ case QSOperator::InplaceSub: return "-=";
+ case QSOperator::Div: return "/";
+ case QSOperator::InplaceDiv: return "/=";
+ case QSOperator::Equal: return "==";
+ case QSOperator::Exp: return "**";
+ case QSOperator::InplaceExp: return "**=";
+ case QSOperator::Ge: return ">=";
+ case QSOperator::Gt: return ">";
+ case QSOperator::In: return "in";
+ case QSOperator::InplaceAdd: return "+=";
+ case QSOperator::InstanceOf: return "instanceof";
+ case QSOperator::Le: return "<=";
+ case QSOperator::LShift: return "<<";
+ case QSOperator::InplaceLeftShift: return "<<=";
+ case QSOperator::Lt: return "<";
+ case QSOperator::Mod: return "%";
+ case QSOperator::InplaceMod: return "%=";
+ case QSOperator::Mul: return "*";
+ case QSOperator::InplaceMul: return "*=";
+ case QSOperator::NotEqual: return "!=";
+ case QSOperator::Or: return "||";
+ case QSOperator::InplaceOr: return "|=";
+ case QSOperator::RShift: return ">>";
+ case QSOperator::InplaceRightShift: return ">>=";
+ case QSOperator::StrictEqual: return "===";
+ case QSOperator::StrictNotEqual: return "!==";
+ case QSOperator::Sub: return "-";
+ case QSOperator::URShift: return ">>>";
+ case QSOperator::InplaceURightShift: return ">>>=";
+ case QSOperator::InplaceXor: return "^=";
+ case QSOperator::As: return "as";
+ case QSOperator::Coalesce: return "??";
+ case QSOperator::Invalid:
+ default:
+ return "INVALID";
+ }
+}
+
+QString DumpAstVisitor::formatComment(const Comment &comment) const
+{
+ QString result;
+
+ bool useMultilineComment = comment.isMultiline() && !comment.isSyntheticMultiline();
+
+ if (useMultilineComment)
+ result += "/*";
+ else
+ result += "//";
+
+ result += comment.m_text;
+
+ if (comment.isSyntheticMultiline())
+ result = result.replace("\n","\n" + formatLine("//", false));
+
+ if (comment.m_location == Comment::Location::Back_Inline)
+ result.prepend(" ");
+
+ if (useMultilineComment)
+ result += "*/";
+
+ return result;
+}
+
+QString DumpAstVisitor::getComment(Node *node, Comment::Location location) const
+{
+ const auto& comments = m_comment->attachedComments();
+ if (!comments.contains(node))
+ return "";
+
+ auto comment = comments[node];
+
+ if (comment.m_location != location)
+ return "";
+
+ return formatComment(comment);
+}
+
+QString DumpAstVisitor::getListItemComment(SourceLocation srcLocation,
+ Comment::Location location) const {
+ const auto& comments = m_comment->listComments();
+
+ if (!comments.contains(srcLocation.begin()))
+ return "";
+
+ auto comment = comments[srcLocation.begin()];
+
+ if (comment.m_location != location)
+ return "";
+
+ return formatComment(comment);
+}
+
+QString DumpAstVisitor::getOrphanedComments(Node *node) const {
+ const auto& orphans = m_comment->orphanComments()[node];
+
+ if (orphans.size() == 0)
+ return "";
+
+ QString result = "";
+
+ for (const Comment& orphan : orphans) {
+ result += formatLine(formatComment(orphan));
+ }
+
+ result += "\n";
+
+ return result;
+}
+
+QString DumpAstVisitor::parseArgumentList(ArgumentList *list)
+{
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next)
+ result += parseExpression(item->expression) + (item->next != nullptr ? ", " : "");
+
+ return result;
+}
+
+QString DumpAstVisitor::parseUiParameterList(UiParameterList *list) {
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next)
+ result += parseUiQualifiedId(item->type) + " " + item->name + (item->next != nullptr ? ", " : "");
+
+ return result;
+}
+
+QString DumpAstVisitor::parsePatternElement(PatternElement *element, bool scope)
+{
+ switch (element->type)
+ {
+ case PatternElement::Literal:
+ return parseExpression(element->initializer);
+ case PatternElement::Binding: {
+ QString result = "";
+ QString expr = parseExpression(element->initializer);
+
+ if (scope) {
+ switch (element->scope) {
+ case VariableScope::NoScope:
+ break;
+ case VariableScope::Let:
+ result = "let ";
+ break;
+ case VariableScope::Const:
+ result = "const ";
+ break;
+ case VariableScope::Var:
+ result = "var ";
+ break;
+ }
+ }
+
+ result += element->bindingIdentifier.toString();
+
+ if (element->typeAnnotation != nullptr)
+ result += ": " + parseType(element->typeAnnotation->type);
+
+ if (!expr.isEmpty())
+ result += " = "+expr;
+
+ return result;
+ }
+ default:
+ m_error = true;
+ return "pe_unknown";
+ }
+}
+
+QString escapeString(QString string)
+{
+ // Handle escape sequences
+ string = string.replace("\r", "\\r").replace("\n", "\\n").replace("\t", "\\t")
+ .replace("\b","\\b").replace("\v", "\\v").replace("\f", "\\f");
+
+ // Escape backslash
+ string = string.replace("\\", "\\\\");
+
+ // Escape "
+ string = string.replace("\"", "\\\"");
+
+ return "\"" + string + "\"";
+}
+
+QString DumpAstVisitor::parsePatternElementList(PatternElementList *list)
+{
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next)
+ result += parsePatternElement(item->element) + (item->next != nullptr ? ", " : "");
+
+ return result;
+}
+
+QString DumpAstVisitor::parseFormalParameterList(FormalParameterList *list)
+{
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next)
+ result += parsePatternElement(item->element) + (item->next != nullptr ? ", " : "");
+
+ return result;
+}
+
+QString DumpAstVisitor::parsePatternProperty(PatternProperty *property)
+{
+ switch (property->type) {
+ case PatternElement::Getter:
+ return "get "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true);
+ case PatternElement::Setter:
+ return "set "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true);
+ default:
+ return escapeString(property->name->asString())+": "+parsePatternElement(property, false);
+ }
+}
+
+QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list)
+{
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next) {
+ result += formatLine(parsePatternProperty(item->property) + (item->next != nullptr ? "," : ""));
+ }
+
+ return result;
+}
+
+QString DumpAstVisitor::parseFunctionExpression(FunctionExpression *functExpr, bool omitFunction)
+{
+ m_indentLevel++;
+ QString result;
+
+ if (!functExpr->isArrowFunction) {
+ result += omitFunction ? "" : "function";
+
+ if (functExpr->isGenerator)
+ result += "*";
+
+ if (!functExpr->name.isEmpty())
+ result += (omitFunction ? "" : " ") + functExpr->name;
+
+ result += "("+parseFormalParameterList(functExpr->formals)+")";
+
+ if (functExpr->typeAnnotation != nullptr)
+ result += " : " + parseType(functExpr->typeAnnotation->type);
+
+ result += " {\n" + parseStatementList(functExpr->body);
+ } else {
+ result += "("+parseFormalParameterList(functExpr->formals)+")";
+
+ if (functExpr->typeAnnotation != nullptr)
+ result += " : " + parseType(functExpr->typeAnnotation->type);
+
+ result += " => {\n" + parseStatementList(functExpr->body);
+ }
+
+ m_indentLevel--;
+
+ result += formatLine("}", false);
+
+ return result;
+
+}
+
+QString DumpAstVisitor::parseType(Type *type) {
+ QString result = parseUiQualifiedId(type->typeId);
+
+ if (type->typeArguments != nullptr) {
+ TypeArgumentList *list = cast<TypeArgumentList *>(type->typeArguments);
+
+ result += "<";
+
+ for (auto *item = list; item != nullptr; item = item->next) {
+ result += parseType(item->typeId) + (item->next != nullptr ? ", " : "");
+ }
+
+ result += ">";
+ }
+
+ return result;
+}
+
+QString DumpAstVisitor::parseExpression(ExpressionNode *expression)
+{
+ if (expression == nullptr)
+ return "";
+
+ switch (expression->kind)
+ {
+ case Node::Kind_ArrayPattern:
+ return "["+parsePatternElementList(cast<ArrayPattern *>(expression)->elements)+"]";
+ case Node::Kind_IdentifierExpression:
+ return cast<IdentifierExpression*>(expression)->name.toString();
+ case Node::Kind_FieldMemberExpression: {
+ auto *fieldMemberExpr = cast<FieldMemberExpression *>(expression);
+ QString result = parseExpression(fieldMemberExpr->base);
+
+ // If we're operating on a numeric literal, always put it in braces
+ if (fieldMemberExpr->base->kind == Node::Kind_NumericLiteral)
+ result = "(" + result + ")";
+
+ result += "." + fieldMemberExpr->name.toString();
+
+ return result;
+ }
+ case Node::Kind_ArrayMemberExpression: {
+ auto *arrayMemberExpr = cast<ArrayMemberExpression *>(expression);
+ return parseExpression(arrayMemberExpr->base)
+ + "[" + parseExpression(arrayMemberExpr->expression) + "]";
+ }
+ case Node::Kind_NestedExpression:
+ return "("+parseExpression(cast<NestedExpression *>(expression)->expression)+")";
+ case Node::Kind_TrueLiteral:
+ return "true";
+ case Node::Kind_FalseLiteral:
+ return "false";
+ case Node::Kind_FunctionExpression:
+ {
+ auto *functExpr = cast<FunctionExpression *>(expression);
+ return parseFunctionExpression(functExpr);
+ }
+ case Node::Kind_NullExpression:
+ return "null";
+ case Node::Kind_ThisExpression:
+ return "this";
+ case Node::Kind_PostIncrementExpression:
+ return parseExpression(cast<PostIncrementExpression *>(expression)->base)+"++";
+ case Node::Kind_PreIncrementExpression:
+ return "++"+parseExpression(cast<PreIncrementExpression *>(expression)->expression);
+ case Node::Kind_PostDecrementExpression:
+ return parseExpression(cast<PostDecrementExpression *>(expression)->base)+"--";
+ case Node::Kind_PreDecrementExpression:
+ return "--"+parseExpression(cast<PreDecrementExpression *>(expression)->expression);
+ case Node::Kind_NumericLiteral:
+ return QString::number(cast<NumericLiteral *>(expression)->value);
+ case Node::Kind_StringLiteral:
+ return escapeString(cast<StringLiteral *>(expression)->value.toString());
+ case Node::Kind_BinaryExpression: {
+ auto *binExpr = expression->binaryExpressionCast();
+ return parseExpression(binExpr->left) + " " + operatorToString(binExpr->op)
+ + " " + parseExpression(binExpr->right);
+ }
+ case Node::Kind_CallExpression: {
+ auto *callExpr = cast<CallExpression *>(expression);
+
+ return parseExpression(callExpr->base) + "(" + parseArgumentList(callExpr->arguments) + ")";
+ }
+ case Node::Kind_NewExpression:
+ return "new "+parseExpression(cast<NewExpression *>(expression)->expression);
+ case Node::Kind_NewMemberExpression: {
+ auto *newMemberExpression = cast<NewMemberExpression *>(expression);
+ return "new "+parseExpression(newMemberExpression->base)
+ + "(" +parseArgumentList(newMemberExpression->arguments)+")";
+ }
+ case Node::Kind_DeleteExpression:
+ return "delete " + parseExpression(cast<DeleteExpression *>(expression)->expression);
+ case Node::Kind_VoidExpression:
+ return "void " + parseExpression(cast<VoidExpression *>(expression)->expression);
+ case Node::Kind_TypeOfExpression:
+ return "typeof " + parseExpression(cast<TypeOfExpression *>(expression)->expression);
+ case Node::Kind_UnaryPlusExpression:
+ return "+" + parseExpression(cast<UnaryPlusExpression *>(expression)->expression);
+ case Node::Kind_UnaryMinusExpression:
+ return "-" + parseExpression(cast<UnaryMinusExpression *>(expression)->expression);
+ case Node::Kind_NotExpression:
+ return "!" + parseExpression(cast<NotExpression *>(expression)->expression);
+ case Node::Kind_TildeExpression:
+ return "~" + parseExpression(cast<TildeExpression *>(expression)->expression);
+ case Node::Kind_ConditionalExpression: {
+ auto *condExpr = cast<ConditionalExpression *>(expression);
+
+ QString result = "";
+
+ result += parseExpression(condExpr->expression) + " ? ";
+ result += parseExpression(condExpr->ok) + " : ";
+ result += parseExpression(condExpr->ko);
+
+ return result;
+ }
+ case Node::Kind_YieldExpression: {
+ auto *yieldExpr = cast<YieldExpression*>(expression);
+
+ QString result = "yield";
+
+ if (yieldExpr->isYieldStar)
+ result += "*";
+
+ if (yieldExpr->expression)
+ result += " " + parseExpression(yieldExpr->expression);
+
+ return result;
+ }
+ case Node::Kind_ObjectPattern: {
+ auto *objectPattern = cast<ObjectPattern*>(expression);
+ QString result = "{\n";
+
+ m_indentLevel++;
+ result += parsePatternPropertyList(objectPattern->properties);
+ m_indentLevel--;
+
+ result += formatLine("}", false);
+
+ return result;
+ }
+ case Node::Kind_Expression: {
+ auto* expr = cast<Expression*>(expression);
+ return parseExpression(expr->left)+", "+parseExpression(expr->right);
+ }
+ case Node::Kind_Type: {
+ auto* type = reinterpret_cast<Type*>(expression);
+ return parseType(type);
+ }
+ case Node::Kind_RegExpLiteral: {
+ auto* regexpLiteral = cast<RegExpLiteral*>(expression);
+ QString result = "/"+regexpLiteral->pattern+"/";
+
+ if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Unicode)
+ result += "u";
+ if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Global)
+ result += "g";
+ if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Multiline)
+ result += "m";
+ if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Sticky)
+ result += "y";
+ if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
+ result += "i";
+
+ return result;
+ }
+ default:
+ m_error = true;
+ return "unknown_expression_"+QString::number(expression->kind);
+ }
+}
+
+QString DumpAstVisitor::parseVariableDeclarationList(VariableDeclarationList *list)
+{
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next) {
+ result += parsePatternElement(item->declaration, (item == list))
+ + (item->next != nullptr ? ", " : "");
+ }
+
+ return result;
+}
+
+QString DumpAstVisitor::parseCaseBlock(CaseBlock *block)
+{
+ QString result = "{\n";
+
+ for (auto *item = block->clauses; item != nullptr; item = item->next) {
+ result += formatLine("case "+parseExpression(item->clause->expression)+":");
+ m_indentLevel++;
+ result += parseStatementList(item->clause->statements);
+ m_indentLevel--;
+ }
+
+ if (block->defaultClause) {
+ result += formatLine("default:");
+ m_indentLevel++;
+ result += parseStatementList(block->defaultClause->statements);
+ m_indentLevel--;
+ }
+
+ result += formatLine("}", false);
+
+ return result;
+}
+
+QString DumpAstVisitor::parseExportSpecifier(ExportSpecifier *specifier)
+{
+ QString result = specifier->identifier.toString();
+
+ if (!specifier->exportedIdentifier.isEmpty())
+ result += " as " + specifier->exportedIdentifier;
+
+ return result;
+}
+
+QString DumpAstVisitor::parseExportsList(ExportsList *list)
+{
+ QString result = "";
+
+ for (auto *item = list; item != nullptr; item = item->next) {
+ result += formatLine(parseExportSpecifier(item->exportSpecifier)
+ + (item->next != nullptr ? "," : ""));
+ }
+
+ return result;
+}
+
+QString DumpAstVisitor::parseBlock(Block *block, bool hasNext, bool allowBraceless)
+{
+ bool hasOneLine = (block->statements == nullptr || block->statements->next == nullptr) && allowBraceless;
+
+ QString result = hasOneLine ? "\n" : "{\n";
+ m_indentLevel++;
+ result += parseStatementList(block->statements);
+ m_indentLevel--;
+
+ if (hasNext)
+ result += formatLine(hasOneLine ? "" : "} ", false);
+
+ if (!hasNext && !hasOneLine)
+ result += formatLine("}", false);
+
+ m_blockNeededBraces |= (block->statements && block->statements->next != nullptr);
+
+ return result;
+}
+
+QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext,
+ bool blockAllowBraceless)
+{
+ if (statement == nullptr)
+ return "";
+
+ switch (statement->kind)
+ {
+ case Node::Kind_EmptyStatement:
+ return "";
+ case Node::Kind_ExpressionStatement:
+ return parseExpression(cast<ExpressionStatement *>(statement)->expression);
+ case Node::Kind_VariableStatement:
+ return parseVariableDeclarationList(cast<VariableStatement *>(statement)->declarations);
+ case Node::Kind_ReturnStatement:
+ return "return "+parseExpression(cast<ReturnStatement *>(statement)->expression);
+ case Node::Kind_ContinueStatement:
+ return "continue";
+ case Node::Kind_BreakStatement:
+ return "break";
+ case Node::Kind_SwitchStatement: {
+ auto *switchStatement = cast<SwitchStatement *>(statement);
+
+ QString result = "switch ("+parseExpression(switchStatement->expression)+") ";
+
+ result += parseCaseBlock(switchStatement->block);
+
+ return result;
+ }
+ case Node::Kind_IfStatement: {
+ auto *ifStatement = cast<IfStatement *>(statement);
+
+ m_blockNeededBraces = !blockAllowBraceless;
+
+ QString ifFalse = parseStatement(ifStatement->ko, false, true);
+ QString ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), true);
+
+ bool ifTrueBlock = ifStatement->ok->kind == Node::Kind_Block;
+ bool ifFalseBlock = ifStatement->ko
+ ? (ifStatement->ko->kind == Node::Kind_Block || ifStatement->ko->kind == Node::Kind_IfStatement)
+ : false;
+
+ if (m_blockNeededBraces) {
+ ifFalse = parseStatement(ifStatement->ko, false, false);
+ ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), false);
+ }
+
+ if (ifStatement->ok->kind != Node::Kind_Block)
+ ifTrue += ";";
+
+ if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement)
+ ifFalse += ";";
+
+ QString result = "if (" + parseExpression(ifStatement->expression) + ")";
+
+ if (m_blockNeededBraces) {
+ if (ifStatement->ok->kind != Node::Kind_Block) {
+ QString result = "{\n";
+ m_indentLevel++;
+ result += formatLine(ifTrue);
+ m_indentLevel--;
+ result += formatLine("} ", false);
+ ifTrue = result;
+ ifTrueBlock = true;
+ }
+
+ if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) {
+ QString result = "{\n";
+ m_indentLevel++;
+ result += formatLine(ifFalse);
+ m_indentLevel--;
+ result += formatLine("} ", false);
+ ifFalse = result;
+ ifFalseBlock = true;
+ }
+ }
+
+ if (ifTrueBlock) {
+ result += " " + ifTrue;
+ } else {
+ result += "\n";
+ m_indentLevel++;
+ result += formatLine(ifTrue);
+ m_indentLevel--;
+ }
+
+ if (!ifFalse.isEmpty())
+ {
+ if (ifTrueBlock)
+ result += "else";
+ else
+ result += formatLine("else", false);
+
+ if (ifFalseBlock) {
+ result += " " + ifFalse;
+ } else {
+ result += "\n";
+ m_indentLevel++;
+ result += formatLine(ifFalse, false);
+ m_indentLevel--;
+ }
+ }
+
+ return result;
+ }
+ case Node::Kind_ForStatement: {
+ auto *forStatement = cast<ForStatement *>(statement);
+
+ QString expr = parseExpression(forStatement->expression);
+ QString result = "for (";
+
+ result += parseVariableDeclarationList(forStatement->declarations);
+
+ result += "; ";
+
+ result += parseExpression(forStatement->condition) + "; ";
+ result += parseExpression(forStatement->expression)+")";
+
+ const QString statement = parseStatement(forStatement->statement);
+
+ if (!statement.isEmpty())
+ result += " "+statement;
+ else
+ result += ";";
+
+ return result;
+ }
+ case Node::Kind_ForEachStatement: {
+ auto *forEachStatement = cast<ForEachStatement *>(statement);
+
+ QString result = "for (";
+
+ PatternElement *patternElement = cast<PatternElement *>(forEachStatement->lhs);
+
+ if (patternElement != nullptr)
+ result += parsePatternElement(patternElement);
+ else
+ result += parseExpression(forEachStatement->lhs->expressionCast());
+
+ switch (forEachStatement->type)
+ {
+ case ForEachType::In:
+ result += " in ";
+ break;
+ case ForEachType::Of:
+ result += " of ";
+ break;
+ }
+
+ result += parseExpression(forEachStatement->expression) + ")";
+
+ const QString statement = parseStatement(forEachStatement->statement);
+
+ if (!statement.isEmpty())
+ result += " "+statement;
+ else
+ result += ";";
+
+ return result;
+ }
+ case Node::Kind_WhileStatement: {
+ auto *whileStatement = cast<WhileStatement *>(statement);
+
+ m_blockNeededBraces = false;
+
+ auto statement = parseStatement(whileStatement->statement, false, true);
+
+ QString result = "while ("+parseExpression(whileStatement->expression) + ")";
+
+ if (!statement.isEmpty())
+ result += (m_blockNeededBraces ? " " : "") + statement;
+ else
+ result += ";";
+
+ return result;
+ }
+ case Node::Kind_DoWhileStatement: {
+ auto *doWhileStatement = cast<DoWhileStatement *>(statement);
+ return "do " + parseBlock(cast<Block *>(doWhileStatement->statement), true, false)
+ + "while (" + parseExpression(doWhileStatement->expression) + ")";
+ }
+ case Node::Kind_TryStatement: {
+ auto *tryStatement = cast<TryStatement *>(statement);
+
+ Catch *catchExpr = tryStatement->catchExpression;
+ Finally *finallyExpr = tryStatement->finallyExpression;
+
+ QString result;
+
+ result += "try " + parseBlock(cast<Block *>(tryStatement->statement), true, false);
+
+ result += "catch (" + parsePatternElement(catchExpr->patternElement, false) + ") "
+ + parseBlock(cast<Block *>(catchExpr->statement), finallyExpr, false);
+
+ if (finallyExpr) {
+ result += "finally " + parseBlock(cast<Block *>(tryStatement->statement), false, false);
+ }
+
+ return result;
+ }
+ case Node::Kind_Block: {
+ return parseBlock(cast<Block *>(statement), blockHasNext, blockAllowBraceless);
+ }
+ case Node::Kind_ThrowStatement:
+ return "throw "+parseExpression(cast<ThrowStatement *>(statement)->expression);
+ case Node::Kind_LabelledStatement: {
+ auto *labelledStatement = cast<LabelledStatement *>(statement);
+ QString result = labelledStatement->label+":\n";
+ result += formatLine(parseStatement(labelledStatement->statement), false);
+
+ return result;
+ }
+ case Node::Kind_WithStatement: {
+ auto *withStatement = cast<WithStatement *>(statement);
+ return "with (" + parseExpression(withStatement->expression) + ") "
+ + parseStatement(withStatement->statement);
+ }
+ case Node::Kind_DebuggerStatement: {
+ return "debugger";
+ }
+ case Node::Kind_ExportDeclaration:
+ m_error = true;
+ return "export_decl_unsupported";
+ case Node::Kind_ImportDeclaration:
+ m_error = true;
+ return "import_decl_unsupported";
+ default:
+ m_error = true;
+ return "unknown_statement_"+QString::number(statement->kind);
+ }
+}
+
+bool needsSemicolon(int kind)
+{
+ switch (kind)
+ {
+ case Node::Kind_ForStatement:
+ case Node::Kind_ForEachStatement:
+ case Node::Kind_IfStatement:
+ case Node::Kind_SwitchStatement:
+ case Node::Kind_WhileStatement:
+ case Node::Kind_DoWhileStatement:
+ case Node::Kind_TryStatement:
+ case Node::Kind_WithStatement:
+ return false;
+ default:
+ return true;
+ }
+}
+
+QString DumpAstVisitor::parseStatementList(StatementList *list)
+{
+ QString result = "";
+
+ result += getOrphanedComments(list);
+
+ for (auto *item = list; item != nullptr; item = item->next) {
+ QString statement = parseStatement(item->statement->statementCast(), false, true);
+ if (statement.isEmpty())
+ continue;
+
+ QString commentFront = getComment(item->statement, Comment::Location::Front);
+ QString commentBackInline = getComment(item->statement, Comment::Location::Back_Inline);
+
+ if (!commentFront.isEmpty())
+ result += formatLine(commentFront);
+
+ result += formatLine(statement + (needsSemicolon(item->statement->kind) ? ";" : "")
+ + commentBackInline);
+ }
+
+ return result;
+}
+
+bool DumpAstVisitor::visit(UiPublicMember *node) {
+
+ QString commentFront = getComment(node, Comment::Location::Front);
+ QString commentBackInline = getComment(node, Comment::Location::Back_Inline);
+
+ switch (node->type)
+ {
+ case UiPublicMember::Signal:
+ if (scope().m_firstSignal) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ scope().m_firstSignal = false;
+ }
+
+ addLine(commentFront);
+ addLine("signal "+node->name.toString()+"("+parseUiParameterList(node->parameters) + ")"
+ + commentBackInline);
+ break;
+ case UiPublicMember::Property: {
+ if (scope().m_firstProperty) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ scope().m_firstProperty = false;
+ }
+
+ const bool is_required = node->requiredToken.isValid();
+ const bool is_default = node->defaultToken.isValid();
+ const bool is_readonly = node->readonlyToken.isValid();
+ const bool has_type_modifier = node->typeModifierToken.isValid();
+
+ QString prefix = "";
+ QString statement = parseStatement(node->statement);
+
+ if (!statement.isEmpty())
+ statement.prepend(": ");
+
+ if (is_required)
+ prefix += "required ";
+
+ if (is_default)
+ prefix += "default ";
+
+ if (is_readonly)
+ prefix += "readonly ";
+
+ QString member_type = parseUiQualifiedId(node->memberType);
+
+ if (has_type_modifier)
+ member_type = node->typeModifier + "<" + member_type + ">";
+
+ addLine(commentFront);
+ if (is_readonly && statement.isEmpty()
+ && scope().m_bindings.contains(node->name.toString())) {
+ m_result += formatLine(prefix + "property " + member_type + " ", false);
+
+ scope().m_pendingBinding = true;
+ } else {
+ addLine(prefix + "property " + member_type + " "
+ + node->name+statement + commentBackInline);
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+QString DumpAstVisitor::generateIndent() const {
+ constexpr int IDENT_WIDTH = 4;
+
+ QString indent = "";
+ for (int i = 0; i < IDENT_WIDTH*m_indentLevel; i++)
+ indent += " ";
+
+ return indent;
+}
+
+QString DumpAstVisitor::formatLine(QString line, bool newline) const {
+ QString result = generateIndent() + line;
+ if (newline)
+ result += "\n";
+
+ return result;
+}
+
+void DumpAstVisitor::addNewLine(bool always) {
+ if (!always && m_result.endsWith("\n\n"))
+ return;
+
+ m_result += "\n";
+}
+
+void DumpAstVisitor::addLine(QString line) {
+ // addLine does not support empty lines, use addNewLine(true) for that
+ if (line.isEmpty())
+ return;
+
+ m_result += formatLine(line);
+}
+
+QHash<QString, UiObjectMember*> findBindings(UiObjectMemberList *list) {
+ QHash<QString, UiObjectMember*> bindings;
+
+ // This relies on RestructureASTVisitor having run beforehand
+
+ for (auto *item = list; item != nullptr; item = item->next) {
+ switch (item->member->kind) {
+ case Node::Kind_UiPublicMember: {
+ UiPublicMember *member = cast<UiPublicMember *>(item->member);
+
+ if (member->type != UiPublicMember::Property)
+ continue;
+
+ bindings[member->name.toString()] = nullptr;
+
+ break;
+ }
+ case Node::Kind_UiObjectBinding: {
+ UiObjectBinding *binding = cast<UiObjectBinding *>(item->member);
+
+ const QString name = parseUiQualifiedId(binding->qualifiedId);
+
+ if (bindings.contains(name))
+ bindings[name] = binding;
+
+ break;
+ }
+ case Node::Kind_UiArrayBinding: {
+ UiArrayBinding *binding = cast<UiArrayBinding *>(item->member);
+
+ const QString name = parseUiQualifiedId(binding->qualifiedId);
+
+ if (bindings.contains(name))
+ bindings[name] = binding;
+
+ break;
+ }
+ case Node::Kind_UiScriptBinding:
+ // We can ignore UiScriptBindings since those are actually properly attached to the property
+ break;
+ }
+ }
+
+ return bindings;
+}
+
+bool DumpAstVisitor::visit(UiObjectDefinition *node) {
+ if (scope().m_firstObject) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ scope().m_firstObject = false;
+ }
+
+ addLine(getComment(node, Comment::Location::Front));
+ addLine(getComment(node, Comment::Location::Front_Inline));
+ addLine(parseUiQualifiedId(node->qualifiedTypeNameId) + " {");
+
+ m_indentLevel++;
+
+ ScopeProperties props;
+ props.m_bindings = findBindings(node->initializer->members);
+ m_scope_properties.push(props);
+
+ m_result += getOrphanedComments(node);
+
+ return true;
+}
+
+void DumpAstVisitor::endVisit(UiObjectDefinition *node) {
+ m_indentLevel--;
+
+ m_scope_properties.pop();
+
+ bool need_comma = scope().m_inArrayBinding && scope().m_lastInArrayBinding != node;
+
+ addLine(need_comma ? "}," : "}");
+ addLine(getComment(node, Comment::Location::Back));
+ if (!scope().m_inArrayBinding)
+ addNewLine();
+}
+
+bool DumpAstVisitor::visit(UiEnumDeclaration *node) {
+
+ addNewLine();
+
+ addLine(getComment(node, Comment::Location::Front));
+ addLine("enum " + node->name + " {");
+ m_indentLevel++;
+ m_result += getOrphanedComments(node);
+
+ return true;
+}
+
+void DumpAstVisitor::endVisit(UiEnumDeclaration *) {
+ m_indentLevel--;
+ addLine("}");
+
+ addNewLine();
+}
+
+bool DumpAstVisitor::visit(UiEnumMemberList *node) {
+ for (auto *members = node; members != nullptr; members = members->next) {
+
+ addLine(getListItemComment(members->memberToken, Comment::Location::Front));
+
+ QString line = members->member.toString();
+
+ if (members->valueToken.isValid())
+ line += " = "+QString::number(members->value);
+
+ if (members->next != nullptr)
+ line += ",";
+
+ line += getListItemComment(members->memberToken, Comment::Location::Back_Inline);
+
+ addLine(line);
+ }
+
+ return true;
+}
+
+bool DumpAstVisitor::visit(UiScriptBinding *node) {
+ if (scope().m_firstBinding) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ if (parseUiQualifiedId(node->qualifiedId) != "id")
+ scope().m_firstBinding = false;
+ }
+
+ addLine(getComment(node, Comment::Location::Front));
+
+ QString statement = parseStatement(node->statement);
+
+ QString result = parseUiQualifiedId(node->qualifiedId) + ":";
+
+ if (!statement.isEmpty())
+ result += " "+statement;
+ else
+ result += ";";
+
+ result += getComment(node, Comment::Location::Back_Inline);
+
+ addLine(result);
+
+ return true;
+}
+
+bool DumpAstVisitor::visit(UiArrayBinding *node) {
+ if (!scope().m_pendingBinding && scope().m_firstBinding) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ scope().m_firstBinding = false;
+ }
+
+ if (scope().m_pendingBinding) {
+ m_result += parseUiQualifiedId(node->qualifiedId)+ ": [\n";
+ scope().m_pendingBinding = false;
+ } else {
+ addLine(getComment(node, Comment::Location::Front));
+ addLine(parseUiQualifiedId(node->qualifiedId)+ ": [");
+ }
+
+ m_indentLevel++;
+
+ ScopeProperties props;
+ props.m_inArrayBinding = true;
+
+ for (auto *item = node->members; item != nullptr; item = item->next) {
+ if (item->next == nullptr)
+ props.m_lastInArrayBinding = item->member;
+ }
+
+ m_scope_properties.push(props);
+
+ m_result += getOrphanedComments(node);
+
+ return true;
+}
+
+void DumpAstVisitor::endVisit(UiArrayBinding *) {
+ m_indentLevel--;
+ m_scope_properties.pop();
+ addLine("]");
+}
+
+bool DumpAstVisitor::visit(FunctionDeclaration *node) {
+
+ addNewLine();
+
+ addLine(getComment(node, Comment::Location::Front));
+
+ QString head = "function";
+
+ if (node->isGenerator)
+ head += "*";
+
+ head += " "+node->name+"("+parseFormalParameterList(node->formals)+")";
+
+ if (node->typeAnnotation != nullptr)
+ head += " : " + parseType(node->typeAnnotation->type);
+
+ head += " {";
+
+ addLine(head);
+ m_indentLevel++;
+ m_result += parseStatementList(node->body);
+ m_indentLevel--;
+ addLine("}");
+
+ addNewLine();
+
+ return true;
+}
+
+bool DumpAstVisitor::visit(UiObjectBinding *node) {
+ if (!scope().m_pendingBinding && scope().m_firstObject) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ scope().m_firstObject = false;
+ }
+
+ QString name = parseUiQualifiedId(node->qualifiedTypeNameId);
+
+ QString result = name;
+
+ ScopeProperties props;
+ props.m_bindings = findBindings(node->initializer->members);
+ m_scope_properties.push(props);
+
+ if (node->hasOnToken)
+ result += " on "+parseUiQualifiedId(node->qualifiedId);
+ else
+ result.prepend(parseUiQualifiedId(node->qualifiedId) + ": ");
+
+ if (scope().m_pendingBinding) {
+ m_result += result + " {\n";
+
+ scope().m_pendingBinding = false;
+ } else {
+ addNewLine();
+ addLine(getComment(node, Comment::Location::Front));
+ addLine(getComment(node, Comment::Location::Front_Inline));
+ addLine(result + " {");
+ }
+
+ m_indentLevel++;
+
+ return true;
+}
+
+void DumpAstVisitor::endVisit(UiObjectBinding *node) {
+ m_indentLevel--;
+ m_scope_properties.pop();
+
+ addLine("}");
+ addLine(getComment(node, Comment::Location::Back));
+
+ addNewLine();
+}
+
+bool DumpAstVisitor::visit(UiImport *node) {
+ scope().m_firstOfAll = false;
+
+ addLine(getComment(node, Comment::Location::Front));
+
+ QString result = "import ";
+
+ if (!node->fileName.isEmpty())
+ result += escapeString(node->fileName.toString());
+ else
+ result += parseUiQualifiedId(node->importUri);
+
+ if (node->version) {
+ result += " " + QString::number(node->version->majorVersion) + "."
+ + QString::number(node->version->minorVersion);
+ }
+
+ if (node->asToken.isValid()) {
+ result +=" as " + node->importId;
+ }
+
+ result += getComment(node, Comment::Location::Back_Inline);
+
+ addLine(result);
+
+ return true;
+}
+
+bool DumpAstVisitor::visit(UiPragma *node) {
+ scope().m_firstOfAll = false;
+
+ addLine(getComment(node, Comment::Location::Front));
+ QString result = "pragma "+ node->name;
+ result += getComment(node, Comment::Location::Back_Inline);
+
+ addLine(result);
+
+ return true;
+}
+
+bool DumpAstVisitor::visit(UiAnnotation *node)
+{
+ if (scope().m_firstObject) {
+ if (scope().m_firstOfAll)
+ scope().m_firstOfAll = false;
+ else
+ addNewLine();
+
+ scope().m_firstObject = false;
+ }
+
+ addLine(getComment(node, Comment::Location::Front));
+ addLine(QLatin1String("@") + parseUiQualifiedId(node->qualifiedTypeNameId) + " {");
+
+ m_indentLevel++;
+
+ ScopeProperties props;
+ props.m_bindings = findBindings(node->initializer->members);
+ m_scope_properties.push(props);
+
+ m_result += getOrphanedComments(node);
+
+ return true;
+}
+
+void DumpAstVisitor::endVisit(UiAnnotation *node) {
+ m_indentLevel--;
+
+ m_scope_properties.pop();
+
+ addLine("}");
+ addLine(getComment(node, Comment::Location::Back));
+}
diff --git a/tools/qmlformat/dumpastvisitor.h b/tools/qmlformat/dumpastvisitor.h
new file mode 100644
index 0000000000..faf067d400
--- /dev/null
+++ b/tools/qmlformat/dumpastvisitor.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** 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 DUMPAST_H
+#define DUMPAST_H
+
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+
+#include <QHash>
+#include <QStack>
+
+#include "commentastvisitor.h"
+
+using namespace QQmlJS::AST;
+using namespace QQmlJS;
+
+class DumpAstVisitor : protected Visitor
+{
+public:
+ DumpAstVisitor(Node *rootNode, CommentAstVisitor *comment);
+
+ QString toString() const { return m_result; }
+
+ bool preVisit(Node *) override;
+
+ bool visit(UiScriptBinding *node) override;
+
+ bool visit(UiArrayBinding *node) override;
+ void endVisit(UiArrayBinding *node) override;
+
+ bool visit(UiObjectBinding *node) override;
+ void endVisit(UiObjectBinding *node) override;
+
+ bool visit(FunctionDeclaration *node) override;
+
+ bool visit(UiObjectDefinition *node) override;
+ void endVisit(UiObjectDefinition *node) override;
+
+ bool visit(UiEnumDeclaration *node) override;
+ void endVisit(UiEnumDeclaration *node) override;
+
+ bool visit(UiEnumMemberList *node) override;
+ bool visit(UiPublicMember *node) override;
+ bool visit(UiImport *node) override;
+ bool visit(UiPragma *node) override;
+
+ bool visit(UiAnnotation *node) override;
+ void endVisit(UiAnnotation *node) override;
+
+ void throwRecursionDepthError() override {}
+
+ bool error() const { return m_error; }
+private:
+ struct ScopeProperties {
+ bool m_firstOfAll = true;
+ bool m_firstSignal = true;
+ bool m_firstProperty = true;
+ bool m_firstBinding = true;
+ bool m_firstObject = true;
+ bool m_inArrayBinding = false;
+ bool m_pendingBinding = false;
+
+ UiObjectMember* m_lastInArrayBinding = nullptr;
+ QHash<QString, UiObjectMember*> m_bindings;
+ };
+
+ QString generateIndent() const;
+ QString formatLine(QString line, bool newline = true) const;
+
+ QString formatComment(const Comment &comment) const;
+
+ QString getComment(Node *node, Comment::Location location) const;
+ QString getListItemComment(SourceLocation srcLocation, Comment::Location location) const;
+
+ void addNewLine(bool always = false);
+ void addLine(QString line);
+
+ QString getOrphanedComments(Node *node) const;
+
+ QString parseStatement(Statement *statement, bool blockHasNext = false,
+ bool blockAllowBraceless = false);
+ QString parseStatementList(StatementList *list);
+
+ QString parseExpression(ExpressionNode *expression);
+
+ QString parsePatternElement(PatternElement *element, bool scope = true);
+ QString parsePatternElementList(PatternElementList *element);
+
+ QString parsePatternProperty(PatternProperty *property);
+ QString parsePatternPropertyList(PatternPropertyList *list);
+
+ QString parseArgumentList(ArgumentList *list);
+
+ QString parseUiParameterList(UiParameterList *list);
+
+ QString parseVariableDeclarationList(VariableDeclarationList *list);
+
+ QString parseCaseBlock(CaseBlock *block);
+ QString parseBlock(Block *block, bool hasNext, bool allowBraceless);
+
+ QString parseExportsList(ExportsList *list);
+ QString parseExportSpecifier(ExportSpecifier *specifier);
+
+ QString parseFormalParameterList(FormalParameterList *list);
+
+ QString parseType(Type *type);
+
+ QString parseFunctionExpression(FunctionExpression *expression, bool omitFunction = false);
+
+ ScopeProperties& scope() { return m_scope_properties.top(); }
+
+ int m_indentLevel = 0;
+
+ bool m_error = false;
+ bool m_blockNeededBraces = false;
+
+ QStack<ScopeProperties> m_scope_properties;
+
+ QString m_result = "";
+ CommentAstVisitor *m_comment;
+};
+
+#endif // DUMPAST_H
diff --git a/tools/qmlformat/main.cpp b/tools/qmlformat/main.cpp
new file mode 100644
index 0000000000..3e71110cf9
--- /dev/null
+++ b/tools/qmlformat/main.cpp
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** 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 <QCoreApplication>
+#include <QFile>
+
+#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>
+
+#if QT_CONFIG(commandlineparser)
+#include <QCommandLineParser>
+#endif
+
+#include "commentastvisitor.h"
+#include "dumpastvisitor.h"
+#include "restructureastvisitor.h"
+
+bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImports, bool force, const QString& newline)
+{
+ QFile file(filename);
+
+ if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ qWarning().noquote() << "Failed to open" << filename << "for reading.";
+ return false;
+ }
+
+ QString code = QString::fromUtf8(file.readAll());
+ file.close();
+
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ lexer.setCode(code, 1, true);
+ QQmlJS::Parser parser(&engine);
+
+ bool success = parser.parse();
+
+ if (!success) {
+ const auto diagnosticMessages = parser.diagnosticMessages();
+ for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
+ .arg(filename).arg(m.loc.startLine).arg(m.message);
+ }
+
+ qWarning().noquote() << "Failed to parse" << filename;
+ return false;
+ }
+
+ // Try to attach comments to AST nodes
+ CommentAstVisitor comment(&engine, parser.rootNode());
+
+ if (verbose)
+ qWarning().noquote() << comment.attachedComments().size() << "comment(s) attached.";
+
+ if (verbose) {
+ int orphaned = 0;
+
+ for (const auto& orphanList : comment.orphanComments().values())
+ orphaned += orphanList.size();
+
+ qWarning().noquote() << orphaned << "comments are orphans.";
+ }
+
+ if (verbose && sortImports)
+ qWarning().noquote() << "Sorting imports";
+
+ // Do the actual restructuring
+ RestructureAstVisitor restructure(parser.rootNode(), sortImports);
+
+ // Turn AST back into source code
+ if (verbose)
+ qWarning().noquote() << "Dumping" << filename;
+
+ DumpAstVisitor dump(parser.rootNode(), &comment);
+
+ QString dumpCode = dump.toString();
+
+ lexer.setCode(dumpCode, 1, true);
+
+ bool dumpSuccess = parser.parse();
+
+ if (!dumpSuccess) {
+ if (verbose) {
+ const auto diagnosticMessages = parser.diagnosticMessages();
+ for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ qWarning().noquote() << QString::fromLatin1("<formatted>:%2 : %3")
+ .arg(m.loc.startLine).arg(m.message);
+ }
+ }
+
+ qWarning().noquote() << "Failed to parse formatted code.";
+ }
+
+ if (dump.error() || !dumpSuccess) {
+ if (force) {
+ qWarning().noquote() << "An error has occurred. The output may not be reliable.";
+ } else {
+ qWarning().noquote() << "An error has occurred. Aborting.";
+ return false;
+ }
+ }
+
+
+ const bool native = newline == "native";
+
+ if (!native) {
+ if (newline == "macos") {
+ dumpCode = dumpCode.replace("\n","\r");
+ } else if (newline == "windows") {
+ dumpCode = dumpCode.replace("\n", "\r\n");
+ } else if (newline == "unix") {
+ // Nothing needs to be done for unix line-endings
+ } else {
+ qWarning().noquote() << "Unknown line ending type" << newline;
+ return false;
+ }
+ }
+
+ if (inplace) {
+ if (verbose)
+ qWarning().noquote() << "Writing to file" << filename;
+
+ if (!file.open(native ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::WriteOnly))
+ {
+ qWarning().noquote() << "Failed to open" << filename << "for writing";
+ return false;
+ }
+
+ file.write(dumpCode.toUtf8());
+ file.close();
+ } else {
+ QFile out;
+ out.open(stdout, QIODevice::WriteOnly);
+ out.write(dumpCode.toUtf8());
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName("qmlformat");
+ QCoreApplication::setApplicationVersion("1.0");
+
+ bool success = true;
+#if QT_CONFIG(commandlineparser)
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Formats QML files according to the QML Coding Conventions.");
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.addOption(QCommandLineOption({"V", "verbose"},
+ QStringLiteral("Verbose mode. Outputs more detailed information.")));
+
+ parser.addOption(QCommandLineOption({"n", "no-sort"},
+ QStringLiteral("Do not sort imports.")));
+
+ parser.addOption(QCommandLineOption({"i", "inplace"},
+ QStringLiteral("Edit file in-place instead of outputting to stdout.")));
+
+ parser.addOption(QCommandLineOption({"f", "force"},
+ QStringLiteral("Continue even if an error has occurred.")));
+
+ parser.addOption(QCommandLineOption({"l", "newline"},
+ QStringLiteral("Override the new line format to use (native macos unix windows)."),
+ "newline", "native"));
+
+ parser.addPositionalArgument("filenames", "files to be processed by qmlformat");
+
+ parser.process(app);
+
+ const auto positionalArguments = parser.positionalArguments();
+
+ if (positionalArguments.isEmpty())
+ parser.showHelp(-1);
+
+ if (!parser.isSet("inplace") && parser.value("newline") != "native") {
+ qWarning() << "Error: The -l option can only be used with -i";
+ return -1;
+ }
+
+ for (const QString& file: parser.positionalArguments()) {
+ if (!parseFile(file, parser.isSet("inplace"), parser.isSet("verbose"), !parser.isSet("no-sort"), parser.isSet("force"), parser.value("newline")))
+ success = false;
+ }
+#endif
+
+ return success ? 0 : 1;
+}
diff --git a/tools/qmlformat/qmlformat.pro b/tools/qmlformat/qmlformat.pro
new file mode 100644
index 0000000000..c8e74a4b55
--- /dev/null
+++ b/tools/qmlformat/qmlformat.pro
@@ -0,0 +1,17 @@
+option(host_build)
+
+QT = core qmldevtools-private
+
+SOURCES += main.cpp \
+ commentastvisitor.cpp \
+ dumpastvisitor.cpp \
+ restructureastvisitor.cpp
+
+QMAKE_TARGET_DESCRIPTION = QML Formatter
+
+HEADERS += \
+ commentastvisitor.h \
+ dumpastvisitor.h \
+ restructureastvisitor.h
+
+load(qt_tool)
diff --git a/tools/qmlformat/restructureastvisitor.cpp b/tools/qmlformat/restructureastvisitor.cpp
new file mode 100644
index 0000000000..f9ac2a20c2
--- /dev/null
+++ b/tools/qmlformat/restructureastvisitor.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** 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 "restructureastvisitor.h"
+
+#include <QList>
+
+RestructureAstVisitor::RestructureAstVisitor(Node *rootNode, bool sortImports) : m_sortImports(sortImports)
+{
+ rootNode->accept(this);
+}
+
+template<typename T>
+static QList<T *> findKind(UiObjectMemberList *list)
+{
+ QList<T *> members;
+ for (auto *item = list; item != nullptr; item = item->next) {
+ if (cast<T *>(item->member) != nullptr)
+ members.append(cast<T *>(item->member));
+ }
+
+ return members;
+}
+
+template<typename T>
+static QList<T *> findKind(UiHeaderItemList *list)
+{
+ QList<T *> members;
+ for (auto *item = list; item != nullptr; item = item->next) {
+ if (cast<T *>(item->headerItem) != nullptr)
+ members.append(cast<T *>(item->headerItem));
+ }
+
+ return members;
+}
+
+static QString parseUiQualifiedId(UiQualifiedId *id)
+{
+ QString name = id->name.toString();
+ for (auto *item = id->next; item != nullptr; item = item->next) {
+ name += "." + item->name;
+ }
+
+ return name;
+}
+
+void RestructureAstVisitor::endVisit(UiHeaderItemList *node)
+{
+ QList<Node *> correctOrder;
+
+ auto imports = findKind<UiImport>(node);
+
+ if (!m_sortImports)
+ return;
+
+ // Sort imports
+ std::sort(imports.begin(), imports.end(), [](UiImport *a, UiImport *b)
+ {
+ auto nameA = a->fileName.isEmpty() ? parseUiQualifiedId(a->importUri)
+ : a->fileName.toString();
+ auto nameB = b->fileName.isEmpty() ? parseUiQualifiedId(b->importUri)
+ : b->fileName.toString();
+
+ return nameA < nameB;
+ });
+
+ // Add imports
+ for (auto *import : imports)
+ correctOrder.append(import);
+
+ // Add all the other items
+ for (auto *item = node; item != nullptr; item = item->next) {
+ if (!correctOrder.contains(item->headerItem))
+ correctOrder.append(item->headerItem);
+ }
+
+ // Rebuild member list from correctOrder
+ for (auto *item = node; item != nullptr; item = item->next) {
+ item->headerItem = correctOrder.front();
+ correctOrder.pop_front();
+ }
+}
+
+void RestructureAstVisitor::endVisit(UiObjectMemberList *node)
+{
+ QList<UiObjectMember*> correctOrder;
+
+ auto enumDeclarations = findKind<UiEnumDeclaration>(node);
+ auto scriptBindings = findKind<UiScriptBinding>(node);
+ auto arrayBindings = findKind<UiArrayBinding>(node);
+ auto publicMembers = findKind<UiPublicMember>(node);
+ auto sourceElements = findKind<UiSourceElement>(node);
+ auto objectDefinitions = findKind<UiObjectDefinition>(node);
+
+ // This structure is based on https://doc.qt.io/qt-5/qml-codingconventions.html
+
+ // 1st id
+ for (auto *binding : scriptBindings) {
+ if (binding->qualifiedId->name == "id") {
+ correctOrder.append(binding);
+
+ scriptBindings.removeOne(binding);
+ break;
+ }
+ }
+
+ // 2nd enums
+ for (auto *enumDeclaration : enumDeclarations)
+ correctOrder.append(enumDeclaration);
+
+ // 3rd property declarations
+ for (auto *publicMember : publicMembers) {
+ if (publicMember->type != UiPublicMember::Property)
+ continue;
+
+ correctOrder.append(publicMember);
+ }
+
+ // 4th signals
+ for (auto *publicMember : publicMembers) {
+ if (publicMember->type != UiPublicMember::Signal)
+ continue;
+
+ correctOrder.append(publicMember);
+ }
+
+ // 5th functions
+ for (auto *source : sourceElements)
+ correctOrder.append(source);
+
+ // 6th properties
+ for (auto *binding : scriptBindings)
+ correctOrder.append(binding);
+
+ for (auto *binding : arrayBindings)
+ correctOrder.append(binding);
+
+ // 7th child objects
+ for (auto *objectDefinition : objectDefinitions)
+ correctOrder.append(objectDefinition);
+
+ // 8th all the rest
+ for (auto *item = node; item != nullptr; item = item->next) {
+ if (!correctOrder.contains(item->member))
+ correctOrder.append(item->member);
+ }
+
+ // Rebuild member list from correctOrder
+ for (auto *item = node; item != nullptr; item = item->next) {
+ item->member = correctOrder.front();
+ correctOrder.pop_front();
+ }
+}
diff --git a/tools/qmlformat/restructureastvisitor.h b/tools/qmlformat/restructureastvisitor.h
new file mode 100644
index 0000000000..a2195c8c2e
--- /dev/null
+++ b/tools/qmlformat/restructureastvisitor.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 RESTRUCTUREASTVISITOR_H
+#define RESTRUCTUREASTVISITOR_H
+
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+
+using namespace QQmlJS::AST;
+
+class RestructureAstVisitor : protected Visitor
+{
+public:
+ RestructureAstVisitor(Node *rootNode, bool sortImports);
+
+ void throwRecursionDepthError() override {}
+
+ void endVisit(UiObjectMemberList *node) override;
+ void endVisit(UiHeaderItemList *node) override;
+private:
+ bool m_sortImports = false;
+};
+
+#endif // RESTRUCTUREASTVISITOR_H
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
index 6cdfaf8f6f..f92b8d295d 100644
--- a/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
@@ -182,3 +182,13 @@ but this file does not exist. Possible reasons include:
endif()
!!ENDIF // !isEmpty(CMAKE_STATIC_TYPE)
endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_import_qml_plugins)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 5)
+ qt5_import_qml_plugins(${ARGV})
+ elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_import_qml_plugins(${ARGV})
+ endif()
+ endfunction()
+endif()
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index c37910bdaf..aca11e2ca4 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -179,7 +179,7 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
{
const QLatin1Char dot('.');
const QLatin1Char slash('/');
- const QStringList parts = uri.split(dot, QString::SkipEmptyParts);
+ const QStringList parts = uri.split(dot, Qt::SkipEmptyParts);
QString ver = version;
while (true) {
@@ -277,7 +277,7 @@ QVariantList findQmlImportsInQmlCode(const QString &filePath, const QString &cod
const auto diagnosticMessages = parser.diagnosticMessages();
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
std::cerr << QDir::toNativeSeparators(filePath).toStdString() << ':'
- << m.line << ':' << m.message.toStdString() << std::endl;
+ << m.loc.startLine << ':' << m.message.toStdString() << std::endl;
}
return QVariantList();
}
@@ -509,7 +509,7 @@ QString generateCmakeIncludeFileContent(const QVariantList &importList) {
QTextStream s(&content);
int importsCount = 0;
for (const QVariant &importVariant: importList) {
- if (static_cast<QMetaType::Type>(importVariant.type()) == QMetaType::QVariantMap) {
+ if (static_cast<QMetaType::Type>(importVariant.userType()) == QMetaType::QVariantMap) {
s << QStringLiteral("set(qml_import_scanner_import_") << importsCount
<< QStringLiteral(" \"");
diff --git a/tools/qmllint/componentversion.cpp b/tools/qmllint/componentversion.cpp
index 3dc4ac37d0..e5047b8302 100644
--- a/tools/qmllint/componentversion.cpp
+++ b/tools/qmllint/componentversion.cpp
@@ -27,67 +27,24 @@
****************************************************************************/
#include "componentversion.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)
-{
-}
+#include <QtCore/qstring.h>
ComponentVersion::ComponentVersion(const QString &versionString)
- : _major(NoVersion), _minor(NoVersion)
{
- int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1)
return;
bool ok = false;
- int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok);
+ const int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok);
if (!ok)
return;
- int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok);
+ const int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok);
if (!ok)
return;
- _major = maybeMajor;
- _minor = maybeMinor;
-}
-
-ComponentVersion::~ComponentVersion()
-{
+ m_major = maybeMajor;
+ m_minor = maybeMinor;
}
-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)
{
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)
{
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)
@@ -112,12 +70,11 @@ 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)
{
return !(lhs == rhs);
}
-
-}
diff --git a/tools/qmllint/componentversion.h b/tools/qmllint/componentversion.h
index 9d079f1d30..9c4604b9a3 100644
--- a/tools/qmllint/componentversion.h
+++ b/tools/qmllint/componentversion.h
@@ -29,36 +29,35 @@
#ifndef 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
-class QCryptographicHash;
-QT_END_NAMESPACE
-
-namespace LanguageUtils {
+#include <QtCore/qglobal.h>
class ComponentVersion
{
- int _major;
- int _minor;
-
public:
- static const int NoVersion;
- static const int MaxVersion;
+ static const int NoVersion = -1;
- ComponentVersion();
- ComponentVersion(int major, int minor);
+ ComponentVersion() = default;
+ ComponentVersion(int major, int minor) : m_major(major), m_minor(minor) {}
explicit ComponentVersion(const QString &versionString);
- ~ComponentVersion();
- int majorVersion() const
- { return _major; }
- int minorVersion() const
- { return _minor; }
+ int majorVersion() const { return m_major; }
+ int minorVersion() const { return m_minor; }
- bool isValid() const;
- QString toString() const;
- void addToHash(QCryptographicHash &hash) const;
+ bool isValid() const { return m_major >= 0 && m_minor >= 0; }
+
+private:
+ int m_major = NoVersion;
+ int m_minor = NoVersion;
};
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);
-} // namespace LanguageUtils
-
#endif // COMPONENTVERSION_H
diff --git a/tools/qmllint/fakemetaobject.cpp b/tools/qmllint/fakemetaobject.cpp
deleted file mode 100644
index 514bb2fe42..0000000000
--- a/tools/qmllint/fakemetaobject.cpp
+++ /dev/null
@@ -1,600 +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, int value)
-{ m_keys.append(key); m_values.append(value); }
-
-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));
- }
- len = m_values.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- foreach (int value, m_values)
- hash.addData(reinterpret_cast<const char *>(&value), sizeof(value));
-}
-
-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 += QLatin1String(": ");
- res += QString::number(m_values.value(i, -1));
- }
- 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();
-}
diff --git a/tools/qmllint/fakemetaobject.h b/tools/qmllint/fakemetaobject.h
deleted file mode 100644
index 4e0ea1f8b3..0000000000
--- a/tools/qmllint/fakemetaobject.h
+++ /dev/null
@@ -1,236 +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;
- QList<int> m_values;
-
-public:
- FakeMetaEnum();
- explicit FakeMetaEnum(const QString &name);
-
- bool isValid() const;
-
- QString name() const;
- void setName(const QString &name);
-
- void addKey(const QString &key, int value);
- 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
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp
index 49d64adb6e..4252cda9d6 100644
--- a/tools/qmllint/findunqualified.cpp
+++ b/tools/qmllint/findunqualified.cpp
@@ -27,32 +27,46 @@
****************************************************************************/
#include "findunqualified.h"
+#include "importedmembersvisitor.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 <QDirIterator>
-#include <QScopedValueRollback>
+#include <QtCore/qfile.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qscopedvaluerollback.h>
-#include <private/qqmljsast_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qv4codegen_p.h>
+static const QString prefixedName(const QString &prefix, const QString &name)
+{
+ Q_ASSERT(!prefix.endsWith('.'));
+ return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name);
+}
-QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc);
+static QQmlDirParser createQmldirParserForFile(const QString &filename)
+{
+ QFile f(filename);
+ f.open(QFile::ReadOnly);
+ QQmlDirParser parser;
+ parser.parse(f.readAll());
+ return parser;
+}
-static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename)
+static TypeDescriptionReader createQmltypesReaderForFile(const QString &filename)
{
QFile f(filename);
f.open(QFile::ReadOnly);
- QQmlJS::TypeDescriptionReader reader { filename, f.readAll() };
+ TypeDescriptionReader reader { filename, f.readAll() };
return reader;
}
-void FindUnqualifiedIDVisitor::enterEnvironment(ScopeType type, QString name)
+void FindUnqualifiedIDVisitor::enterEnvironment(ScopeType type, const QString &name)
{
- m_currentScope = m_currentScope->createNewChildScope(type, name);
+ m_currentScope = m_currentScope->createNewChildScope(type, name).get();
}
void FindUnqualifiedIDVisitor::leaveEnvironment()
@@ -60,33 +74,75 @@ void FindUnqualifiedIDVisitor::leaveEnvironment()
m_currentScope = m_currentScope->parentScope();
}
-enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned };
+void FindUnqualifiedIDVisitor::parseHeaders(QQmlJS::AST::UiHeaderItemList *header)
+{
+ using namespace QQmlJS::AST;
+
+ while (header) {
+ if (auto import = cast<UiImport *>(header->headerItem)) {
+ if (import->version) {
+ QString path;
+ auto uri = import->importUri;
+ while (uri) {
+ path.append(uri->name);
+ path.append("/");
+ uri = uri->next;
+ }
+ path.chop(1);
+ importHelper(path,
+ import->asToken.isValid() ? import->importId.toString() : QString(),
+ import->version->majorVersion,
+ import->version->minorVersion);
+ }
+ }
+ header = header->next;
+ }
+}
-QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin)
+ScopeTree *FindUnqualifiedIDVisitor::parseProgram(QQmlJS::AST::Program *program,
+ const QString &name)
+{
+ using namespace QQmlJS::AST;
+ ScopeTree *result = new ScopeTree(ScopeType::JSLexicalScope, name);
+ for (auto *statement = program->statements; statement; statement = statement->next) {
+ if (auto *function = cast<FunctionDeclaration *>(statement->statement)) {
+ MetaMethod method(function->name.toString());
+ method.setMethodType(MetaMethod::Method);
+ for (auto *parameters = function->formals; parameters; parameters = parameters->next)
+ method.addParameter(parameters->element->bindingIdentifier.toString(), "");
+ result->addMethod(method);
+ }
+ }
+ return result;
+}
+
+enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath };
+
+QStringList completeImportPaths(const QString &uri, const QString &basePath, int vmaj, int vmin)
{
static const QLatin1Char Slash('/');
static const QLatin1Char Backslash('\\');
- static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes");
- const QVector<QStringRef> parts = uri.splitRef(QLatin1Char('.'), QString::SkipEmptyParts);
+ const QVector<QStringRef> parts = uri.splitRef(QLatin1Char('.'), Qt::SkipEmptyParts);
QStringList qmlDirPathsPaths;
// fully & partially versioned parts + 1 unversioned for each base path
- qmlDirPathsPaths.reserve(basePaths.count() * (2 * parts.count() + 1));
+ qmlDirPathsPaths.reserve(2 * parts.count() + 1);
auto versionString = [](int vmaj, int vmin, ImportVersion version)
{
if (version == FullyVersioned) {
// extension with fully encoded version number (eg. MyModule.3.2)
- return QString::asprintf(".%d.%d", vmaj, vmin);
- } else if (version == PartiallyVersioned) {
+ return QString::fromLatin1(".%1.%2").arg(vmaj).arg(vmin);
+ }
+ if (version == PartiallyVersioned) {
// extension with encoded version major (eg. MyModule.3)
- return QString::asprintf(".%d", vmaj);
- } // else extension without version number (eg. MyModule)
+ return QString::fromLatin1(".%1").arg(vmaj);
+ }
+ // else extension without version number (eg. MyModule)
return QString();
};
- auto joinStringRefs = [](const QVector<QStringRef> &refs, const QChar &sep)
- {
+ auto joinStringRefs = [](const QVector<QStringRef> &refs, const QChar &sep) {
QString str;
for (auto it = refs.cbegin(); it != refs.cend(); ++it) {
if (it != refs.cbegin())
@@ -96,236 +152,270 @@ QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePat
return str;
};
- for (int version = FullyVersioned; version <= Unversioned; ++version) {
+ const ImportVersion initial = (vmin >= 0)
+ ? FullyVersioned
+ : (vmaj >= 0 ? PartiallyVersioned : Unversioned);
+ for (int version = initial; version <= BasePath; ++version) {
const QString ver = versionString(vmaj, vmin, static_cast<ImportVersion>(version));
- for (const QString &path : basePaths) {
- QString dir = path;
- if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
- dir += Slash;
+ QString dir = basePath;
+ if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
+ dir += Slash;
+ if (version == BasePath) {
+ qmlDirPathsPaths += dir;
+ } else {
// append to the end
- qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver + SlashPluginsDotQmltypes;
-
- if (version != Unversioned) {
- // insert in the middle
- for (int index = parts.count() - 2; index >= 0; --index) {
- qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash)
- + ver + Slash
- + joinStringRefs(parts.mid(index + 1), Slash) + SlashPluginsDotQmltypes;
- }
+ qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver;
+ }
+
+ if (version < Unversioned) {
+ // insert in the middle
+ for (int index = parts.count() - 2; index >= 0; --index) {
+ qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash)
+ + ver + Slash
+ + joinStringRefs(parts.mid(index + 1), Slash);
}
}
}
-
return qmlDirPathsPaths;
}
-void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int major, int minor)
+static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
+static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes");
+
+void FindUnqualifiedIDVisitor::readQmltypes(const QString &filename,
+ FindUnqualifiedIDVisitor::Import &result)
{
- QPair<QString, QString> importId { id, prefix };
- if (m_alreadySeenImports.contains(importId)) {
- return;
- } else {
- m_alreadySeenImports.insert(importId);
- }
- id = id.replace(QLatin1String("/"), QLatin1String("."));
- auto qmltypesPaths = completeQmltypesPaths(id, m_qmltypeDirs, major, minor);
+ auto reader = createQmltypesReaderForFile(filename);
+ auto succ = reader(&result.objects, &result.moduleApis, &result.dependencies);
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
+}
- QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects;
- QList<QQmlJS::ModuleApiInfo> moduleApis;
- QStringList dependencies;
- for (auto const &qmltypesPath : qmltypesPaths) {
- if (QFile::exists(qmltypesPath)) {
- auto reader = createReaderForFile(qmltypesPath);
- auto succ = reader(&objects, &moduleApis, &dependencies);
- if (!succ) {
- qDebug() << reader.errorMessage();
- }
- break;
+FindUnqualifiedIDVisitor::Import FindUnqualifiedIDVisitor::readQmldir(const QString &path)
+{
+ Import result;
+ auto reader = createQmldirParserForFile(path + SlashQmldir);
+ const auto imports = reader.imports();
+ for (const QString &import : imports)
+ result.dependencies.append(import);
+
+ QHash<QString, ScopeTree *> qmlComponents;
+ const auto components = reader.components();
+ for (auto it = components.begin(), end = components.end(); it != end; ++it) {
+ const QString filePath = path + QLatin1Char('/') + it->fileName;
+ if (!QFile::exists(filePath)) {
+ m_colorOut.write(QLatin1String("warning: "), Warning);
+ m_colorOut.write(it->fileName + QLatin1String(" is listed as component in ")
+ + path + SlashQmldir
+ + QLatin1String(" but does not exist.\n"));
+ continue;
}
+
+ auto mo = qmlComponents.find(it.key());
+ if (mo == qmlComponents.end())
+ mo = qmlComponents.insert(it.key(), localFile2ScopeTree(filePath));
+
+ (*mo)->addExport(
+ it.key(), reader.typeNamespace(),
+ ComponentVersion(it->majorVersion, it->minorVersion));
}
- for (auto const &dependency : qAsConst(dependencies)) {
+ for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it)
+ result.objects.insert( it.key(), ScopeTree::ConstPtr(it.value()));
+
+ if (!reader.plugins().isEmpty() && QFile::exists(path + SlashPluginsDotQmltypes))
+ readQmltypes(path + SlashPluginsDotQmltypes, result);
+
+ return result;
+}
+
+void FindUnqualifiedIDVisitor::processImport(const QString &prefix, const FindUnqualifiedIDVisitor::Import &import)
+{
+ for (auto const &dependency : qAsConst(import.dependencies)) {
auto const split = dependency.split(" ");
- auto const id = split.at(0);
- auto const major = split.at(1).split('.').at(0).toInt();
- auto const minor = split.at(1).split('.').at(1).toInt();
- importHelper(id, QString(), major, minor);
+ auto const &id = split.at(0);
+ if (split.length() > 1) {
+ const auto version = split.at(1).split('.');
+ importHelper(id, QString(),
+ version.at(0).toInt(),
+ version.length() > 1 ? version.at(1).toInt() : -1);
+ } else {
+ importHelper(id, QString(), -1, -1);
+ }
+
+
}
+
// add objects
- for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) {
- auto val = ob_it.value();
- m_exportedName2MetaObject[prefix + val->className()] = val;
- for (auto export_ : val->exports()) {
- m_exportedName2MetaObject[prefix + export_.type] = val;
- }
- for (auto enumCount = 0; enumCount < val->enumeratorCount(); ++enumCount) {
- m_currentScope->insertQMLIdentifier(val->enumerator(enumCount).name());
+ for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
+ const auto &val = it.value();
+ m_types[it.key()] = val;
+ m_exportedName2Scope.insert(prefixedName(prefix, val->className()), val);
+
+ const auto exports = val->exports();
+ for (const auto &valExport : exports)
+ m_exportedName2Scope.insert(prefixedName(prefix, valExport.type()), val);
+
+ const auto enums = val->enums();
+ for (const auto &valEnum : enums)
+ m_currentScope->addEnum(valEnum);
+ }
+}
+
+void FindUnqualifiedIDVisitor::importHelper(const QString &module, const QString &prefix,
+ int major, int minor)
+{
+ const QString id = QString(module).replace(QLatin1Char('/'), QLatin1Char('.'));
+ QPair<QString, QString> importId { id, prefix };
+ if (m_alreadySeenImports.contains(importId))
+ return;
+ m_alreadySeenImports.insert(importId);
+
+ for (const QString &qmltypeDir : m_qmltypeDirs) {
+ auto qmltypesPaths = completeImportPaths(id, qmltypeDir, major, minor);
+
+ for (auto const &qmltypesPath : qmltypesPaths) {
+ if (QFile::exists(qmltypesPath + SlashQmldir)) {
+ processImport(prefix, readQmldir(qmltypesPath));
+
+ // break so that we don't import unversioned qml components
+ // in addition to versioned ones
+ break;
+ }
+
+ if (!m_qmltypeFiles.isEmpty())
+ continue;
+
+ Import result;
+
+ QDirIterator it { qmltypesPath, QStringList() << QLatin1String("*.qmltypes"), QDir::Files };
+
+ while (it.hasNext())
+ readQmltypes(it.next(), result);
+
+ processImport(prefix, result);
}
}
+
+ if (!m_qmltypeFiles.isEmpty())
+ {
+ Import result;
+
+ for (const auto &qmltypeFile : m_qmltypeFiles)
+ readQmltypes(qmltypeFile, result);
+
+ processImport("", result);
+ }
}
-LanguageUtils::FakeMetaObject *
-FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
+ScopeTree *FindUnqualifiedIDVisitor::localFile2ScopeTree(const QString &filePath)
{
using namespace QQmlJS::AST;
- auto fake = new LanguageUtils::FakeMetaObject;
- fake->setClassName(QFileInfo { filePath }.baseName());
+ const QFileInfo info { filePath };
+ QString baseName = info.baseName();
+ const QString scopeName = baseName.endsWith(".ui") ? baseName.chopped(3) : baseName;
+
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ const QString lowerSuffix = info.suffix().toLower();
+ const bool isESModule = lowerSuffix == QLatin1String("mjs");
+ const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
+
QFile file(filePath);
if (!file.open(QFile::ReadOnly)) {
- return fake;
+ return new ScopeTree(isJavaScript ? ScopeType::JSLexicalScope : ScopeType::QMLScope,
+ scopeName);
}
+
QString code = file.readAll();
file.close();
- QQmlJS::Engine engine;
- QQmlJS::Lexer lexer(&engine);
-
- lexer.setCode(code, 1, true);
+ lexer.setCode(code, /*line = */ 1, /*qmlMode=*/ !isJavaScript);
QQmlJS::Parser parser(&engine);
- if (!parser.parse()) {
- return fake;
+
+ const bool success = isJavaScript ? (isESModule ? parser.parseModule()
+ : parser.parseProgram())
+ : parser.parse();
+ if (!success) {
+ return new ScopeTree(isJavaScript ? ScopeType::JSLexicalScope : ScopeType::QMLScope,
+ scopeName);
}
- QQmlJS::AST::UiProgram *program = parser.ast();
- auto header = program->headers;
- while (header) {
- if (auto import = cast<UiImport *>(header->headerItem)) {
- if (import->version) {
- QString path;
- auto uri = import->importUri;
- while (uri) {
- path.append(uri->name);
- path.append("/");
- uri = uri->next;
- }
- path.chop(1);
- QString prefix = QLatin1String("");
- if (import->asToken.isValid()) {
- prefix += import->importId + QLatin1Char('.');
- }
- importHelper(path, prefix, import->version->majorVersion, import->version->minorVersion);
- }
- }
- header = header->next;
+
+ if (!isJavaScript) {
+ QQmlJS::AST::UiProgram *program = parser.ast();
+ parseHeaders(program->headers);
+ ImportedMembersVisitor membersVisitor(&m_colorOut);
+ program->members->accept(&membersVisitor);
+ return membersVisitor.result(scopeName);
}
- auto member = program->members;
- // member should be the sole element
- Q_ASSERT(!member->next);
- Q_ASSERT(member && member->member->kind == UiObjectMember::Kind_UiObjectDefinition);
- auto definition = static_cast<UiObjectDefinition *>(member->member);
- auto qualifiedId = definition->qualifiedTypeNameId;
- while (qualifiedId && qualifiedId->next) {
- qualifiedId = qualifiedId->next;
- }
- fake->setSuperclassName(qualifiedId->name.toString());
- UiObjectMemberList *initMembers = definition->initializer->members;
- while (initMembers) {
- switch (initMembers->member->kind) {
- case UiObjectMember::Kind_UiArrayBinding: {
- // nothing to do
- break;
- }
- case UiObjectMember::Kind_UiEnumDeclaration: {
- // nothing to do
- break;
- }
- case UiObjectMember::Kind_UiObjectBinding: {
- // nothing to do
- break;
- }
- case UiObjectMember::Kind_UiObjectDefinition: {
- // creates nothing accessible
- break;
- }
- case UiObjectMember::Kind_UiPublicMember: {
- auto publicMember = static_cast<UiPublicMember *>(initMembers->member);
- switch (publicMember->type) {
- case UiPublicMember::Signal: {
- UiParameterList *param = publicMember->parameters;
- LanguageUtils::FakeMetaMethod method;
- method.setMethodType(LanguageUtils::FakeMetaMethod::Signal);
- method.setMethodName(publicMember->name.toString());
- while (param) {
- method.addParameter(param->name.toString(), param->type->name.toString());
- param = param->next;
- }
- fake->addMethod(method);
- break;
- }
- case UiPublicMember::Property: {
- LanguageUtils::FakeMetaProperty fakeprop { publicMember->name.toString(),
- publicMember->typeModifier.toString(),
- false,
- false,
- false,
- 0 };
- fake->addProperty(fakeprop);
- break;
- }
- }
- break;
- }
- case UiObjectMember::Kind_UiScriptBinding: {
- // does not create anything new, ignore
- break;
- }
- case UiObjectMember::Kind_UiSourceElement: {
- auto sourceElement = static_cast<UiSourceElement *>(initMembers->member);
- if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
- LanguageUtils::FakeMetaMethod method;
- method.setMethodType(LanguageUtils::FakeMetaMethod::Method);
- FormalParameterList *parameters = fexpr->formals;
- while (parameters) {
- method.addParameter(parameters->element->bindingIdentifier.toString(),
- "");
- parameters = parameters->next;
- }
- fake->addMethod(method);
- } else if (ClassExpression *clexpr =
- sourceElement->sourceElement->asClassDefinition()) {
- LanguageUtils::FakeMetaProperty prop {
- clexpr->name.toString(), "", false, false, false, 1
- };
- fake->addProperty(prop);
- } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
- // nothing to do
- } else {
- qDebug() << "unsupportedd sourceElement at" << sourceElement->firstSourceLocation()
- << sourceElement->sourceElement->kind;
- }
- break;
- }
- default: {
- qDebug() << "unsupported element of kind" << initMembers->member->kind;
- }
- }
- initMembers = initMembers->next;
+
+ // TODO: Anything special to do with ES modules here?
+ return parseProgram(QQmlJS::AST::cast<QQmlJS::AST::Program *>(parser.rootNode()), scopeName);
+}
+
+void FindUnqualifiedIDVisitor::importFileOrDirectory(const QString &fileOrDirectory,
+ const QString &prefix)
+{
+ QString name = fileOrDirectory;
+
+ if (QFileInfo(name).isRelative())
+ name = QDir(QFileInfo { m_filePath }.path()).filePath(name);
+
+ if (QFileInfo(name).isFile()) {
+ m_exportedName2Scope.insert(prefix, ScopeTree::ConstPtr(localFile2ScopeTree(name)));
+ return;
+ }
+
+ QDirIterator it { name, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
+ while (it.hasNext()) {
+ ScopeTree::ConstPtr scope(localFile2ScopeTree(it.next()));
+ if (!scope->className().isEmpty())
+ m_exportedName2Scope.insert(prefixedName(prefix, scope->className()), scope);
}
- return fake;
}
-void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString name)
+void FindUnqualifiedIDVisitor::importExportedNames(const QStringRef &prefix, QString name)
{
+ QList<ScopeTree::ConstPtr> scopes;
for (;;) {
- auto metaObject = m_exportedName2MetaObject[m_exportedName2MetaObject.contains(name)
- ? name
- : prefix + QLatin1Char('.') + name];
- if (metaObject) {
- auto propertyCount = metaObject->propertyCount();
- for (auto i = 0; i < propertyCount; ++i) {
- m_currentScope->insertPropertyIdentifier(metaObject->property(i).name());
- }
-
- m_currentScope->addMethodsFromMetaObject(metaObject);
+ ScopeTree::ConstPtr scope = m_exportedName2Scope.value(m_exportedName2Scope.contains(name)
+ ? name
+ : prefix + QLatin1Char('.') + name);
+ if (scope) {
+ if (scopes.contains(scope)) {
+ QString inheritenceCycle = name;
+ for (const auto seen: qAsConst(scopes)) {
+ inheritenceCycle.append(QLatin1String(" -> "));
+ inheritenceCycle.append(seen->superclassName());
+ }
- name = metaObject->superclassName();
- if (name.isEmpty() || name == QLatin1String("QObject")) {
+ m_colorOut.write(QLatin1String("Warning: "), Warning);
+ m_colorOut.write(QString::fromLatin1("%1 is part of an inheritance cycle: %2\n")
+ .arg(name)
+ .arg(inheritenceCycle));
+ m_unknownImports.insert(name);
+ m_visitFailed = true;
break;
}
+ scopes.append(scope);
+ const auto properties = scope->properties();
+ for (auto property : properties) {
+ property.setType(m_exportedName2Scope.value(property.typeName()).get());
+ m_currentScope->insertPropertyIdentifier(property);
+ }
+
+ m_currentScope->addMethods(scope->methods());
+ name = scope->superclassName();
+ if (name.isEmpty() || name == QLatin1String("QObject"))
+ break;
} else {
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_visitFailed = true;
break;
}
}
@@ -341,42 +431,48 @@ void FindUnqualifiedIDVisitor::throwRecursionDepthError()
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
{
enterEnvironment(ScopeType::QMLScope, "program");
- QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects;
- QList<QQmlJS::ModuleApiInfo> moduleApis;
+ QHash<QString, ScopeTree::ConstPtr> objects;
+ QList<ModuleApiInfo> moduleApis;
QStringList dependencies;
for (auto const &dir : m_qmltypeDirs) {
QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter,
QDirIterator::Subdirectories };
while (it.hasNext()) {
- auto reader = createReaderForFile(it.next());
+ auto reader = createQmltypesReaderForFile(it.next());
auto succ = reader(&objects, &moduleApis, &dependencies);
- if (!succ) {
- qDebug() << reader.errorMessage();
- }
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
}
}
- // add builtins
- for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) {
- auto val = ob_it.value();
- for (auto export_ : val->exports()) {
- m_exportedName2MetaObject[export_.type] = val;
- }
- for (auto enumCount = 0; enumCount < val->enumeratorCount(); ++enumCount) {
- m_currentScope->insertQMLIdentifier(val->enumerator(enumCount).name());
+
+ if (!m_qmltypeFiles.isEmpty())
+ {
+ for (const auto &qmltypeFile : m_qmltypeFiles) {
+ auto reader = createQmltypesReaderForFile(qmltypeFile);
+ auto succ = reader(&objects, &moduleApis, &dependencies);
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
}
}
- // add "self" (as we only ever check the first part of a qualified identifier, we get away with
- // using an empty FakeMetaObject
- m_exportedName2MetaObject[QFileInfo { m_filePath }.baseName()] = {};
- // add QML builtins
- m_exportedName2MetaObject["QtObject"] = {}; // QtObject contains nothing of interest
+ // add builtins
+ for (auto objectIt = objects.begin(); objectIt != objects.end(); ++objectIt) {
+ auto val = objectIt.value();
+ m_types[objectIt.key()] = val;
+
+ const auto exports = val->exports();
+ for (const auto &valExport : exports)
+ m_exportedName2Scope.insert(valExport.type(), val);
+
+ 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
+ // using an empty ScopeTree
+ m_exportedName2Scope.insert(QFileInfo { m_filePath }.baseName(), {});
- LanguageUtils::FakeMetaObject *meta = new LanguageUtils::FakeMetaObject{};
- meta->addProperty(LanguageUtils::FakeMetaProperty {"enabled", "bool", false, false, false, 0});
- meta->addProperty(LanguageUtils::FakeMetaProperty {"ignoreUnknownSignals", "bool", false, false, false, 0});
- meta->addProperty(LanguageUtils::FakeMetaProperty {"target", "QObject", false, false, false, 0});
- m_exportedName2MetaObject["Connections"] = LanguageUtils::FakeMetaObject::ConstPtr { meta };
+ importFileOrDirectory(".", QString());
return true;
}
@@ -454,7 +550,8 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::CaseBlock *)
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *catchStatement)
{
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;
}
@@ -465,8 +562,13 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Catch *)
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::WithStatement *withStatement)
{
- m_colorOut.write(QString::asprintf("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("Warning: "), Warning);
+ 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");
return true;
}
@@ -476,40 +578,62 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::WithStatement *)
leaveEnvironment();
}
+static QString signalName(const QStringRef &handlerName)
+{
+ if (handlerName.startsWith("on") && handlerName.size() > 2) {
+ QString signal = handlerName.mid(2).toString();
+ for (int i = 0; i < signal.length(); ++i) {
+ QCharRef ch = signal[i];
+ if (ch.isLower())
+ return QString();
+ if (ch.isUpper()) {
+ ch = ch.toLower();
+ return signal;
+ }
+ }
+ }
+ return QString();
+}
+
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
{
using namespace QQmlJS::AST;
auto name = uisb->qualifiedId->name;
if (name == QLatin1String("id")) {
// found id
- auto expstat = static_cast<ExpressionStatement *>(uisb->statement);
- auto identexp = static_cast<IdentifierExpression *>(expstat->expression);
+ auto expstat = cast<ExpressionStatement *>(uisb->statement);
+ auto identexp = cast<IdentifierExpression *>(expstat->expression);
QString elementName = m_currentScope->name();
- m_qmlid2meta.insert(identexp->name.toString(), m_exportedName2MetaObject[elementName]);
- if (m_currentScope->isVisualRootScope()) {
+ m_qmlid2scope.insert(identexp->name.toString(), m_currentScope);
+ if (m_currentScope->isVisualRootScope())
m_rootId = identexp->name.toString();
+ } else {
+ const QString signal = signalName(name);
+ if (signal.isEmpty())
+ return true;
+
+ if (!m_currentScope->methods().contains(signal)) {
+ m_currentScope->addUnmatchedSignalHandler(name.toString(), uisb->firstSourceLocation());
+ return true;
}
- } else if (name.startsWith("on") && name.size() > 2 && name.at(2).isUpper()) {
- auto statement = uisb->statement;
+
+ const auto statement = uisb->statement;
if (statement->kind == Node::Kind::Kind_ExpressionStatement) {
- if (static_cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) {
+ if (cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) {
// functions are already handled
// they do not get names inserted according to the signal, but access their formal
// parameters
return true;
}
}
- QString signal = name.mid(2).toString();
- signal[0] = signal[0].toLower();
- if (!m_currentScope->methods().contains(signal)) {
- qDebug() << "Info file does not contain signal" << signal;
- } else {
- auto method = m_currentScope->methods()[signal];
- for (auto const &param : method.parameterNames()) {
- auto firstSourceLocation = uisb->statement->firstSourceLocation();
- bool hasMultilineStatementBody = uisb->statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
- m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, hasMultilineStatementBody);
- }
+
+ auto method = m_currentScope->methods()[signal];
+ for (auto const &param : method.parameterNames()) {
+ const auto firstSourceLocation = statement->firstSourceLocation();
+ bool hasMultilineStatementBody
+ = statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
+ m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation,
+ hasMultilineStatementBody);
}
return true;
}
@@ -520,35 +644,45 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiPublicMember *uipm)
{
// property bool inactive: !active
// extract name inactive
- m_currentScope->insertPropertyIdentifier(uipm->name.toString());
+ MetaProperty property(
+ uipm->name.toString(),
+ // TODO: signals, complex types etc.
+ uipm->memberType ? uipm->memberType->name.toString() : QString(),
+ uipm->typeModifier == QLatin1String("list"),
+ !uipm->isReadonlyMember,
+ false,
+ uipm->memberType ? (uipm->memberType->name == QLatin1String("alias")) : false,
+ 0);
+ property.setType(m_exportedName2Scope.value(property.typeName()).get());
+ m_currentScope->insertPropertyIdentifier(property);
return true;
}
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
{
auto name = idexp->name;
- if (!m_exportedName2MetaObject.contains(name.toString())) {
- m_currentScope->addIdToAccssedIfNotInParentScopes(
- { name.toString(), idexp->firstSourceLocation() }, m_unknownImports);
- }
+ m_currentScope->addIdToAccessed(name.toString(), idexp->firstSourceLocation());
+ m_fieldMemberBase = idexp;
return true;
}
-FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs,
- const QString &code, const QString &fileName)
+FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QStringList qmltypeFiles, QString code,
+ QString fileName, bool silent)
: m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }),
m_currentScope(m_rootScope.get()),
- m_qmltypeDirs(qmltypeDirs),
- m_code(code),
+ m_qmltypeDirs(std::move(qmltypeDirs)),
+ m_qmltypeFiles(std::move(qmltypeFiles)),
+ m_code(std::move(code)),
m_rootId(QLatin1String("<id>")),
- m_filePath(fileName)
+ m_filePath(std::move(fileName)),
+ m_colorOut(silent)
{
// setup color output
- m_colorOut.insertColorMapping(Error, ColorOutput::RedForeground);
- m_colorOut.insertColorMapping(Warning, ColorOutput::PurpleForeground);
- m_colorOut.insertColorMapping(Info, ColorOutput::BlueForeground);
- m_colorOut.insertColorMapping(Normal, ColorOutput::DefaultColor);
- m_colorOut.insertColorMapping(Hint, ColorOutput::GreenForeground);
+ m_colorOut.insertMapping(Error, ColorOutput::RedForeground);
+ m_colorOut.insertMapping(Warning, ColorOutput::PurpleForeground);
+ m_colorOut.insertMapping(Info, ColorOutput::BlueForeground);
+ m_colorOut.insertMapping(Normal, ColorOutput::DefaultColor);
+ m_colorOut.insertMapping(Hint, ColorOutput::GreenForeground);
QLatin1String jsGlobVars[] = {
/* Not listed on the MDN page; browser and QML extensions: */
// console/debug api
@@ -556,32 +690,36 @@ FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDir
// garbage collector
QLatin1String("gc"),
// 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
QLatin1String("XMLHttpRequest")
};
- for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr; ++globalName) {
- m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), QQmlJS::AST::VariableScope::Const);
+ for (const char **globalName = QV4::Compiler::Codegen::s_globalNames;
+ *globalName != nullptr;
+ ++globalName) {
+ m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName),
+ QQmlJS::AST::VariableScope::Const);
}
for (const auto& jsGlobVar: jsGlobVars)
m_currentScope->insertJSIdentifier(jsGlobVar, QQmlJS::AST::VariableScope::Const);
}
-FindUnqualifiedIDVisitor::~FindUnqualifiedIDVisitor() = default;
-
bool FindUnqualifiedIDVisitor::check()
{
if (m_visitFailed)
return false;
// now that all ids are known, revisit any Connections whose target were perviously unknown
- for (auto const& outstandingConnection: m_outstandingConnections) {
- auto metaObject = m_qmlid2meta[outstandingConnection.targetName];
- outstandingConnection.scope->addMethodsFromMetaObject(metaObject);
+ for (auto const &outstandingConnection: m_outstandingConnections) {
+ auto targetScope = m_qmlid2scope[outstandingConnection.targetName];
+ if (outstandingConnection.scope)
+ outstandingConnection.scope->addMethods(targetScope->methods());
QScopedValueRollback<ScopeTree*> rollback(m_currentScope, outstandingConnection.scope);
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_exportedName2Scope,
+ m_rootScope.get(), m_rootId, m_colorOut);
}
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
@@ -597,18 +735,16 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
void FindUnqualifiedIDVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
{
using namespace QQmlJS::AST;
- if (!fexpr->name.isEmpty()) {
- auto name = fexpr->name.toString();
- if (m_currentScope->scopeType() == ScopeType::QMLScope) {
- m_currentScope->insertQMLIdentifier(name);
- } else {
+ auto name = fexpr->name.toString();
+ if (!name.isEmpty()) {
+ if (m_currentScope->scopeType() == ScopeType::QMLScope)
+ m_currentScope->addMethod(MetaMethod(name, QLatin1String("void")));
+ else
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)
@@ -646,24 +782,17 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
// construct path
QString prefix = QLatin1String("");
if (import->asToken.isValid()) {
- prefix += import->importId + QLatin1Char('.');
+ prefix += import->importId;
}
auto dirname = import->fileName.toString();
- if (!dirname.isEmpty()) {
- QFileInfo info { dirname };
- if (info.isRelative()) {
- dirname = QDir(QFileInfo { m_filePath }.path()).filePath(dirname);
- }
- QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
- while (it.hasNext()) {
- LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next());
- m_exportedName2MetaObject.insert(
- fake->className(), QSharedPointer<const LanguageUtils::FakeMetaObject>(fake));
- }
- }
+ if (!dirname.isEmpty())
+ importFileOrDirectory(dirname, prefix);
+
QString path {};
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
+ const QString importId = import->importId.toString();
+ m_qmlid2scope.insert(importId, m_exportedName2Scope.value(importId).get());
}
if (import->version) {
auto uri = import->importUri;
@@ -681,14 +810,17 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
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;
}
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
{
// property QtObject __styleData: QtObject {...}
- m_currentScope->insertPropertyIdentifier(uiob->qualifiedId->name.toString());
+
QString name {};
auto id = uiob->qualifiedTypeNameId;
QStringRef prefix = uiob->qualifiedTypeNameId->name;
@@ -697,20 +829,34 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
id = id->next;
}
name.chop(1);
+
+ MetaProperty prop(uiob->qualifiedId->name.toString(), name, false, true, true,
+ name == QLatin1String("alias"), 0);
+ prop.setType(m_exportedName2Scope.value(uiob->qualifiedTypeNameId->name.toString()).get());
+ m_currentScope->addProperty(prop);
+
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
- return true;
importExportedNames(prefix, name);
return true;
}
-void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectBinding *)
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
{
+ const auto childScope = m_currentScope;
leaveEnvironment();
+ MetaProperty property(uiob->qualifiedId->name.toString(),
+ uiob->qualifiedTypeNameId->name.toString(),
+ false, true, true,
+ uiob->qualifiedTypeNameId->name == QLatin1String("alias"),
+ 0);
+ property.setType(childScope);
+ m_currentScope->addProperty(property);
}
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
{
+ using namespace QQmlJS::AST;
+
QString name {};
auto id = uiod->qualifiedTypeNameId;
QStringRef prefix = uiod->qualifiedTypeNameId->name;
@@ -722,8 +868,7 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
enterEnvironment(ScopeType::QMLScope, name);
if (name.isLower())
return false; // Ignore grouped properties for now
- if (name == QLatin1String("Component") || name == QLatin1String("QtObject")) // there is no typeinfo for Component
- return true;
+
importExportedNames(prefix, name);
if (name.endsWith("Connections")) {
QString target;
@@ -745,39 +890,87 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
}
member = member->next;
}
- LanguageUtils::FakeMetaObject::ConstPtr metaObject {};
+ const ScopeTree *targetScope;
if (target.isEmpty()) {
// no target set, connection comes from parentF
ScopeTree* scope = m_currentScope;
do {
scope = scope->parentScope(); // TODO: rename method
} while (scope->scopeType() != ScopeType::QMLScope);
- auto metaObject = m_exportedName2MetaObject[scope->name()];
+ targetScope = m_exportedName2Scope.value(scope->name()).get();
} else {
// there was a target, check if we already can find it
- auto metaObjectIt = m_qmlid2meta.find(target);
- if (metaObjectIt != m_qmlid2meta.end()) {
- metaObject = *metaObjectIt;
+ auto scopeIt = m_qmlid2scope.find(target);
+ if (scopeIt != m_qmlid2scope.end()) {
+ targetScope = *scopeIt;
} else {
m_outstandingConnections.push_back({target, m_currentScope, uiod});
return false; // visit children later once target is known
}
}
- m_currentScope->addMethodsFromMetaObject(metaObject);
+ if (targetScope)
+ m_currentScope->addMethods(targetScope->methods());
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::PatternElement *element)
+{
+ if (element->isVariableDeclaration()) {
+ QQmlJS::AST::BoundNames names;
+ element->boundNames(&names);
+ for (const auto &name : names)
+ m_currentScope->insertJSIdentifier(name.id, element->scope);
}
+
return true;
}
void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
{
+ auto childScope = m_currentScope;
leaveEnvironment();
+ childScope->updateParentProperty(m_currentScope);
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FieldMemberExpression *)
+{
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
+{
+ using namespace QQmlJS::AST;
+ ExpressionNode *base = fieldMember->base;
+ while (auto *nested = cast<NestedExpression *>(base))
+ base = nested->expression;
+
+ if (m_fieldMemberBase == base) {
+ QString type;
+ if (auto *binary = cast<BinaryExpression *>(base)) {
+ if (binary->op == QSOperator::As) {
+ if (auto *right = cast<IdentifierExpression *>(binary->right))
+ type = right->name.toString();
+ }
+ }
+ m_currentScope->accessMember(fieldMember->name.toString(),
+ type,
+ fieldMember->identifierToken);
+ m_fieldMemberBase = fieldMember;
+ } else {
+ m_fieldMemberBase = nullptr;
+ }
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::BinaryExpression *)
+{
+ return true;
}
-QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc)
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::BinaryExpression *binExp)
{
- QDebugStateSaver saver(dbg);
- dbg.nospace() << loc.startLine;
- dbg.nospace() << ":";
- dbg.nospace() << loc.startColumn;
- return dbg.maybeSpace();
+ if (binExp->op == QSOperator::As && m_fieldMemberBase == binExp->left)
+ m_fieldMemberBase = binExp;
+ else
+ m_fieldMemberBase = nullptr;
}
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
index 181f42f265..1394816ac9 100644
--- a/tools/qmllint/findunqualified.h
+++ b/tools/qmllint/findunqualified.h
@@ -29,31 +29,50 @@
#ifndef FINDUNQUALIFIED_H
#define FINDUNQUALIFIED_H
-#include "qmljstypedescriptionreader.h"
-#include "qcoloroutput_p.h"
-
-#include <private/qqmljsastvisitor_p.h>
-#include <private/qqmljsast_p.h>
-
-#include <QScopedPointer>
-
-class ScopeTree;
-enum class ScopeType;
-
-class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor {
-
+//
+// 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 "typedescriptionreader.h"
+#include "scopetree.h"
+#include "qcoloroutput.h"
+
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+
+#include <QtCore/qscopedpointer.h>
+
+class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor
+{
+ Q_DISABLE_COPY_MOVE(FindUnqualifiedIDVisitor)
public:
- explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, const QString& fileName);
- ~FindUnqualifiedIDVisitor() override;
+ explicit FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QStringList qmltypeFiles, QString code,
+ QString fileName, bool silent);
+ ~FindUnqualifiedIDVisitor() override = default;
bool check();
private:
+ struct Import {
+ QHash<QString, ScopeTree::ConstPtr> objects;
+ QList<ModuleApiInfo> moduleApis;
+ QStringList dependencies;
+ };
+
QScopedPointer<ScopeTree> m_rootScope;
ScopeTree *m_currentScope;
- QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_exportedName2MetaObject;
+ QQmlJS::AST::ExpressionNode *m_fieldMemberBase = nullptr;
+ QHash<QString, ScopeTree::ConstPtr> m_types;
+ QHash<QString, ScopeTree::ConstPtr> m_exportedName2Scope;
QStringList m_qmltypeDirs;
- const QString& m_code;
- QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_qmlid2meta;
+ QStringList m_qmltypeFiles;
+ QString m_code;
+ QHash<QString, const ScopeTree *> m_qmlid2scope;
QString m_rootId;
QString m_filePath;
QSet<QPair<QString, QString>> m_alreadySeenImports;
@@ -61,17 +80,31 @@ private:
ColorOutput m_colorOut;
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
- void enterEnvironment(ScopeType type, QString name);
+ void enterEnvironment(ScopeType type, const QString &name);
void leaveEnvironment();
- void importHelper(QString id, QString prefix, int major, int minor);
- LanguageUtils::FakeMetaObject* localQmlFile2FakeMetaObject(QString filePath);
+ void importHelper(const QString &module, const QString &prefix = QString(),
+ int major = -1, int minor = -1);
+ void readQmltypes(const QString &filename, Import &result);
+ Import readQmldir(const QString &dirname);
+ void processImport(const QString &prefix, const Import &import);
- void importExportedNames(QStringRef prefix, QString name);
+ ScopeTree *localFile2ScopeTree(const QString &filePath);
+
+ void importFileOrDirectory(const QString &directory, const QString &prefix);
+ void importExportedNames(const QStringRef &prefix, QString name);
+
+ void parseHeaders(QQmlJS::AST::UiHeaderItemList *headers);
+ ScopeTree *parseProgram(QQmlJS::AST::Program *program, const QString &name);
void throwRecursionDepthError() override;
@@ -125,7 +158,13 @@ private:
// expression handling
bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
-};
+ bool visit(QQmlJS::AST::PatternElement *) override;
+ bool visit(QQmlJS::AST::FieldMemberExpression *idprop) override;
+ void endVisit(QQmlJS::AST::FieldMemberExpression *) override;
+
+ bool visit(QQmlJS::AST::BinaryExpression *) override;
+ void endVisit(QQmlJS::AST::BinaryExpression *) override;
+};
#endif // FINDUNQUALIFIED_H
diff --git a/tools/qmllint/importedmembersvisitor.cpp b/tools/qmllint/importedmembersvisitor.cpp
new file mode 100644
index 0000000000..c5214f2bb9
--- /dev/null
+++ b/tools/qmllint/importedmembersvisitor.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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 "importedmembersvisitor.h"
+#include "scopetree.h"
+
+using namespace QQmlJS::AST;
+
+ScopeTree *ImportedMembersVisitor::result(const QString &scopeName) const
+{
+ ScopeTree *result = new ScopeTree(ScopeType::QMLScope);
+ result->setClassName(scopeName);
+ result->setSuperclassName(m_rootObject->superclassName());
+ const auto properties = m_rootObject->properties();
+ for (auto property : properties) {
+ if (property.isAlias()) {
+ const auto it = m_objects.find(property.typeName());
+ if (it != m_objects.end())
+ property.setType(it->get());
+ result->addProperty(property);
+ } else {
+ result->addProperty(property);
+ }
+ }
+
+ for (const auto &method : m_rootObject->methods())
+ result->addMethod(method);
+
+ return result;
+}
+
+bool ImportedMembersVisitor::visit(UiObjectDefinition *definition)
+{
+ ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope));
+ QString superType;
+ for (auto segment = definition->qualifiedTypeNameId; segment; segment = segment->next) {
+ if (!superType.isEmpty())
+ superType.append('.');
+ superType.append(segment->name.toString());
+ }
+ scope->setSuperclassName(superType);
+ if (!m_rootObject)
+ m_rootObject = scope;
+ m_currentObjects.append(scope);
+ return true;
+}
+
+void ImportedMembersVisitor::endVisit(UiObjectDefinition *)
+{
+ m_currentObjects.pop_back();
+}
+
+bool ImportedMembersVisitor::visit(UiPublicMember *publicMember)
+{
+ switch (publicMember->type) {
+ case UiPublicMember::Signal: {
+ UiParameterList *param = publicMember->parameters;
+ MetaMethod method;
+ method.setMethodType(MetaMethod::Signal);
+ method.setMethodName(publicMember->name.toString());
+ while (param) {
+ method.addParameter(param->name.toString(), param->type->name.toString());
+ param = param->next;
+ }
+ currentObject()->addMethod(method);
+ break;
+ }
+ case UiPublicMember::Property: {
+ auto typeName = publicMember->memberType->name;
+ const bool isAlias = (typeName == QLatin1String("alias"));
+ if (isAlias) {
+ const auto expression = cast<ExpressionStatement *>(publicMember->statement);
+ if (const auto idExpression = cast<IdentifierExpression *>(expression->expression))
+ typeName = idExpression->name;
+ }
+ MetaProperty prop {
+ publicMember->name.toString(),
+ typeName.toString(),
+ false,
+ false,
+ false,
+ isAlias,
+ 0
+ };
+ currentObject()->addProperty(prop);
+ break;
+ }
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(UiSourceElement *sourceElement)
+{
+ if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
+ MetaMethod method;
+ method.setMethodName(fexpr->name.toString());
+ method.setMethodType(MetaMethod::Method);
+ FormalParameterList *parameters = fexpr->formals;
+ while (parameters) {
+ method.addParameter(parameters->element->bindingIdentifier.toString(), "");
+ parameters = parameters->next;
+ }
+ currentObject()->addMethod(method);
+ } else if (ClassExpression *clexpr = sourceElement->sourceElement->asClassDefinition()) {
+ MetaProperty prop { clexpr->name.toString(), "", false, false, false, false, 1 };
+ currentObject()->addProperty(prop);
+ } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
+ // nothing to do
+ } else {
+ const auto loc = sourceElement->firstSourceLocation();
+ m_colorOut->writeUncolored(
+ "unsupportedd sourceElement at "
+ + QString::fromLatin1("%1:%2: ").arg(loc.startLine).arg(loc.startColumn)
+ + QString::number(sourceElement->sourceElement->kind));
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(UiScriptBinding *scriptBinding)
+{
+ if (scriptBinding->qualifiedId->name == QLatin1String("id")) {
+ const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
+ const auto *idExprension = cast<IdentifierExpression *>(statement->expression);
+ m_objects.insert(idExprension->name.toString(), currentObject());
+ }
+ return true;
+}
+
+void ImportedMembersVisitor::throwRecursionDepthError()
+{
+ m_colorOut->write(QStringLiteral("Error"), Error);
+ m_colorOut->write(QStringLiteral("Maximum statement or expression depth exceeded"), Error);
+}
diff --git a/tools/qmllint/importedmembersvisitor.h b/tools/qmllint/importedmembersvisitor.h
new file mode 100644
index 0000000000..793ff18dcc
--- /dev/null
+++ b/tools/qmllint/importedmembersvisitor.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 IMPORTEDMEMBERSVISITOR_H
+#define IMPORTEDMEMBERSVISITOR_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 "qcoloroutput.h"
+
+#include <private/qqmljsast_p.h>
+
+class ImportedMembersVisitor : public QQmlJS::AST::Visitor
+{
+public:
+ ImportedMembersVisitor(ColorOutput *colorOut) :
+ m_colorOut(colorOut)
+ {}
+
+ ScopeTree *result(const QString &scopeName) const;
+
+private:
+ bool visit(QQmlJS::AST::UiObjectDefinition *) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
+ bool visit(QQmlJS::AST::UiPublicMember *) override;
+ bool visit(QQmlJS::AST::UiSourceElement *) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *) override;
+ void throwRecursionDepthError() override;
+
+ ScopeTree::Ptr currentObject() const { return m_currentObjects.back(); }
+
+ QVector<ScopeTree::Ptr> m_currentObjects;
+ ScopeTree::ConstPtr m_rootObject;
+ QHash<QString, ScopeTree::Ptr> m_objects;
+
+ ColorOutput *m_colorOut = nullptr;
+};
+
+#endif // IMPORTEDMEMBERSVISITOR_H
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 235ec16c6e..05519fc8d8 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -26,31 +26,34 @@
**
****************************************************************************/
-#include <QDebug>
-#include <QFile>
-#include <QFileInfo>
+#include "findunqualified.h"
+
+#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 <QCommandLineParser>
+#include <QtCore/qcommandlineparser.h>
#endif
-#include <QCoreApplication>
#ifndef QT_BOOTSTRAPPED
-#include <QLibraryInfo>
+#include <QtCore/qlibraryinfo.h>
#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"
-
-static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied, QStringList const &qmltypeDirs)
+static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied,
+ const QStringList &qmltypeDirs, const QStringList &qmltypeFiles)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
- qWarning() << "Failed to open file" << filename << file.error();
+ if (!silent)
+ qWarning() << "Failed to open file" << filename << file.error();
return false;
}
@@ -61,22 +64,27 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
QQmlJS::Lexer lexer(&engine);
QFileInfo info(filename);
- bool isJavaScript = info.suffix().toLower() == QLatin1String("js");
- lexer.setCode(code, /*line = */ 1, /*qmlMode=*/ !isJavaScript);
+ const QString lowerSuffix = info.suffix().toLower();
+ const bool isESModule = lowerSuffix == QLatin1String("mjs");
+ const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
+
+ lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/ !isJavaScript);
QQmlJS::Parser parser(&engine);
- bool success = isJavaScript ? parser.parseProgram() : parser.parse();
+ bool success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram())
+ : parser.parse();
if (!success && !silent) {
const auto diagnosticMessages = parser.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.loc.startLine).arg(m.message);
}
}
if (success && !isJavaScript && warnUnqualied) {
auto root = parser.rootNode();
- FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename};
+ FindUnqualifiedIDVisitor v { qmltypeDirs, qmltypeFiles, code, filename, silent };
root->accept(&v);
success = v.check();
}
@@ -95,10 +103,12 @@ int main(int argv, char *argc[])
parser.addHelpOption();
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);
- 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);
QCommandLineOption qmltypesDirsOption(
@@ -108,7 +118,15 @@ int main(int argv, char *argc[])
QLatin1String("directory"));
parser.addOption(qmltypesDirsOption);
- parser.addPositionalArgument(QLatin1String("files"), QLatin1String("list of qml or js files to verify"));
+ QCommandLineOption qmltypesFilesOption(
+ QStringList() << "i"
+ << "qmltypes",
+ QLatin1String("Include the specified qmltypes files"),
+ QLatin1String("qmltypes"));
+ parser.addOption(qmltypesFilesOption);
+
+ parser.addPositionalArgument(QLatin1String("files"),
+ QLatin1String("list of qml or js files to verify"));
parser.process(app);
@@ -120,16 +138,23 @@ int main(int argv, char *argc[])
bool silent = parser.isSet(silentOption);
bool warnUnqualified = parser.isSet(checkUnqualified);
// use host qml import path as a sane default if nothing else has been provided
- QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption) ? parser.values(qmltypesDirsOption)
-#ifndef QT_BOOTSTRAPPED
- : QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)};
-#else
- : QStringList{};
-#endif
+ QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption)
+ ? parser.values(qmltypesDirsOption)
+# ifndef QT_BOOTSTRAPPED
+ : QStringList { QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) };
+# else
+ : QStringList {};
+# endif
+
+ if (!parser.isSet(qmltypesFilesOption))
+ qmltypeDirs << ".";
+
+ QStringList qmltypeFiles = parser.isSet(qmltypesFilesOption) ? parser.values(qmltypesFilesOption) : QStringList {};
#else
bool silent = false;
bool warnUnqualified = false;
QStringList qmltypeDirs {};
+ QStringList qmltypeFiles {};
#endif
bool success = true;
#if QT_CONFIG(commandlineparser)
@@ -138,7 +163,7 @@ int main(int argv, char *argc[])
const auto arguments = app.arguments();
for (const QString &filename : arguments)
#endif
- success &= lint_file(filename, silent, warnUnqualified, qmltypeDirs);
+ success &= lint_file(filename, silent, warnUnqualified, qmltypeDirs, qmltypeFiles);
return success ? 0 : -1;
}
diff --git a/tools/qmllint/metatypes.h b/tools/qmllint/metatypes.h
new file mode 100644
index 0000000000..eb832c27ba
--- /dev/null
+++ b/tools/qmllint/metatypes.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** 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
+{
+ QStringList m_keys;
+ QString m_name;
+ QString m_alias;
+ bool m_isFlag = false;
+
+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; }
+
+ QString alias() const { return m_alias; }
+ void setAlias(const QString &alias) { m_alias = alias; }
+
+ bool isFlag() const { return m_isFlag; }
+ void setIsFlag(bool isFlag) { m_isFlag = isFlag; }
+
+ void addKey(const QString &key) { m_keys.append(key); }
+ QStringList keys() const { return m_keys; }
+};
+
+class MetaMethod
+{
+public:
+ enum Type {
+ Signal,
+ Slot,
+ Method
+ };
+
+ enum Access {
+ Private,
+ Protected,
+ Public
+ };
+
+ MetaMethod() = default;
+ explicit MetaMethod(QString name, QString returnType = QString())
+ : m_name(std::move(name))
+ , m_returnType(std::move(returnType))
+ , m_methodType(Method)
+ , m_methodAccess(Public)
+ {}
+
+ QString methodName() const { return m_name; }
+ void setMethodName(const QString &name) { m_name = name; }
+
+ void setReturnType(const QString &type) { m_returnType = type; }
+
+ QStringList parameterNames() const { return m_paramNames; }
+ QStringList parameterTypes() const { return m_paramTypes; }
+ void addParameter(const QString &name, const QString &type)
+ {
+ m_paramNames.append(name);
+ m_paramTypes.append(type);
+ }
+
+ int methodType() const { return m_methodType; }
+ void setMethodType(Type methodType) { m_methodType = methodType; }
+
+ Access access() const { return m_methodAccess; }
+
+ int revision() const { return m_revision; }
+ void setRevision(int r) { m_revision = r; }
+
+private:
+ QString m_name;
+ QString m_returnType;
+ QStringList m_paramNames;
+ QStringList m_paramTypes;
+ Type m_methodType = Signal;
+ Access m_methodAccess = Private;
+ int m_revision = 0;
+};
+
+class ScopeTree;
+class MetaProperty
+{
+ QString m_propertyName;
+ QString m_typeName;
+ const ScopeTree *m_type = nullptr;
+ bool m_isList;
+ bool m_isWritable;
+ bool m_isPointer;
+ bool m_isAlias;
+ int m_revision;
+
+public:
+ MetaProperty(QString propertyName, QString typeName,
+ bool isList, bool isWritable, bool isPointer, bool isAlias,
+ int revision)
+ : m_propertyName(std::move(propertyName))
+ , m_typeName(std::move(typeName))
+ , m_isList(isList)
+ , m_isWritable(isWritable)
+ , m_isPointer(isPointer)
+ , m_isAlias(isAlias)
+ , m_revision(revision)
+ {}
+
+ QString propertyName() const { return m_propertyName; }
+ QString typeName() const { return m_typeName; }
+
+ void setType(const ScopeTree *type) { m_type = type; }
+ const ScopeTree *type() const { return m_type; }
+
+ bool isList() const { return m_isList; }
+ bool isWritable() const { return m_isWritable; }
+ bool isPointer() const { return m_isPointer; }
+ bool isAlias() const { return m_isAlias; }
+ int revision() const { return m_revision; }
+};
+
+#endif // METATYPES_H
diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp
index d2e723700a..84bd22c428 100644
--- a/tools/qmllint/qcoloroutput.cpp
+++ b/tools/qmllint/qcoloroutput.cpp
@@ -26,71 +26,76 @@
**
****************************************************************************/
-#include <QFile>
-#include <QHash>
-#include <QTextCodec>
+#include "qcoloroutput.h"
+
+#include <QtCore/qfile.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qtextcodec.h>
#ifndef Q_OS_WIN
#include <unistd.h>
#endif
-#include "qcoloroutput_p.h"
-
class ColorOutputPrivate
{
public:
- ColorOutputPrivate() : currentColorID(-1)
-
+ ColorOutputPrivate(bool silent) : m_currentColorID(-1), m_silent(silent)
{
- /* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance
- * is considered of lower priority.
+ /* - QIODevice::Unbuffered because we want it to appear when the user actually calls,
+ * performance is considered of lower priority.
*/
m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
-
- coloringEnabled = isColoringPossible();
+ m_coloringEnabled = isColoringPossible();
}
- ColorOutput::ColorMapping colorMapping;
- int currentColorID;
- bool coloringEnabled;
-
static const char *const foregrounds[];
static const char *const backgrounds[];
- inline void write(const QString &msg)
- {
- m_out.write(msg.toLocal8Bit());
- }
+ inline void write(const QString &msg) { m_out.write(msg.toLocal8Bit()); }
static QString escapeCode(const QString &in)
{
+ const ushort escapeChar = 0x1B;
QString result;
- result.append(QChar(0x1B));
+ result.append(QChar(escapeChar));
result.append(QLatin1Char('['));
result.append(in);
result.append(QLatin1Char('m'));
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:
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.
*/
inline bool isColoringPossible() const
{
-# if defined(Q_OS_WIN)
- /* Windows doesn't at all support ANSI escape codes, unless
- * the user install a "device driver". See the Wikipedia links in the
- * class documentation for details. */
- return false;
-# else
- /* 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
- * of abstraction helps handle such cases. */
- return isatty(m_out.handle());
-# endif
+#if defined(Q_OS_WIN)
+ /* Windows doesn't at all support ANSI escape codes, unless
+ * the user install a "device driver". See the Wikipedia links in the
+ * class documentation for details. */
+ return false;
+#else
+ /* 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
+ * of abstraction helps handle such cases. */
+ return isatty(m_out.handle());
+#endif
}
};
@@ -127,7 +132,6 @@ const char *const ColorOutputPrivate::backgrounds[] =
/*!
\class ColorOutput
- \since 4.4
\nonreentrant
\brief Outputs colored messages to \c stderr.
\internal
@@ -214,40 +218,17 @@ const char *const ColorOutputPrivate::backgrounds[] =
*/
/*!
- 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.
*/
-ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
-{
-}
+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
is initialized to not color at all.
@@ -258,7 +239,8 @@ ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer ha
*/
void ColorOutput::write(const QString &message, int colorID)
{
- d->write(colorify(message, colorID));
+ if (!d->isSilent())
+ d->write(colorify(message, colorID));
}
/*!
@@ -269,7 +251,8 @@ void ColorOutput::write(const QString &message, int colorID)
*/
void ColorOutput::writeUncolored(const QString &message)
{
- d->write(message + QLatin1Char('\n'));
+ if (!d->isSilent())
+ d->write(message + QLatin1Char('\n'));
}
/*!
@@ -282,61 +265,56 @@ void ColorOutput::writeUncolored(const QString &message)
*/
QString ColorOutput::colorify(const QString &message, int colorID) const
{
- Q_ASSERT_X(colorID == -1 || d->colorMapping.contains(colorID), Q_FUNC_INFO,
- qPrintable(QString::fromLatin1("There is no color registered by id %1").arg(colorID)));
- Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO, "It makes no sense to attempt to print an empty string.");
+ Q_ASSERT_X(colorID == -1 || d->containsColor(colorID), Q_FUNC_INFO,
+ qPrintable(QString::fromLatin1("There is no color registered by id %1")
+ .arg(colorID)));
+ Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO,
+ "It makes no sense to attempt to print an empty string.");
if (colorID != -1)
- d->currentColorID = colorID;
+ d->setCurrentColorID(colorID);
- if (d->coloringEnabled && colorID != -1)
- {
- const int color(d->colorMapping.value(colorID));
+ if (d->coloringEnabled() && colorID != -1) {
+ const int color = d->color(colorID);
/* If DefaultColor is set, we don't want to color it. */
if (color & DefaultColor)
return message;
- const int foregroundCode = (int(color) & ForegroundMask) >> ForegroundShift;
- const int backgroundCode = (int(color) & BackgroundMask) >> BackgroundShift;
+ const int foregroundCode = (color & ForegroundMask) >> ForegroundShift;
+ const int backgroundCode = (color & BackgroundMask) >> BackgroundShift;
QString finalMessage;
bool closureNeeded = false;
- if (foregroundCode)
- {
- finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
+ if (foregroundCode > 0) {
+ finalMessage.append(
+ ColorOutputPrivate::escapeCode(
+ QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
closureNeeded = true;
}
- if (backgroundCode)
- {
- finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
+ if (backgroundCode > 0) {
+ finalMessage.append(
+ ColorOutputPrivate::escapeCode(
+ QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
closureNeeded = true;
}
finalMessage.append(message);
if (closureNeeded)
- {
- finalMessage.append(QChar(0x1B));
- finalMessage.append(QLatin1String("[0m"));
- }
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String("0")));
return finalMessage;
}
- else
- return message;
+
+ return message;
}
/*!
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);
}
diff --git a/tools/qmllint/qcoloroutput_p.h b/tools/qmllint/qcoloroutput.h
index 710bf5db74..92f4b47ff0 100644
--- a/tools/qmllint/qcoloroutput_p.h
+++ b/tools/qmllint/qcoloroutput.h
@@ -26,6 +26,9 @@
**
****************************************************************************/
+#ifndef QCOLOROUTPUT_H
+#define QCOLOROUTPUT_H
+
//
// W A R N I N G
// -------------
@@ -36,12 +39,8 @@
//
// We mean it.
-#ifndef QCOLOROUTPUT_P_H
-#define QCOLOROUTPUT_P_H
-
-#include <QtCore/QtGlobal>
-#include <QtCore/QHash>
-#include <QScopedPointer>
+#include <QtCore/qglobal.h>
+#include <QtCore/qscopedpointer.h>
class ColorOutputPrivate;
@@ -86,15 +85,13 @@ public:
DefaultColor = 1 << SpecialShift
};
- typedef QFlags<ColorCodeComponent> ColorCode;
- typedef QHash<int, ColorCode> ColorMapping;
+ using ColorCode = QFlags<ColorCodeComponent>;
+ using ColorMapping = QHash<int, ColorCode>;
- ColorOutput();
+ ColorOutput(bool silent);
~ColorOutput();
- void setColorMapping(const ColorMapping &cMapping);
- ColorMapping colorMapping() const;
- void insertColorMapping(int colorID, const ColorCode colorCode);
+ void insertMapping(int colorID, ColorCode colorCode);
void writeUncolored(const QString &message);
void write(const QString &message, int color = -1);
@@ -102,9 +99,9 @@ public:
private:
QScopedPointer<ColorOutputPrivate> d;
- Q_DISABLE_COPY(ColorOutput)
+ Q_DISABLE_COPY_MOVE(ColorOutput)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(ColorOutput::ColorCode)
-#endif
+#endif // QCOLOROUTPUT_H
diff --git a/tools/qmllint/qmljstypedescriptionreader.h b/tools/qmllint/qmljstypedescriptionreader.h
deleted file mode 100644
index df215af8d2..0000000000
--- a/tools/qmllint/qmljstypedescriptionreader.h
+++ /dev/null
@@ -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
diff --git a/tools/qmllint/qmllint.pro b/tools/qmllint/qmllint.pro
index 76363a7cd8..4b7ca947cf 100644
--- a/tools/qmllint/qmllint.pro
+++ b/tools/qmllint/qmllint.pro
@@ -1,14 +1,14 @@
option(host_build)
-QT = core qmldevtools-private
+QT = core-private qmldevtools-private
SOURCES += main.cpp \
componentversion.cpp \
- fakemetaobject.cpp \
findunqualified.cpp \
- qmljstypedescriptionreader.cpp \
+ importedmembersvisitor.cpp \
qcoloroutput.cpp \
- scopetree.cpp
+ scopetree.cpp \
+ typedescriptionreader.cpp
QMAKE_TARGET_DESCRIPTION = QML Syntax Verifier
@@ -16,8 +16,9 @@ load(qt_tool)
HEADERS += \
componentversion.h \
- fakemetaobject.h \
findunqualified.h \
- qmljstypedescriptionreader.h \
- qcoloroutput_p.h \
- scopetree.h
+ importedmembersvisitor.h \
+ metatypes.h \
+ qcoloroutput.h \
+ scopetree.h \
+ typedescriptionreader.h
diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp
index 2eff3fa319..c47dac3df5 100644
--- a/tools/qmllint/scopetree.cpp
+++ b/tools/qmllint/scopetree.cpp
@@ -27,28 +27,27 @@
****************************************************************************/
#include "scopetree.h"
+#include "qcoloroutput.h"
-#include "qcoloroutput_p.h"
+#include <QtCore/qqueue.h>
#include <algorithm>
-#include <QQueue>
-
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) {
- 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};
+ScopeTree::Ptr ScopeTree::createNewChildScope(ScopeType type, const QString &name)
+{
+ Q_ASSERT(type != ScopeType::QMLScope
+ || !m_parentScope
+ || m_parentScope->m_scopeType == ScopeType::QMLScope
+ || m_parentScope->m_name == "global");
+ auto childScope = ScopeTree::Ptr(new ScopeTree{type, name, this});
m_childScopes.push_back(childScope);
return childScope;
}
-ScopeTree *ScopeTree::parentScope() {
- return m_parentScope;
-}
-
-void ScopeTree::insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope)
+void ScopeTree::insertJSIdentifier(const QString &id, QQmlJS::AST::VariableScope scope)
{
Q_ASSERT(m_scopeType != ScopeType::QMLScope);
if (scope == QQmlJS::AST::VariableScope::Var) {
@@ -56,29 +55,31 @@ void ScopeTree::insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope)
while (targetScope->scopeType() != ScopeType::JSFunctionScope) {
targetScope = targetScope->m_parentScope;
}
- targetScope->m_currentScopeJSIdentifiers.insert(id);
+ targetScope->m_jsIdentifiers.insert(id);
} 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::SourceLocation &loc,
+ bool hasMultilineHandlerBody)
{
Q_ASSERT(m_scopeType == ScopeType::QMLScope);
- m_currentScopeQMLIdentifiers.insert(id);
+ m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
}
-void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody)
+void ScopeTree::insertPropertyIdentifier(const MetaProperty &property)
{
- Q_ASSERT(m_scopeType == ScopeType::QMLScope);
- m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
+ addProperty(property);
+ MetaMethod method(property.propertyName() + QLatin1String("Changed"), "void");
+ addMethod(method);
}
-void ScopeTree::insertPropertyIdentifier(QString id)
+void ScopeTree::addUnmatchedSignalHandler(const QString &handler,
+ const QQmlJS::SourceLocation &location)
{
- this->insertQMLIdentifier(id);
- LanguageUtils::FakeMetaMethod method( id + QLatin1String("Changed"), "void");
- this->addMethod(method);
+ m_unmatchedSignalHandlers.append(qMakePair(handler, location));
}
bool ScopeTree::isIdInCurrentScope(const QString &id) const
@@ -86,184 +87,440 @@ bool ScopeTree::isIdInCurrentScope(const QString &id) const
return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
}
-void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair, const QSet<QString>& unknownImports) {
- // 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
- // 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
- // which makes for a very nonsensical warning
- auto qmlScope = getCurrentQMLScope();
- if (!isIdInCurrentScope(id_loc_pair.first) && !(id_loc_pair.first == QLatin1String("parent") && qmlScope && unknownImports.contains(qmlScope->name()))) {
- m_accessedIdentifiers.push_back(id_loc_pair);
- }
+void ScopeTree::addIdToAccessed(const QString &id, const QQmlJS::SourceLocation &location) {
+ m_currentFieldMember = new FieldMemberList {id, QString(), location, {}};
+ m_accessedIdentifiers.push_back(std::unique_ptr<FieldMemberList>(m_currentFieldMember));
}
-bool ScopeTree::isVisualRootScope() const
+void ScopeTree::accessMember(const QString &name, const QString &parentType,
+ const QQmlJS::SourceLocation &location)
+{
+ Q_ASSERT(m_currentFieldMember);
+ auto *fieldMember = new FieldMemberList {name, parentType, location, {}};
+ m_currentFieldMember->m_child.reset(fieldMember);
+ m_currentFieldMember = fieldMember;
+}
+
+void ScopeTree::resetMemberScope()
{
- return m_parentScope && m_parentScope->m_parentScope && m_parentScope->m_parentScope->m_parentScope == nullptr;
+ m_currentFieldMember = nullptr;
}
-QString ScopeTree::name() const
+bool ScopeTree::isVisualRootScope() const
{
- return m_name;
+ return m_parentScope && m_parentScope->m_parentScope
+ && m_parentScope->m_parentScope->m_parentScope == nullptr;
}
-struct IssueLocationWithContext
+class IssueLocationWithContext
{
- IssueLocationWithContext(const QString& code, QQmlJS::AST::SourceLocation location) {
+public:
+ IssueLocationWithContext(const QString &code, const QQmlJS::SourceLocation &location) {
int before = std::max(0,code.lastIndexOf('\n', location.offset));
- beforeText = code.midRef(before+1, location.offset - (before+1) );
- issueText = code.midRef(location.offset, location.length);
- int after = code.indexOf('\n', location.offset + location.length);
- afterText = code.midRef(location.offset+location.length, after - (location.offset+location.length));
+ m_beforeText = code.midRef(before + 1, int(location.offset - (before + 1)));
+ m_issueText = code.midRef(location.offset, location.length);
+ int after = code.indexOf('\n', int(location.offset + location.length));
+ m_afterText = code.midRef(int(location.offset + location.length),
+ int(after - (location.offset+location.length)));
}
- QStringRef beforeText;
- QStringRef issueText;
- QStringRef afterText;
+ QStringRef beforeText() const { return m_beforeText; }
+ QStringRef issueText() const { return m_issueText; }
+ QStringRef afterText() const { return m_afterText; }
+
+private:
+ QStringRef m_beforeText;
+ QStringRef m_issueText;
+ QStringRef m_afterText;
+};
+
+static const QStringList unknownBuiltins = {
+ // TODO: "string" should be added to builtins.qmltypes, and the special handling below removed
+ QStringLiteral("alias"), // TODO: we cannot properly resolve aliases, yet
+ QStringLiteral("QRectF"), // TODO: should be added to builtins.qmltypes
+ QStringLiteral("QFont"), // TODO: should be added to builtins.qmltypes
+ QStringLiteral("QJSValue"), // We cannot say anything intelligent about untyped JS values.
+ QStringLiteral("variant"), // Same for generic variants
};
-bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> &qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput& colorOut) const
+bool ScopeTree::checkMemberAccess(
+ const QString &code,
+ FieldMemberList *members,
+ const ScopeTree *scope,
+ const QHash<QString, ScopeTree::ConstPtr> &types,
+ ColorOutput& colorOut) const
+{
+ if (!members->m_child)
+ return true;
+
+ Q_ASSERT(scope != nullptr);
+
+ const QString scopeName = scope->name().isEmpty() ? scope->className() : scope->name();
+ const auto &access = members->m_child;
+
+ const auto scopeIt = scope->m_properties.find(access->m_name);
+ if (scopeIt != scope->m_properties.end()) {
+ const QString typeName = access->m_parentType.isEmpty() ? scopeIt->typeName()
+ : access->m_parentType;
+ if (scopeIt->isList() || typeName == QLatin1String("string")) {
+ if (access->m_child && access->m_child->m_name != QLatin1String("length")) {
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(
+ QString::fromLatin1(
+ "\"%1\" is a %2. You cannot access \"%3\" on it at %4:%5\n")
+ .arg(access->m_name)
+ .arg(QLatin1String(scopeIt->isList() ? "list" : "string"))
+ .arg(access->m_child->m_name)
+ .arg(access->m_child->m_location.startLine)
+ .arg(access->m_child->m_location.startColumn), Normal);
+ printContext(colorOut, code, access->m_child->m_location);
+ return false;
+ }
+ return true;
+ }
+
+ if (!access->m_child)
+ return true;
+
+ if (const ScopeTree *type = scopeIt->type()) {
+ if (access->m_parentType.isEmpty())
+ return checkMemberAccess(code, access.get(), type, types, colorOut);
+ }
+
+ if (unknownBuiltins.contains(typeName))
+ return true;
+
+ const auto it = types.find(typeName);
+ if (it != types.end())
+ return checkMemberAccess(code, access.get(), it->get(), types, colorOut);
+
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(
+ QString::fromLatin1("Type \"%1\" of member \"%2\" not found at %3:%4.\n")
+ .arg(typeName)
+ .arg(access->m_name)
+ .arg(access->m_location.startLine)
+ .arg(access->m_location.startColumn), Normal);
+ printContext(colorOut, code, access->m_location);
+ return false;
+ }
+
+ const auto scopeMethodIt = scope->m_methods.find(access->m_name);
+ if (scopeMethodIt != scope->m_methods.end())
+ return true; // Access to property of JS function
+
+ for (const auto enumerator : scope->m_enums) {
+ for (const QString &key : enumerator.keys()) {
+ if (access->m_name != key)
+ continue;
+
+ if (!access->m_child)
+ return true;
+
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(QString::fromLatin1(
+ "\"%1\" is an enum value. You cannot access \"%2\" on it at %3:%4\n")
+ .arg(access->m_name)
+ .arg(access->m_child->m_name)
+ .arg(access->m_child->m_location.startLine)
+ .arg(access->m_child->m_location.startColumn), Normal);
+ printContext(colorOut, code, access->m_child->m_location);
+ return false;
+ }
+ }
+
+ auto type = types.value(scopeName);
+ while (type) {
+ const auto typeIt = type->m_properties.find(access->m_name);
+ if (typeIt != type->m_properties.end()) {
+ const ScopeTree *propType = access->m_parentType.isEmpty()
+ ? typeIt->type()
+ : types.value(access->m_parentType).get();
+ return checkMemberAccess(code, access.get(),
+ propType ? propType : types.value(typeIt->typeName()).get(),
+ types, colorOut);
+ }
+
+ const auto typeMethodIt = type->m_methods.find(access->m_name);
+ if (typeMethodIt != type->m_methods.end()) {
+ if (access->m_child == nullptr)
+ return true;
+
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(QString::fromLatin1(
+ "\"%1\" is a method. You cannot access \"%2\" on it at %3:%4\n")
+ .arg(access->m_name)
+ .arg(access->m_child->m_name)
+ .arg(access->m_child->m_location.startLine)
+ .arg(access->m_child->m_location.startColumn), Normal);
+ printContext(colorOut, code, access->m_child->m_location);
+ return false;
+ }
+
+ type = types.value(type->superclassName());
+ }
+
+ if (access->m_name.front().isUpper() && scope->scopeType() == ScopeType::QMLScope) {
+ // may be an attached type
+ const auto it = types.find(access->m_name);
+ if (it != types.end() && !(*it)->attachedTypeName().isEmpty()) {
+ const auto attached = types.find((*it)->attachedTypeName());
+ if (attached != types.end())
+ return checkMemberAccess(code, access.get(), attached->get(), types, colorOut);
+ }
+ }
+
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(QString::fromLatin1(
+ "Property \"%1\" not found on type \"%2\" at %3:%4\n")
+ .arg(access->m_name)
+ .arg(scopeName)
+ .arg(access->m_location.startLine)
+ .arg(access->m_location.startColumn), Normal);
+ printContext(colorOut, code, access->m_location);
+ return false;
+}
+
+bool ScopeTree::recheckIdentifiers(
+ const QString &code,
+ const QHash<QString, const ScopeTree *> &qmlIDs,
+ const QHash<QString, ScopeTree::ConstPtr> &types,
+ const ScopeTree *root, const QString &rootId,
+ ColorOutput& colorOut) const
{
bool noUnqualifiedIdentifier = true;
// revisit all scopes
- QQueue<const ScopeTree*> workQueue;
+ QQueue<const ScopeTree *> workQueue;
workQueue.enqueue(this);
while (!workQueue.empty()) {
- const ScopeTree* currentScope = workQueue.dequeue();
- for (auto idLocationPair : currentScope->m_accessedIdentifiers) {
- if (qmlIDs.contains(idLocationPair.first))
+ const ScopeTree *currentScope = workQueue.dequeue();
+ for (const auto &handler : currentScope->m_unmatchedSignalHandlers) {
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(QString::fromLatin1(
+ "no matching signal found for handler \"%1\" at %2:%3\n")
+ .arg(handler.first).arg(handler.second.startLine)
+ .arg(handler.second.startColumn), Normal);
+ printContext(colorOut, code, handler.second);
+ }
+
+ for (const auto &memberAccessTree : qAsConst(currentScope->m_accessedIdentifiers)) {
+ if (currentScope->isIdInCurrentJSScopes(memberAccessTree->m_name))
continue;
- if (currentScope->isIdInCurrentScope(idLocationPair.first)) {
+
+ auto it = qmlIDs.find(memberAccessTree->m_name);
+ if (it != qmlIDs.end()) {
+ if (*it != nullptr) {
+ if (!checkMemberAccess(code, memberAccessTree.get(), *it, types, colorOut))
+ noUnqualifiedIdentifier = false;
+ continue;
+ } else if (memberAccessTree->m_child
+ && memberAccessTree->m_child->m_name.front().isUpper()) {
+ // It could be a qualified type name
+ const QString qualified = memberAccessTree->m_name + QLatin1Char('.')
+ + memberAccessTree->m_child->m_name;
+ const auto typeIt = types.find(qualified);
+ if (typeIt != types.end()) {
+ if (!checkMemberAccess(code, memberAccessTree->m_child.get(), typeIt->get(),
+ types, colorOut)) {
+ noUnqualifiedIdentifier = false;
+ }
+ continue;
+ }
+ }
+ }
+
+ auto qmlScope = currentScope->currentQMLScope();
+ if (qmlScope->methods().contains(memberAccessTree->m_name)) {
+ // a property of a JavaScript function
+ continue;
+ }
+
+ const auto qmlIt = qmlScope->m_properties.find(memberAccessTree->m_name);
+ if (qmlIt != qmlScope->m_properties.end()) {
+ if (!memberAccessTree->m_child || unknownBuiltins.contains(qmlIt->typeName()))
+ continue;
+
+ if (!qmlIt->type()) {
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(QString::fromLatin1(
+ "Type of property \"%2\" not found at %3:%4\n")
+ .arg(memberAccessTree->m_name)
+ .arg(memberAccessTree->m_location.startLine)
+ .arg(memberAccessTree->m_location.startColumn), Normal);
+ printContext(colorOut, code, memberAccessTree->m_location);
+ noUnqualifiedIdentifier = false;
+ } else if (!checkMemberAccess(code, memberAccessTree.get(), qmlIt->type(), types,
+ colorOut)) {
+ noUnqualifiedIdentifier = false;
+ }
+
+ continue;
+ }
+
+ // TODO: Lots of builtins are missing
+ if (memberAccessTree->m_name == "Qt")
+ continue;
+
+ const auto typeIt = types.find(memberAccessTree->m_name);
+ if (typeIt != types.end()) {
+ if (!checkMemberAccess(code, memberAccessTree.get(), typeIt->get(), types,
+ colorOut)) {
+ noUnqualifiedIdentifier = false;
+ }
continue;
}
+
noUnqualifiedIdentifier = false;
colorOut.write("Warning: ", Warning);
- auto location = idLocationPair.second;
- colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine, location.startColumn), Normal);
- IssueLocationWithContext issueLocationWithContext {code, location};
- colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
- colorOut.write(issueLocationWithContext.issueText.toString(), Error);
- colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal);
- int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t'));
- colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount) + QString("\t").repeated(tabCount) + QString("^").repeated(location.length) + QLatin1Char('\n'), Normal);
+ auto location = memberAccessTree->m_location;
+ colorOut.write(QString::fromLatin1("unqualified access at %1:%2\n")
+ .arg(location.startLine).arg(location.startColumn),
+ Normal);
+
+ printContext(colorOut, code, location);
+
// root(JS) --> program(qml) --> (first element)
- if (root->m_childScopes[0]->m_childScopes[0]->m_currentScopeQMLIdentifiers.contains(idLocationPair.first)) {
- ScopeTree *parentScope = currentScope->m_parentScope;
- while (parentScope && parentScope->scopeType() != ScopeType::QMLScope) {
- parentScope = parentScope->m_parentScope;
- }
+ const auto firstElement = root->m_childScopes[0]->m_childScopes[0];
+ if (firstElement->m_properties.contains(memberAccessTree->m_name)
+ || firstElement->m_methods.contains(memberAccessTree->m_name)
+ || firstElement->m_enums.contains(memberAccessTree->m_name)) {
colorOut.write("Note: ", Info);
- colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal );
+ colorOut.write(memberAccessTree->m_name + QLatin1String(" is a member of the root element\n"), Normal );
colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal);
if (rootId == QLatin1String("<id>")) {
colorOut.write("Note: ", Warning);
colorOut.write(("You first have to give the root element an id\n"));
}
- colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
+ IssueLocationWithContext issueLocationWithContext {code, location};
+ colorOut.write(issueLocationWithContext.beforeText().toString(), Normal);
colorOut.write(rootId + QLatin1Char('.'), Hint);
- colorOut.write(issueLocationWithContext.issueText.toString(), Normal);
- colorOut.write(issueLocationWithContext.afterText + QLatin1Char('\n'), Normal);
- } else if (currentScope->isIdInjectedFromSignal(idLocationPair.first)) {
- auto qmlScope = currentScope->getCurrentQMLScope();
- auto methodUsages = qmlScope->m_injectedSignalIdentifiers.values(idLocationPair.first);
- auto location = idLocationPair.second;
+ colorOut.write(issueLocationWithContext.issueText().toString(), Normal);
+ colorOut.write(issueLocationWithContext.afterText() + QLatin1Char('\n'), Normal);
+ } else if (currentScope->isIdInjectedFromSignal(memberAccessTree->m_name)) {
+ auto methodUsages = currentScope->currentQMLScope()->m_injectedSignalIdentifiers
+ .values(memberAccessTree->m_name);
+ auto location = memberAccessTree->m_location;
// 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
// 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) {
- return m1.loc.startLine < m2.loc.startLine || (m1.loc.startLine == m2.loc.startLine && m1.loc.startColumn < m2.loc.startColumn);
+ std::sort(methodUsages.begin(), methodUsages.end(),
+ [](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) {
- return location.startLine < methodUsage.loc.startLine || (location.startLine == methodUsage.loc.startLine && location.startColumn < methodUsage.loc.startColumn);
+ auto oneBehindIt = std::find_if(methodUsages.begin(), methodUsages.end(),
+ [&location](const MethodUsage &methodUsage) {
+ return location.startLine < methodUsage.loc.startLine
+ || (location.startLine == methodUsage.loc.startLine
+ && location.startColumn < methodUsage.loc.startColumn);
});
auto methodUsage = *(--oneBehindIt);
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(
+ memberAccessTree->m_name + 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);
IssueLocationWithContext context {code, methodUsage.loc};
- colorOut.write(context.beforeText + QLatin1Char(' '));
+ colorOut.write(context.beforeText() + QLatin1Char(' '));
colorOut.write(methodUsage.hasMultilineHandlerBody ? "function(" : "(", Hint);
const auto parameters = methodUsage.method.parameterNames();
for (int numParams = parameters.size(); numParams > 0; --numParams) {
colorOut.write(parameters.at(parameters.size() - numParams), Hint);
- if (numParams > 1) {
+ if (numParams > 1)
colorOut.write(", ", Hint);
- }
}
colorOut.write(methodUsage.hasMultilineHandlerBody ? ")" : ") => ", Hint);
colorOut.write(" {...", Normal);
}
colorOut.write("\n\n\n", Normal);
}
- for (auto const& childScope: currentScope->m_childScopes) {
- workQueue.enqueue(childScope);
- }
+ for (auto const &childScope: currentScope->m_childScopes)
+ workQueue.enqueue(childScope.get());
}
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
-{
- auto qmlScope = getCurrentQMLScope();
- return qmlScope->m_currentScopeQMLIdentifiers.contains(id);
-}
-
-bool ScopeTree::isIdInCurrentJSScopes(QString id) const
+bool ScopeTree::isIdInCurrentJSScopes(const QString &id) const
{
auto jsScope = this;
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;
jsScope = jsScope->m_parentScope;
}
return false;
}
-bool ScopeTree::isIdInjectedFromSignal(QString id) const
+bool ScopeTree::isIdInjectedFromSignal(const QString &id) const
{
- auto qmlScope = getCurrentQMLScope();
- return qmlScope->m_injectedSignalIdentifiers.contains(id);
+ return currentQMLScope()->m_injectedSignalIdentifiers.contains(id);
}
-const ScopeTree *ScopeTree::getCurrentQMLScope() const
+const ScopeTree *ScopeTree::currentQMLScope() const
{
auto qmlScope = this;
- while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope)
qmlScope = qmlScope->m_parentScope;
- }
return qmlScope;
}
-ScopeTree *ScopeTree::getCurrentQMLScope()
+void ScopeTree::printContext(ColorOutput &colorOut, const QString &code,
+ const QQmlJS::SourceLocation &location) const
{
- auto qmlScope = this;
- while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
- qmlScope = qmlScope->m_parentScope;
- }
- return qmlScope;
+ IssueLocationWithContext issueLocationWithContext {code, location};
+ colorOut.write(issueLocationWithContext.beforeText().toString(), Normal);
+ colorOut.write(issueLocationWithContext.issueText().toString(), Error);
+ colorOut.write(issueLocationWithContext.afterText().toString() + QLatin1Char('\n'), Normal);
+ int tabCount = issueLocationWithContext.beforeText().count(QLatin1Char('\t'));
+ colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText().length() - tabCount)
+ + QString("\t").repeated(tabCount)
+ + QString("^").repeated(location.length)
+ + QLatin1Char('\n'), Normal);
}
-ScopeType ScopeTree::scopeType() {return m_scopeType;}
+void ScopeTree::addExport(const QString &name, const QString &package,
+ const ComponentVersion &version)
+{
+ m_exports.append(Export(package, name, version, 0));
+}
-void ScopeTree::addMethod(LanguageUtils::FakeMetaMethod method)
+void ScopeTree::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
{
- m_methods.insert(method.methodName(), method);
+ m_exports[exportIndex].setMetaObjectRevision(metaObjectRevision);
}
-void ScopeTree::addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject)
+void ScopeTree::updateParentProperty(const ScopeTree *scope)
{
- if (metaObject) {
- auto methodCount = metaObject->methodCount();
- for (auto i = 0; i < methodCount; ++i) {
- auto method = metaObject->method(i);
- this->addMethod(method);
- }
- }
+ auto it = m_properties.find(QLatin1String("parent"));
+ if (it != m_properties.end()
+ && scope->name() != QLatin1String("Component")
+ && scope->name() != QLatin1String("program"))
+ it->setType(scope);
+}
+
+ScopeTree::Export::Export(QString package, QString type, const ComponentVersion &version,
+ int metaObjectRevision) :
+ m_package(std::move(package)),
+ m_type(std::move(type)),
+ m_version(version),
+ m_metaObjectRevision(metaObjectRevision)
+{
+}
+
+bool ScopeTree::Export::isValid() const
+{
+ return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
}
diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h
index 872a509123..63f4310bf8 100644
--- a/tools/qmllint/scopetree.h
+++ b/tools/qmllint/scopetree.h
@@ -29,15 +29,28 @@
#ifndef SCOPETREE_H
#define SCOPETREE_H
-#include "fakemetaobject.h"
-#include "private/qqmljsast_p.h"
-#include "private/qqmljssourcelocation_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 <QSet>
-#include <QString>
-#include <QMap>
+#include "metatypes.h"
+#include "componentversion.h"
-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,
Warning,
Info,
@@ -54,53 +67,157 @@ enum class ScopeType
struct MethodUsage
{
- LanguageUtils::FakeMetaMethod method;
- QQmlJS::AST::SourceLocation loc;
+ MetaMethod method;
+ QQmlJS::SourceLocation loc;
bool hasMultilineHandlerBody;
};
class ColorOutput;
-
-class ScopeTree {
+class ScopeTree
+{
+ Q_DISABLE_COPY_MOVE(ScopeTree)
public:
- ScopeTree(ScopeType type, QString name="<none given>", ScopeTree* parentScope=nullptr);
- ~ScopeTree() {qDeleteAll(m_childScopes);}
+ using Ptr = QSharedPointer<ScopeTree>;
+ using ConstPtr = QSharedPointer<const ScopeTree>;
+
+ class Export {
+ public:
+ Export() = default;
+ Export(QString package, QString type, const ComponentVersion &version,
+ int metaObjectRevision);
+
+ bool isValid() const;
+
+ int metaObjectRevision() const { return m_metaObjectRevision; }
+ void setMetaObjectRevision(int metaObjectRevision)
+ {
+ m_metaObjectRevision = metaObjectRevision;
+ }
- ScopeTree* createNewChildScope(ScopeType type, QString name);
- ScopeTree* parentScope();
+ QString package() const { return m_package; }
+ QString type() const { return m_type; }
- void insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope);
- void insertQMLIdentifier(QString id);
- void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody);
- void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding
+ private:
+ QString m_package;
+ QString m_type;
+ ComponentVersion m_version;
+ int m_metaObjectRevision = 0;
+ };
- bool isIdInCurrentScope(QString const &id) const;
- void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports);
+ ScopeTree(ScopeType type, QString name = QString(),
+ ScopeTree *parentScope = nullptr);
+
+ ScopeTree::Ptr createNewChildScope(ScopeType type, const QString &name);
+ ScopeTree *parentScope() const { return m_parentScope; }
+
+ void insertJSIdentifier(const QString &id, QQmlJS::AST::VariableScope scope);
+ void insertSignalIdentifier(const QString &id, const MetaMethod &method,
+ const QQmlJS::SourceLocation &loc, bool hasMultilineHandlerBody);
+ // inserts property as qml identifier as well as the corresponding
+ void insertPropertyIdentifier(const MetaProperty &prop);
+ void addUnmatchedSignalHandler(const QString &handler,
+ const QQmlJS::SourceLocation &location);
+
+ bool isIdInCurrentScope(const QString &id) const;
+ void addIdToAccessed(const QString &id, const QQmlJS::SourceLocation &location);
+ void accessMember(const QString &name, const QString &parentType,
+ const QQmlJS::SourceLocation &location);
+ void resetMemberScope();
bool isVisualRootScope() const;
- QString name() const;
+ QString name() const { return m_name; }
+
+ bool recheckIdentifiers(
+ const QString &code,
+ const QHash<QString, const ScopeTree *> &qmlIDs,
+ const QHash<QString, ScopeTree::ConstPtr> &types,
+ const ScopeTree *root, const QString& rootId, ColorOutput &colorOut) const;
+
+ ScopeType scopeType() const { return m_scopeType; }
+
+ void addMethods(const QHash<QString, MetaMethod> &methods) { m_methods.insert(methods); }
+ void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); }
+ QHash<QString, MetaMethod> methods() const { return m_methods; }
+
+ void addEnum(const MetaEnum &fakeEnum) { m_enums.insert(fakeEnum.name(), fakeEnum); }
+ QHash<QString, MetaEnum> enums() const { return m_enums; }
- bool recheckIdentifiers(const QString &code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr>& qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput &colorOut) const;
- ScopeType scopeType();
- void addMethod(LanguageUtils::FakeMetaMethod);
- void addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject);
- QMap<QString, LanguageUtils::FakeMetaMethod>const & methods() const;
+ QString className() const { return m_className; }
+ void setClassName(const QString &name) { m_className = name; }
+
+ void addExport(const QString &name, const QString &package, const ComponentVersion &version);
+ void setExportMetaObjectRevision(int exportIndex, int metaObjectRevision);
+ QList<Export> exports() const { return m_exports; }
+
+ void setSuperclassName(const QString &superclass) { m_superName = superclass; }
+ QString superclassName() const { return m_superName; }
+
+ void addProperty(const MetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); }
+ QHash<QString, MetaProperty> properties() const { return m_properties; }
+ void updateParentProperty(const ScopeTree *scope);
+
+ QString defaultPropertyName() const { return m_defaultPropertyName; }
+ void setDefaultPropertyName(const QString &name) { m_defaultPropertyName = name; }
+
+ QString attachedTypeName() const { return m_attachedTypeName; }
+ void setAttachedTypeName(const QString &name) { m_attachedTypeName = name; }
+
+ bool isSingleton() const { return m_isSingleton; }
+ bool isCreatable() const { return m_isCreatable; }
+ bool isComposite() const { return m_isComposite; }
+ void setIsSingleton(bool value) { m_isSingleton = value; }
+ void setIsCreatable(bool value) { m_isCreatable = value; }
+ void setIsComposite(bool value) { m_isSingleton = value; }
private:
- QSet<QString> m_currentScopeJSIdentifiers;
- QSet<QString> m_currentScopeQMLIdentifiers;
+ struct FieldMemberList
+ {
+ QString m_name;
+ QString m_parentType;
+ QQmlJS::SourceLocation m_location;
+ std::unique_ptr<FieldMemberList> m_child;
+ };
+
+ QSet<QString> m_jsIdentifiers;
QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers;
- QMap<QString, LanguageUtils::FakeMetaMethod> m_methods;
- QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers;
- QVector<ScopeTree*> m_childScopes;
+
+ QHash<QString, MetaMethod> m_methods;
+ QHash<QString, MetaProperty> m_properties;
+ QHash<QString, MetaEnum> m_enums;
+
+ std::vector<std::unique_ptr<FieldMemberList>> m_accessedIdentifiers;
+ FieldMemberList *m_currentFieldMember = nullptr;
+
+ QVector<QPair<QString, QQmlJS::SourceLocation>> m_unmatchedSignalHandlers;
+
+ QVector<ScopeTree::Ptr> m_childScopes;
ScopeTree *m_parentScope;
+
QString m_name;
- ScopeType m_scopeType;
+ QString m_className;
+ QString m_superName;
- bool isIdInCurrentQMlScopes(QString id) const;
- bool isIdInCurrentJSScopes(QString id) const;
- bool isIdInjectedFromSignal(QString id) const;
- const ScopeTree* getCurrentQMLScope() const;
- ScopeTree* getCurrentQMLScope();
+ 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::SourceLocation &location) const;
+ bool checkMemberAccess(
+ const QString &code,
+ FieldMemberList *members,
+ const ScopeTree *scope,
+ const QHash<QString, ScopeTree::ConstPtr> &types,
+ ColorOutput& colorOut) const;
};
+
#endif // SCOPETREE_H
diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/typedescriptionreader.cpp
index 542cdf99eb..12a1cc01d5 100644
--- a/tools/qmllint/qmljstypedescriptionreader.cpp
+++ b/tools/qmllint/typedescriptionreader.cpp
@@ -26,25 +26,18 @@
**
****************************************************************************/
-#include "qmljstypedescriptionreader.h"
+#include "typedescriptionreader.h"
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
-#include <QDir>
-
-#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
-#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
-#define QTC_ASSERT_STRING(cond) qDebug() << (\
- "\"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
-#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
+#include <QtCore/qdir.h>
using namespace QQmlJS;
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;
@@ -58,17 +51,8 @@ QString toString(const AST::UiQualifiedId *qualifiedId, QChar delimiter = QLatin
return result;
}
-TypeDescriptionReader::TypeDescriptionReader(const QString &fileName, const QString &data)
- : _fileName (fileName), _source(data), _objects(0)
-{
-}
-
-TypeDescriptionReader::~TypeDescriptionReader()
-{
-}
-
bool TypeDescriptionReader::operator()(
- QHash<QString, FakeMetaObject::ConstPtr> *objects,
+ QHash<QString, ScopeTree::ConstPtr> *objects,
QList<ModuleApiInfo> *moduleApis,
QStringList *dependencies)
{
@@ -77,32 +61,22 @@ bool TypeDescriptionReader::operator()(
Lexer lexer(&engine);
Parser parser(&engine);
- lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true);
+ lexer.setCode(m_source, /*lineno = */ 1, /*qmlMode = */true);
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.errorColumnNumber()),
parser.errorMessage());
return false;
}
- _objects = objects;
- _moduleApis = moduleApis;
- _dependencies = dependencies;
+ m_objects = objects;
+ m_moduleApis = moduleApis;
+ m_dependencies = dependencies;
readDocument(parser.ast());
- return _errorMessage.isEmpty();
-}
-
-QString TypeDescriptionReader::errorMessage() const
-{
- return _errorMessage;
-}
-
-QString TypeDescriptionReader::warningMessage() const
-{
- return _warningMessage;
+ return m_errorMessage.isEmpty();
}
void TypeDescriptionReader::readDocument(UiProgram *ast)
@@ -112,26 +86,25 @@ void TypeDescriptionReader::readDocument(UiProgram *ast)
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."));
return;
}
- UiImport *import = AST::cast<AST::UiImport *>(ast->headers->headerItem);
+ auto *import = cast<UiImport *>(ast->headers->headerItem);
if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
addError(import->importToken, tr("Expected import of QtQuick.tooling."));
return;
}
- ComponentVersion version;
- const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
- const int dotIdx = versionString.indexOf(QLatin1Char('.'));
- if (dotIdx != -1) {
- version = ComponentVersion(versionString.leftRef(dotIdx).toInt(),
- versionString.midRef(dotIdx + 1).toInt());
+ if (!import->version) {
+ addError(import->firstSourceLocation(), tr("Import statement without version."));
+ return;
}
- if (version.majorVersion() != 1) {
- addError(import->versionToken, tr("Major version different from 1 not supported."));
+
+ if (import->version->majorVersion != 1) {
+ addError(import->version->firstSourceLocation(),
+ tr("Major version different from 1 not supported."));
return;
}
@@ -140,7 +113,7 @@ void TypeDescriptionReader::readDocument(UiProgram *ast)
return;
}
- UiObjectDefinition *module = AST::cast<UiObjectDefinition *>(ast->members->member);
+ auto *module = cast<UiObjectDefinition *>(ast->members->member);
if (!module) {
addError(SourceLocation(), tr("Expected document to contain a single object definition."));
return;
@@ -158,9 +131,9 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
{
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
- UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ auto *component = cast<UiObjectDefinition *>(member);
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
readDependencies(script);
continue;
@@ -170,7 +143,8 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
if (component)
typeName = toString(component->qualifiedTypeNameId);
- if (!component || (typeName != QLatin1String("Component") && typeName != QLatin1String("ModuleApi"))) {
+ if (!component || (typeName != QLatin1String("Component")
+ && typeName != QLatin1String("ModuleApi"))) {
continue;
}
@@ -183,8 +157,8 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
{
- _errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
- QDir::toNativeSeparators(_fileName),
+ m_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(m_fileName),
QString::number(loc.startLine),
QString::number(loc.startColumn),
message);
@@ -192,8 +166,8 @@ void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &m
void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
{
- _warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
- QDir::toNativeSeparators(_fileName),
+ m_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(m_fileName),
QString::number(loc.startLine),
QString::number(loc.startColumn),
message);
@@ -201,83 +175,82 @@ void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString
void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
{
- ExpressionStatement *stmt = AST::cast<ExpressionStatement*>(ast->statement);
+ auto *stmt = cast<ExpressionStatement*>(ast->statement);
if (!stmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
return;
}
- ArrayPattern *exp = AST::cast<ArrayPattern *>(stmt->expression);
+ auto *exp = cast<ArrayPattern *>(stmt->expression);
if (!exp) {
addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
return;
}
for (PatternElementList *l = exp->elements; l; l = l->next) {
- //StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
- StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
- *_dependencies << str->value.toString();
+ auto *str = cast<StringLiteral *>(l->element->initializer);
+ *m_dependencies << str->value.toString();
}
}
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) {
UiObjectMember *member = it->member;
- UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *component = cast<UiObjectDefinition *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (component) {
QString name = toString(component->qualifiedTypeNameId);
if (name == QLatin1String("Property"))
- readProperty(component, fmo);
+ readProperty(component, scope);
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"))
- readEnum(component, fmo);
+ readEnum(component, scope);
else
addWarning(component->firstSourceLocation(),
- tr("Expected only Property, Method, Signal and Enum object definitions, not \"%1\".")
- .arg(name));
+ tr("Expected only Property, Method, Signal and Enum object definitions, "
+ "not \"%1\".").arg(name));
} else if (script) {
QString name = toString(script->qualifiedId);
if (name == QLatin1String("name")) {
- fmo->setClassName(readStringBinding(script));
+ scope->setClassName(readStringBinding(script));
} else if (name == QLatin1String("prototype")) {
- fmo->setSuperclassName(readStringBinding(script));
+ scope->setSuperclassName(readStringBinding(script));
} else if (name == QLatin1String("defaultProperty")) {
- fmo->setDefaultPropertyName(readStringBinding(script));
+ scope->setDefaultPropertyName(readStringBinding(script));
} else if (name == QLatin1String("exports")) {
- readExports(script, fmo);
+ readExports(script, scope);
} else if (name == QLatin1String("exportMetaObjectRevisions")) {
- readMetaObjectRevisions(script, fmo);
+ readMetaObjectRevisions(script, scope);
} else if (name == QLatin1String("attachedType")) {
- fmo->setAttachedTypeName(readStringBinding(script));
+ scope->setAttachedTypeName(readStringBinding(script));
} else if (name == QLatin1String("isSingleton")) {
- fmo->setIsSingleton(readBoolBinding(script));
+ scope->setIsSingleton(readBoolBinding(script));
} else if (name == QLatin1String("isCreatable")) {
- fmo->setIsCreatable(readBoolBinding(script));
+ scope->setIsCreatable(readBoolBinding(script));
} else if (name == QLatin1String("isComposite")) {
- fmo->setIsComposite(readBoolBinding(script));
+ scope->setIsComposite(readBoolBinding(script));
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name, prototype, defaultProperty, attachedType, exports, "
- "isSingleton, isCreatable, isComposite and exportMetaObjectRevisions "
- "script bindings, not \"%1\".").arg(name));
+ tr("Expected only name, prototype, defaultProperty, attachedType, "
+ "exports, isSingleton, isCreatable, isComposite and "
+ "exportMetaObjectRevisions script bindings, not \"%1\".").arg(name));
}
} else {
- addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ 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."));
return;
}
// ### add implicit export into the package of c++ types
- fmo->addExport(fmo->className(), QStringLiteral("<cpp>"), ComponentVersion());
- fmo->updateFingerprint();
- _objects->insert(fmo->className(), fmo);
+ scope->addExport(scope->className(), QStringLiteral("<cpp>"), ComponentVersion());
+ m_objects->insert(scope->className(), scope);
}
void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
@@ -286,7 +259,7 @@ void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (script) {
const QString name = toString(script->qualifiedId);
@@ -306,58 +279,65 @@ void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
}
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;
}
- if (_moduleApis)
- _moduleApis->append(apiInfo);
+ if (m_moduleApis)
+ 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.
if (isMethod)
- fmm.setMethodType(FakeMetaMethod::Slot);
+ metaMethod.setMethodType(MetaMethod::Slot);
else
- fmm.setMethodType(FakeMetaMethod::Signal);
+ metaMethod.setMethodType(MetaMethod::Signal);
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
- UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *component = cast<UiObjectDefinition *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (component) {
QString name = toString(component->qualifiedTypeNameId);
- if (name == QLatin1String("Parameter"))
- readParameter(component, &fmm);
- else
- addWarning(component->firstSourceLocation(), tr("Expected only Parameter object definitions."));
+ if (name == QLatin1String("Parameter")) {
+ readParameter(component, &metaMethod);
+ } else {
+ addWarning(component->firstSourceLocation(),
+ tr("Expected only Parameter object definitions."));
+ }
} else if (script) {
QString name = toString(script->qualifiedId);
- if (name == QLatin1String("name"))
- fmm.setMethodName(readStringBinding(script));
- else if (name == QLatin1String("type"))
- fmm.setReturnType(readStringBinding(script));
- else if (name == QLatin1String("revision"))
- fmm.setRevision(readIntBinding(script));
- else
- addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
-
+ if (name == QLatin1String("name")) {
+ metaMethod.setMethodName(readStringBinding(script));
+ } else if (name == QLatin1String("type")) {
+ metaMethod.setReturnType(readStringBinding(script));
+ } else if (name == QLatin1String("revision")) {
+ metaMethod.setRevision(readIntBinding(script));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name and type script bindings."));
+ }
} else {
- addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ addWarning(member->firstSourceLocation(),
+ tr("Expected only script bindings and object definitions."));
}
}
- if (fmm.methodName().isEmpty()) {
- addError(ast->firstSourceLocation(), tr("Method or signal is missing a name script binding."));
+ if (metaMethod.methodName().isEmpty()) {
+ addError(ast->firstSourceLocation(),
+ tr("Method or signal is missing a name script binding."));
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 type;
@@ -368,69 +348,79 @@ void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (!script) {
addWarning(member->firstSourceLocation(), tr("Expected script binding."));
continue;
}
QString id = toString(script->qualifiedId);
- if (id == QLatin1String("name"))
+ if (id == QLatin1String("name")) {
name = readStringBinding(script);
- else if (id == QLatin1String("type"))
+ } else if (id == QLatin1String("type")) {
type = readStringBinding(script);
- else if (id == QLatin1String("isPointer"))
+ } else if (id == QLatin1String("isPointer")) {
isPointer = readBoolBinding(script);
- else if (id == QLatin1String("isReadonly"))
+ } else if (id == QLatin1String("isReadonly")) {
isReadonly = readBoolBinding(script);
- else if (id == QLatin1String("isList"))
+ } else if (id == QLatin1String("isList")) {
isList = readBoolBinding(script);
- else if (id == QLatin1String("revision"))
+ } else if (id == QLatin1String("revision")) {
revision = readIntBinding(script);
- else
- addWarning(script->firstSourceLocation(), tr("Expected only type, name, revision, isPointer, isReadonly and isList script bindings."));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only type, name, revision, isPointer, isReadonly and"
+ " isList script bindings."));
+ }
}
if (name.isEmpty() || type.isEmpty()) {
- addError(ast->firstSourceLocation(), tr("Property object is missing a name or type script binding."));
+ addError(ast->firstSourceLocation(),
+ tr("Property object is missing a name or type script binding."));
return;
}
- fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision));
+ scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, false, 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) {
UiObjectMember *member = it->member;
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (!script) {
addWarning(member->firstSourceLocation(), tr("Expected script binding."));
continue;
}
QString name = toString(script->qualifiedId);
- if (name == QLatin1String("name"))
- fme.setName(readStringBinding(script));
- else if (name == QLatin1String("values"))
- readEnumValues(script, &fme);
- else
- addWarning(script->firstSourceLocation(), tr("Expected only name and values script bindings."));
+ if (name == QLatin1String("name")) {
+ metaEnum.setName(readStringBinding(script));
+ } else if (name == QLatin1String("alias")) {
+ metaEnum.setAlias(readStringBinding(script));
+ } else if (name == QLatin1String("isFlag")) {
+ metaEnum.setIsFlag(readBoolBinding(script));
+ } else if (name == QLatin1String("values")) {
+ readEnumValues(script, &metaEnum);
+ } else {
+ 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 type;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
- UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
if (!script) {
addWarning(member->firstSourceLocation(), tr("Expected script binding."));
continue;
@@ -448,29 +438,30 @@ void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMetho
} else if (id == QLatin1String("isList")) {
// ### unhandled
} 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)
{
- QTC_ASSERT(ast, return QString());
+ Q_ASSERT(ast);
if (!ast->statement) {
addError(ast->colonToken, tr("Expected string after colon."));
return QString();
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
return QString();
}
- StringLiteral *stringLit = AST::cast<StringLiteral *>(expStmt->expression);
+ auto *stringLit = cast<StringLiteral *>(expStmt->expression);
if (!stringLit) {
addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
return QString();
@@ -479,23 +470,23 @@ QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
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) {
addError(ast->colonToken, tr("Expected boolean after colon."));
return false;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
return false;
}
- TrueLiteral *trueLit = AST::cast<TrueLiteral *>(expStmt->expression);
- FalseLiteral *falseLit = AST::cast<FalseLiteral *>(expStmt->expression);
+ auto *trueLit = cast<TrueLiteral *>(expStmt->expression);
+ auto *falseLit = cast<FalseLiteral *>(expStmt->expression);
if (!trueLit && !falseLit) {
addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
return false;
@@ -504,22 +495,23 @@ bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
return trueLit;
}
-double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast)
+double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
{
- QTC_ASSERT(ast, return qQNaN());
+ Q_ASSERT(ast);
if (!ast->statement) {
addError(ast->colonToken, tr("Expected numeric literal after colon."));
return 0;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
- addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected numeric literal after colon."));
return 0;
}
- NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
if (!numericLit) {
addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
return 0;
@@ -533,26 +525,29 @@ ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBindin
ComponentVersion invalidVersion;
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;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
- addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected numeric literal after colon."));
return invalidVersion;
}
- NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
if (!numericLit) {
addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
return invalidVersion;
}
- return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length));
+ 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);
int i = static_cast<int>(v);
@@ -565,31 +560,33 @@ int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
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) {
addError(ast->colonToken, tr("Expected array of strings after colon."));
return;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
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;
}
- ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
if (!arrayLit) {
addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
return;
}
for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
- StringLiteral *stringLit = AST::cast<StringLiteral *>(it->element->initializer);
+ auto *stringLit = cast<StringLiteral *>(it->element->initializer);
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;
}
QString exp = stringLit->value.toString();
@@ -598,7 +595,9 @@ void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Pt
ComponentVersion version(exp.mid(spaceIdx + 1));
if (spaceIdx == -1 || !version.isValid()) {
- addError(stringLit->firstSourceLocation(), tr("Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'."));
+ addError(stringLit->firstSourceLocation(),
+ tr("Expected string literal to contain 'Package/Name major.minor' "
+ "or 'Name major.minor'."));
continue;
}
QString package;
@@ -607,42 +606,46 @@ void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Pt
QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
// ### 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) {
addError(ast->colonToken, tr("Expected array of numbers after colon."));
return;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
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;
}
- ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
if (!arrayLit) {
addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
return;
}
int exportIndex = 0;
- const int exportCount = fmo->exports().size();
+ const int exportCount = scope->exports().size();
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) {
- 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;
}
if (exportIndex >= exportCount) {
- addError(numberLit->firstSourceLocation(), tr("Meta object revision without matching export."));
+ addError(numberLit->firstSourceLocation(),
+ tr("Meta object revision without matching export."));
return;
}
@@ -653,11 +656,11 @@ void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMe
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)
return;
@@ -666,39 +669,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt
return;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
- addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
- return;
- }
-
- ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
- if (!objectLit) {
- addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
+ addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
return;
}
- for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
- PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
- if (assignement) {
- StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
- NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
- UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
- if (minus)
- value = AST::cast<NumericLiteral *>(minus->expression);
- if (!propName || !value) {
- addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
- continue;
+ if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) {
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ if (PatternProperty *assignement = it->property) {
+ if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) {
+ metaEnum->addKey(name->id.toString());
+ continue;
+ }
}
-
- double v = value->value;
- if (minus)
- v = -v;
- fme->addKey(propName->id.toString(), v);
- continue;
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
+ }
+ } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) {
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ if (PatternElement *element = it->element) {
+ if (auto *name = cast<StringLiteral *>(element->initializer)) {
+ metaEnum->addKey(name->value.toString());
+ continue;
+ }
+ }
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
}
- PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
- if (getterSetter)
- addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
+ } else {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected either array or object literal as enum definition."));
}
}
diff --git a/tools/qmllint/typedescriptionreader.h b/tools/qmllint/typedescriptionreader.h
new file mode 100644
index 0000000000..48c33bee3c
--- /dev/null
+++ b/tools/qmllint/typedescriptionreader.h
@@ -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::SourceLocation &loc, const QString &message);
+ void addWarning(const QQmlJS::SourceLocation &loc, const QString &message);
+
+ QString m_fileName;
+ QString m_source;
+ QString m_errorMessage;
+ QString m_warningMessage;
+ QHash<QString, ScopeTree::ConstPtr> *m_objects = nullptr;
+ QList<ModuleApiInfo> *m_moduleApis = nullptr;
+ QStringList *m_dependencies = nullptr;
+};
+
+#endif // TYPEDESCRIPTIONREADER_H
diff --git a/tools/qmlmin/main.cpp b/tools/qmlmin/main.cpp
index 26833d2a08..3c9b3c7251 100644
--- a/tools/qmlmin/main.cpp
+++ b/tools/qmlmin/main.cpp
@@ -638,6 +638,8 @@ int runQmlmin(int argc, char *argv[])
return 0;
}
+ std::cerr << "qmlmin: This tool is deprecated and will be removed in Qt 6. It is not needed anymore due to QtQml's built-in caching." << std::endl;
+
QFile file(fileName);
if (! file.open(QFile::ReadOnly)) {
std::cerr << "qmlmin: '" << qPrintable(fileName) << "' no such file or directory" << std::endl;
diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro
index 62b08e9334..e374ae45f4 100644
--- a/tools/qmlplugindump/qmlplugindump.pro
+++ b/tools/qmlplugindump/qmlplugindump.pro
@@ -5,14 +5,16 @@ CONFIG += no_import_scan
QTPLUGIN.platforms = qminimal
+INCLUDEPATH += ../shared
+
SOURCES += \
main.cpp \
- qmlstreamwriter.cpp \
- qmltypereader.cpp
+ qmltypereader.cpp \
+ ../shared/qmlstreamwriter.cpp
HEADERS += \
- qmlstreamwriter.h \
- qmltypereader.h
+ qmltypereader.h \
+ ../shared/qmlstreamwriter.h
macx {
# Prevent qmlplugindump from popping up in the dock when launched.
diff --git a/tools/qmlpreview/qmlpreviewapplication.cpp b/tools/qmlpreview/qmlpreviewapplication.cpp
index 17017dae77..2568425573 100644
--- a/tools/qmlpreview/qmlpreviewapplication.cpp
+++ b/tools/qmlpreview/qmlpreviewapplication.cpp
@@ -195,7 +195,7 @@ void QmlPreviewApplication::processFinished()
void QmlPreviewApplication::logError(const QString &error)
{
QTextStream err(stderr);
- err << "Error: " << error << endl;
+ err << "Error: " << error << Qt::endl;
}
void QmlPreviewApplication::logStatus(const QString &status)
@@ -203,7 +203,7 @@ void QmlPreviewApplication::logStatus(const QString &status)
if (!m_verbose)
return;
QTextStream err(stderr);
- err << status << endl;
+ err << status << Qt::endl;
}
void QmlPreviewApplication::serveRequest(const QString &path)
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index 7b010546c3..7c92f428ae 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -354,7 +354,7 @@ bool QmlProfilerApplication::checkOutputFile(PendingRequest pending)
void QmlProfilerApplication::userCommand(const QString &command)
{
- auto args = command.splitRef(QChar::Space, QString::SkipEmptyParts);
+ auto args = command.splitRef(QChar::Space, Qt::SkipEmptyParts);
if (args.isEmpty()) {
prompt();
return;
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index 260c5bb7d1..b14166ad8d 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -500,7 +500,8 @@ int main(int argc, char ** argv)
}
}
- if (qEnvironmentVariableIsSet("QMLSCENE_CORE_PROFILE"))
+ if (qEnvironmentVariableIsSet("QMLSCENE_CORE_PROFILE")
+ || qEnvironmentVariableIsSet("QSG_CORE_PROFILE"))
options.coreProfile = true;
// Set default surface format before creating the window
diff --git a/tools/qmltime/qmltime.cpp b/tools/qmltime/qmltime.cpp
index 7baedff611..ca10fdde53 100644
--- a/tools/qmltime/qmltime.cpp
+++ b/tools/qmltime/qmltime.cpp
@@ -25,47 +25,18 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
+#include "qmltime.h"
+
#include <QQmlEngine>
-#include <QQmlComponent>
#include <QDebug>
#include <QGuiApplication>
#include <QElapsedTimer>
#include <QQmlContext>
-#include <QQuickView>
#include <QQuickItem>
#include <private/qquickview_p.h>
-class Timer : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QQmlComponent *component READ component WRITE setComponent)
-
-public:
- Timer();
-
- QQmlComponent *component() const;
- void setComponent(QQmlComponent *);
-
- static Timer *timerInstance();
-
- void run(uint);
-
- bool willParent() const;
- void setWillParent(bool p);
-
-private:
- void runTest(QQmlContext *, uint);
-
- QQmlComponent *m_component;
- static Timer *m_timer;
-
- bool m_willparent;
- QQuickView m_view;
- QQuickItem *m_item;
-};
-QML_DECLARE_TYPE(Timer);
-
Timer *Timer::m_timer = nullptr;
Timer::Timer()
@@ -207,8 +178,6 @@ int main(int argc, char ** argv)
QGuiApplication app(argc, argv);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- qmlRegisterType<Timer>("QmlTime", 1, 0, "Timer");
-
uint iterations = 1024;
QString filename;
bool willParent = false;
@@ -269,5 +238,3 @@ int main(int argc, char ** argv)
return 0;
}
-
-#include "qmltime.moc"
diff --git a/tools/qmltime/qmltime.h b/tools/qmltime/qmltime.h
new file mode 100644
index 0000000000..a23dc902e2
--- /dev/null
+++ b/tools/qmltime/qmltime.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 QMLTIME_H
+#define QMLTIME_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickview.h>
+
+class Timer : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlComponent *component READ component WRITE setComponent)
+ QML_ELEMENT
+
+public:
+ Timer();
+
+ QQmlComponent *component() const;
+ void setComponent(QQmlComponent *);
+
+ static Timer *timerInstance();
+
+ void run(uint);
+
+ bool willParent() const;
+ void setWillParent(bool p);
+
+private:
+ void runTest(QQmlContext *, uint);
+
+ QQmlComponent *m_component;
+ static Timer *m_timer;
+
+ bool m_willparent;
+ QQuickView m_view;
+ QQuickItem *m_item;
+};
+QML_DECLARE_TYPE(Timer);
+
+#endif // QMLTIME_H
diff --git a/tools/qmltime/qmltime.pro b/tools/qmltime/qmltime.pro
index 04a5fd5957..c915f6e8c1 100644
--- a/tools/qmltime/qmltime.pro
+++ b/tools/qmltime/qmltime.pro
@@ -4,5 +4,11 @@ QT += qml quick
QT += quick-private
macx:CONFIG -= app_bundle
+CONFIG += qmltypes
+QML_IMPORT_NAME = QmlTime
+QML_IMPORT_VERSION = 1.0
+
QMAKE_TARGET_DESCRIPTION = QML Time
+
SOURCES += qmltime.cpp
+HEADERS += qmltime.h
diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/shared/qmlstreamwriter.cpp
index 3632cee60d..b0fbc4e443 100644
--- a/tools/qmlplugindump/qmlstreamwriter.cpp
+++ b/tools/shared/qmlstreamwriter.cpp
@@ -50,9 +50,9 @@ void QmlStreamWriter::writeEndDocument()
void QmlStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as)
{
- m_stream->write(QString("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8());
+ m_stream->write(QString::fromLatin1("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8());
if (!as.isEmpty())
- m_stream->write(QString(" as %1").arg(as).toUtf8());
+ m_stream->write(QString::fromLatin1(" as %1").arg(as).toUtf8());
m_stream->write("\n");
}
@@ -60,7 +60,7 @@ void QmlStreamWriter::writeStartObject(const QString &component)
{
flushPotentialLinesWithNewlines();
writeIndent();
- m_stream->write(QString("%1 {").arg(component).toUtf8());
+ m_stream->write(QString::fromLatin1("%1 {").arg(component).toUtf8());
++m_indentDepth;
m_maybeOneline = true;
}
@@ -89,7 +89,7 @@ void QmlStreamWriter::writeEndObject()
void QmlStreamWriter::writeScriptBinding(const QString &name, const QString &rhs)
{
- writePotentialLine(QString("%1: %2").arg(name, rhs).toUtf8());
+ writePotentialLine(QString::fromLatin1("%1: %2").arg(name, rhs).toUtf8());
}
void QmlStreamWriter::writeBooleanBinding(const QString &name, bool value)
@@ -104,7 +104,7 @@ void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList &
// try to use a single line
QString singleLine;
- singleLine += QString("%1: [").arg(name);
+ singleLine += QString::fromLatin1("%1: [").arg(name);
for (int i = 0; i < elements.size(); ++i) {
singleLine += elements.at(i);
if (i != elements.size() - 1)
@@ -117,7 +117,7 @@ void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList &
}
// write multi-line
- m_stream->write(QString("%1: [\n").arg(name).toUtf8());
+ m_stream->write(QString::fromLatin1("%1: [\n").arg(name).toUtf8());
++m_indentDepth;
for (int i = 0; i < elements.size(); ++i) {
writeIndent();
@@ -143,13 +143,13 @@ void QmlStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const
{
flushPotentialLinesWithNewlines();
writeIndent();
- m_stream->write(QString("%1: {\n").arg(name).toUtf8());
+ m_stream->write(QString::fromLatin1("%1: {\n").arg(name).toUtf8());
++m_indentDepth;
for (int i = 0; i < keyValue.size(); ++i) {
const QString key = keyValue.at(i).first;
const QString value = keyValue.at(i).second;
writeIndent();
- m_stream->write(QString("%1: %2").arg(key, value).toUtf8());
+ m_stream->write(QString::fromLatin1("%1: %2").arg(key, value).toUtf8());
if (i != keyValue.size() - 1) {
m_stream->write(",\n");
} else {
diff --git a/tools/qmlplugindump/qmlstreamwriter.h b/tools/shared/qmlstreamwriter.h
index cb642159ea..cb642159ea 100644
--- a/tools/qmlplugindump/qmlstreamwriter.h
+++ b/tools/shared/qmlstreamwriter.h
diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri
index c094b51d5f..1438c3b3da 100644
--- a/tools/shared/shared.pri
+++ b/tools/shared/shared.pri
@@ -1,3 +1,9 @@
INCLUDEPATH += $$PWD
-SOURCES += $$PWD/resourcefilemapper.cpp
-HEADERS += $$PWD/resourcefilemapper.h
+
+SOURCES += \
+ $$PWD/resourcefilemapper.cpp \
+ $$PWD/qmlstreamwriter.cpp
+
+HEADERS += \
+ $$PWD/resourcefilemapper.h \
+ $$PWD/qmlstreamwriter.h
diff --git a/tools/tools.pro b/tools/tools.pro
index 25ed760903..2e04f9330c 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -5,7 +5,8 @@ qtConfig(qml-devtools) {
SUBDIRS += \
qmllint \
qmlmin \
- qmlimportscanner
+ qmlimportscanner \
+ qmlformat
qtConfig(commandlineparser):qtConfig(xmlstreamwriter): SUBDIRS += qmlcachegen
}