aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/qml/conf/content/resizeItemToWindow.qml70
-rw-r--r--tools/qml/conf/content/resizeWindowToItem.qml (renamed from tools/qml/conf/qtquick.qml)6
-rw-r--r--tools/qml/conf/default.qml (renamed from tools/qml/conf/configuration.qml)4
-rw-r--r--tools/qml/conf/resizeToItem.qml57
-rw-r--r--tools/qml/main.cpp389
-rw-r--r--tools/qml/qml.qrc6
-rw-r--r--tools/qmlcachegen/generateloader.cpp8
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp134
-rw-r--r--tools/qmlcachegen/qmlcachegen.pro8
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in184
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in5
-rw-r--r--tools/qmlimportscanner/main.cpp69
-rw-r--r--tools/qmlimportscanner/qmlimportscanner.pro46
-rw-r--r--tools/qmljs/qmljs.cpp3
-rw-r--r--tools/qmllint/componentversion.cpp123
-rw-r--r--tools/qmllint/componentversion.h73
-rw-r--r--tools/qmllint/fakemetaobject.cpp600
-rw-r--r--tools/qmllint/fakemetaobject.h236
-rw-r--r--tools/qmllint/findunqualified.cpp783
-rw-r--r--tools/qmllint/findunqualified.h131
-rw-r--r--tools/qmllint/main.cpp44
-rw-r--r--tools/qmllint/qcoloroutput.cpp342
-rw-r--r--tools/qmllint/qcoloroutput_p.h110
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.cpp704
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.h103
-rw-r--r--tools/qmllint/qmllint.pro16
-rw-r--r--tools/qmllint/scopetree.cpp269
-rw-r--r--tools/qmllint/scopetree.h106
-rw-r--r--tools/qmlplugindump/main.cpp254
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp4
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp1
-rw-r--r--tools/qmlscene/main.cpp51
-rw-r--r--tools/qmltime/qmltime.cpp4
-rw-r--r--tools/shared/resourcefilemapper.cpp (renamed from tools/qmlcachegen/resourcefilemapper.cpp)11
-rw-r--r--tools/shared/resourcefilemapper.h (renamed from tools/qmlcachegen/resourcefilemapper.h)6
-rw-r--r--tools/shared/shared.pri3
-rw-r--r--tools/tools.pro2
37 files changed, 4564 insertions, 401 deletions
diff --git a/tools/qml/conf/content/resizeItemToWindow.qml b/tools/qml/conf/content/resizeItemToWindow.qml
new file mode 100644
index 0000000000..a645cf8ea9
--- /dev/null
+++ b/tools/qml/conf/content/resizeItemToWindow.qml
@@ -0,0 +1,70 @@
+/*****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+*****************************************************************************/
+import QtQuick.Window 2.0
+import QtQuick 2.0
+
+Window {
+ property Item containedObject: null
+ property bool __resizeGuard: false
+ onContainedObjectChanged: {
+ if (containedObject == undefined || containedObject == null) {
+ visible = false;
+ return;
+ }
+ __resizeGuard = true
+ width = containedObject.width;
+ height = containedObject.height;
+ containedObject.parent = contentItem;
+ visible = true;
+ __resizeGuard = false
+ }
+ onWidthChanged: if (!__resizeGuard && containedObject) containedObject.width = width
+ onHeightChanged: if (!__resizeGuard && containedObject) containedObject.height = height
+}
diff --git a/tools/qml/conf/qtquick.qml b/tools/qml/conf/content/resizeWindowToItem.qml
index ef7a46eb2f..cd03e5065a 100644
--- a/tools/qml/conf/qtquick.qml
+++ b/tools/qml/conf/content/resizeWindowToItem.qml
@@ -1,6 +1,6 @@
/*****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -57,8 +57,8 @@ Window {
visible = false;
return;
}
- width = containedObject.width;
- height = containedObject.height;
+ width = Qt.binding(function() { return containedObject.width });
+ height = Qt.binding(function() { return containedObject.height });
containedObject.parent = contentItem;
visible = true;
}
diff --git a/tools/qml/conf/configuration.qml b/tools/qml/conf/default.qml
index e12fa39a3e..be44ee79b4 100644
--- a/tools/qml/conf/configuration.qml
+++ b/tools/qml/conf/default.qml
@@ -1,6 +1,6 @@
/*****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -52,6 +52,6 @@ import QmlRuntime.Config 1.0
Configuration {
PartialScene {
itemType: "QQuickItem"
- container: "qtquick.qml"
+ container: "content/resizeItemToWindow.qml"
}
}
diff --git a/tools/qml/conf/resizeToItem.qml b/tools/qml/conf/resizeToItem.qml
new file mode 100644
index 0000000000..480995a6b0
--- /dev/null
+++ b/tools/qml/conf/resizeToItem.qml
@@ -0,0 +1,57 @@
+/*****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+*****************************************************************************/
+import QmlRuntime.Config 1.0
+
+Configuration {
+ PartialScene {
+ itemType: "QQuickItem"
+ container: "content/resizeWindowToItem.qml"
+ }
+}
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 8cfc0eaaac..daa278457d 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -44,9 +44,12 @@
#include <QQmlApplicationEngine>
#include <QQmlComponent>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
#include <QDir>
#include <QFile>
#include <QFileInfo>
+#include <QLoggingCategory>
#include <QStringList>
#include <QScopedPointer>
#include <QDebug>
@@ -57,6 +60,7 @@
#include <qqml.h>
#include <qqmldebug.h>
+#include <private/qmemory_p.h>
#include <private/qtqmlglobal_p.h>
#if QT_CONFIG(qml_animation)
#include <private/qabstractanimation_p.h>
@@ -65,24 +69,41 @@
#include <cstdio>
#include <cstring>
#include <cstdlib>
-
-#define VERSION_MAJ 1
-#define VERSION_MIN 1
-#define VERSION_STR "1.1"
+#include <memory>
#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
+enum QmlApplicationType {
+ QmlApplicationTypeUnknown
+ , QmlApplicationTypeCore
+#ifdef QT_GUI_LIB
+ , QmlApplicationTypeGui
+#ifdef QT_WIDGETS_LIB
+ , QmlApplicationTypeWidget
+#endif // QT_WIDGETS_LIB
+#endif // QT_GUI_LIB
+};
+
+static QmlApplicationType applicationType =
+#ifndef QT_GUI_LIB
+ QmlApplicationTypeCore;
+#else
+ QmlApplicationTypeGui;
+#endif // QT_GUI_LIB
+
static Config *conf = nullptr;
static QQmlApplicationEngine *qae = nullptr;
#if defined(Q_OS_DARWIN) || defined(QT_GUI_LIB)
static int exitTimerId = -1;
#endif
-bool verboseMode = false;
static const QString iconResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/resources/qml-64.png"));
+static const QString confResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/conf/"));
+static bool verboseMode = false;
+static bool quietMode = false;
static void loadConf(const QString &override, bool quiet) // Terminates app on failure
{
- const QString defaultFileName = QLatin1String("configuration.qml");
+ const QString defaultFileName = QLatin1String("default.qml");
QUrl settingsUrl;
bool builtIn = false; //just for keeping track of the warning
if (override.isEmpty()) {
@@ -92,29 +113,38 @@ static void loadConf(const QString &override, bool quiet) // Terminates app on f
settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
} else {
// ### If different built-in configs are needed per-platform, just apply QFileSelector to the qrc conf.qml path
- settingsUrl = QUrl(QLatin1String("qrc:///qt-project.org/QmlRuntime/conf/") + defaultFileName);
+ fi.setFile(confResourcePath + defaultFileName);
+ settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
builtIn = true;
}
} else {
QFileInfo fi;
- fi.setFile(override);
- if (!fi.exists()) {
- printf("qml: Couldn't find required configuration file: %s\n",
- qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
- exit(1);
+ fi.setFile(confResourcePath + override + QLatin1String(".qml"));
+ if (fi.exists()) {
+ settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ builtIn = true;
+ } else {
+ fi.setFile(override);
+ if (!fi.exists()) {
+ printf("qml: Couldn't find required configuration file: %s\n",
+ qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
+ exit(1);
+ }
+ settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
}
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
}
if (!quiet) {
printf("qml: %s\n", QLibraryInfo::build());
- if (builtIn)
- printf("qml: Using built-in configuration.\n");
- else
- printf("qml: Using configuration file: %s\n",
+ if (builtIn) {
+ printf("qml: Using built-in configuration: %s\n",
+ qPrintable(override.isEmpty() ? defaultFileName : override));
+ } else {
+ printf("qml: Using configuration: %s\n",
qPrintable(settingsUrl.isLocalFile()
? QDir::toNativeSeparators(settingsUrl.toLocalFile())
: settingsUrl.toString()));
+ }
}
// TODO: When we have better engine control, ban QtQuick* imports on this engine
@@ -128,9 +158,23 @@ static void loadConf(const QString &override, bool quiet) // Terminates app on f
}
}
-#ifdef QT_GUI_LIB
+void noFilesGiven()
+{
+ if (!quietMode)
+ printf("qml: No files specified. Terminating.\n");
+ exit(1);
+}
-void noFilesGiven();
+static void listConfFiles()
+{
+ QDir confResourceDir(confResourcePath);
+ printf("%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")));
+ for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files))
+ printf(" %s\n", qPrintable(fi.baseName()));
+ exit(0);
+}
+
+#ifdef QT_GUI_LIB
// Loads qml after receiving a QFileOpenEvent
class LoaderApplication : public QGuiApplication
@@ -179,8 +223,8 @@ public:
connect(e, &QQmlEngine::exit, this, &LoadWatcher::exit);
}
- bool earlyExit = false;
int returnCode = 0;
+ bool earlyExit = false;
public Q_SLOTS:
void checkFinished(QObject *o, const QUrl &url)
@@ -221,8 +265,8 @@ private:
void checkForWindow(QObject *o);
private:
- int expectedFileCount;
bool haveWindow = false;
+ int expectedFileCount;
};
void LoadWatcher::contain(QObject *o, const QUrl &containPath)
@@ -282,93 +326,18 @@ void quietMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const Q
exit(-1);
case QtCriticalMsg:
case QtDebugMsg:
+ case QtInfoMsg:
case QtWarningMsg:
- default:
;
}
}
-
-// ### Should command line arguments have translations? Qt creator doesn't, so maybe it's not worth it.
-enum QmlApplicationType {
- QmlApplicationTypeUnknown
- , QmlApplicationTypeCore
-#ifdef QT_GUI_LIB
- , QmlApplicationTypeGui
-#ifdef QT_WIDGETS_LIB
- , QmlApplicationTypeWidget
-#endif // QT_WIDGETS_LIB
-#endif // QT_GUI_LIB
-};
-
-#ifndef QT_GUI_LIB
-QmlApplicationType applicationType = QmlApplicationTypeCore;
-#else
-QmlApplicationType applicationType = QmlApplicationTypeGui;
-#endif // QT_GUI_LIB
-bool quietMode = false;
-void printVersion()
-{
- printf("qml binary version ");
- printf(VERSION_STR);
- printf("\nbuilt with Qt version ");
- printf(QT_VERSION_STR);
- printf("\n");
- exit(0);
-}
-
-void printUsage()
-{
- printf("Usage: qml [options] [files] [-- args]\n");
- printf("\n");
- printf("Any unknown argument before '--' will be treated as a QML file to be loaded.\n");
- printf("Any number of QML files can be loaded. They will share the same engine.\n");
- printf("'gui' application type is only available if the QtGui module is available.\n");
- printf("'widget' application type is only available if the QtWidgets module is available.\n");
- printf("\n");
- printf("General Options:\n");
- printf("\t-h, -help..................... Print this usage information and exit.\n");
- printf("\t-v, -version.................. Print the version information and exit.\n");
-#ifdef QT_GUI_LIB
-#ifndef QT_WIDGETS_LIB
- printf("\t-apptype [core|gui] .......... Select which application class to use. Default is gui.\n");
-#else
- printf("\t-apptype [core|gui|widget] ... Select which application class to use. Default is gui.\n");
-#endif // QT_WIDGETS_LIB
-#endif // QT_GUI_LIB
- printf("\t-quiet ....................... Suppress all output.\n");
- printf("\t-I [path] .................... Prepend the given path to the import paths.\n");
- printf("\t-f [file] .................... Load the given file as a QML file.\n");
- printf("\t-config [file] ............... Load the given file as the configuration file.\n");
- printf("\t-- ........................... Arguments after this one are ignored by the launcher, but may be used within the QML application.\n");
- printf("\tGL options:\n");
- printf("\t-desktop.......................Force use of desktop GL (AA_UseDesktopOpenGL)\n");
- printf("\t-gles..........................Force use of GLES (AA_UseOpenGLES)\n");
- printf("\t-software......................Force use of software rendering (AA_UseOpenGLES)\n");
- printf("\t-scaling.......................Enable High DPI scaling (AA_EnableHighDpiScaling)\n");
- printf("\t-no-scaling....................Disable High DPI scaling (AA_DisableHighDpiScaling)\n");
- printf("\tDebugging options:\n");
- printf("\t-verbose ..................... Print information about what qml is doing, like specific file urls being loaded.\n");
- printf("\t-translation [file] .......... Load the given file as the translations file.\n");
- printf("\t-dummy-data [directory] ...... Load QML files from the given directory as context properties.\n");
- printf("\t-slow-animations ............. Run all animations in slow motion.\n");
- printf("\t-fixed-animations ............ Run animations off animation tick rather than wall time.\n");
- exit(0);
-}
-
-void noFilesGiven()
-{
- if (!quietMode)
- printf("qml: No files specified. Terminating.\n");
- exit(1);
-}
-
// Called before application initialization, removes arguments it uses
void getAppFlags(int &argc, char **argv)
{
#ifdef QT_GUI_LIB
for (int i=0; i<argc; i++) {
- if (!strcmp(argv[i], "-apptype")) { // Must be done before application, as it selects application
+ if (!strcmp(argv[i], "--apptype") || !strcmp(argv[i], "-a") || !strcmp(argv[i], "-apptype")) {
applicationType = QmlApplicationTypeUnknown;
if (i+1 < argc) {
if (!strcmp(argv[i+1], "core"))
@@ -380,15 +349,6 @@ void getAppFlags(int &argc, char **argv)
applicationType = QmlApplicationTypeWidget;
#endif // QT_WIDGETS_LIB
}
-
- if (applicationType == QmlApplicationTypeUnknown) {
-#ifndef QT_WIDGETS_LIB
- printf("-apptype must be followed by one of the following: core gui\n");
-#else
- printf("-apptype must be followed by one of the following: core gui widget\n");
-#endif // QT_WIDGETS_LIB
- printUsage();
- }
for (int j=i; j<argc-2; j++)
argv[j] = argv[j+2];
argc -= 2;
@@ -440,24 +400,23 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
int main(int argc, char *argv[])
{
getAppFlags(argc, argv);
- QCoreApplication *app = nullptr;
+ std::unique_ptr<QCoreApplication> app;
switch (applicationType) {
- case QmlApplicationTypeCore:
- app = new QCoreApplication(argc, argv);
- break;
#ifdef QT_GUI_LIB
case QmlApplicationTypeGui:
- app = new LoaderApplication(argc, argv);
+ app = qt_make_unique<LoaderApplication>(argc, argv);
break;
#ifdef QT_WIDGETS_LIB
case QmlApplicationTypeWidget:
- app = new QApplication(argc, argv);
- static_cast<QApplication *>(app)->setWindowIcon(QIcon(iconResourcePath));
+ app = qt_make_unique<QApplication>(argc, argv);
+ static_cast<QApplication *>(app.get())->setWindowIcon(QIcon(iconResourcePath));
break;
#endif // QT_WIDGETS_LIB
#endif // QT_GUI_LIB
- default:
- Q_ASSERT_X(false, Q_FUNC_INFO, "impossible case");
+ case QmlApplicationTypeCore:
+ Q_FALLTHROUGH();
+ default: // QmlApplicationTypeUnknown: not allowed, but we'll exit after checking apptypeOption below
+ app = qt_make_unique<QCoreApplication>(argc, argv);
break;
}
@@ -475,67 +434,143 @@ int main(int argc, char *argv[])
QString dummyDir;
// Handle main arguments
- const QStringList argList = app->arguments();
- for (int i = 1; i < argList.count(); i++) {
- const QString &arg = argList[i];
- if (arg == QLatin1String("-quiet"))
- quietMode = true;
- else if (arg == QLatin1String("-v") || arg == QLatin1String("-version"))
- printVersion();
- else if (arg == QLatin1String("-h") || arg == QLatin1String("-help"))
- printUsage();
- else if (arg == QLatin1String("--"))
- break;
- else if (arg == QLatin1String("-verbose"))
- verboseMode = true;
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
+ const QCommandLineOption helpOption = parser.addHelpOption();
+ const QCommandLineOption versionOption = parser.addVersionOption();
+#ifdef QT_GUI_LIB
+ QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"),
+ QCoreApplication::translate("main", "Select which application class to use. Default is gui."),
+#ifdef QT_WIDGETS_LIB
+ QStringLiteral("core|gui|widget"));
+#else
+ QStringLiteral("core|gui"));
+#endif // QT_WIDGETS_LIB
+ parser.addOption(apptypeOption); // Just for the help text... we've already handled this argument above
+#endif // QT_GUI_LIB
+ QCommandLineOption importOption(QStringLiteral("I"),
+ QCoreApplication::translate("main", "Prepend the given path to the import paths."), QStringLiteral("path"));
+ parser.addOption(importOption);
+ QCommandLineOption qmlFileOption(QStringLiteral("f"),
+ QCoreApplication::translate("main", "Load the given file as a QML file."), QStringLiteral("file"));
+ parser.addOption(qmlFileOption);
+ QCommandLineOption configOption(QStringList() << QStringLiteral("c") << QStringLiteral("config"),
+ QCoreApplication::translate("main", "Load the given built-in configuration or configuration file."), QStringLiteral("file"));
+ parser.addOption(configOption);
+ QCommandLineOption listConfOption(QStringList() << QStringLiteral("list-conf"),
+ QCoreApplication::translate("main", "List the built-in configurations."));
+ parser.addOption(listConfOption);
+ QCommandLineOption translationOption(QStringLiteral("translation"),
+ QCoreApplication::translate("main", "Load the given file as the translations file."), QStringLiteral("file"));
+ parser.addOption(translationOption);
+ QCommandLineOption dummyDataOption(QStringLiteral("dummy-data"),
+ QCoreApplication::translate("main", "Load QML files from the given directory as context properties."), QStringLiteral("file"));
+ parser.addOption(dummyDataOption);
+ // OpenGL options
+ QCommandLineOption glDesktopOption(QStringLiteral("desktop"),
+ QCoreApplication::translate("main", "Force use of desktop OpenGL (AA_UseDesktopOpenGL)."));
+ parser.addOption(glDesktopOption);
+ QCommandLineOption glEsOption(QStringLiteral("gles"),
+ QCoreApplication::translate("main", "Force use of GLES (AA_UseOpenGLES)."));
+ parser.addOption(glEsOption);
+ QCommandLineOption glSoftwareOption(QStringLiteral("software"),
+ QCoreApplication::translate("main", "Force use of software rendering (AA_UseSoftwareOpenGL)."));
+ parser.addOption(glSoftwareOption);
+ QCommandLineOption scalingOption(QStringLiteral("scaling"),
+ QCoreApplication::translate("main", "Enable High DPI scaling (AA_EnableHighDpiScaling)."));
+ parser.addOption(scalingOption);
+ QCommandLineOption noScalingOption(QStringLiteral("no-scaling"),
+ QCoreApplication::translate("main", "Disable High DPI scaling (AA_DisableHighDpiScaling)."));
+ parser.addOption(noScalingOption);
+ // Debugging and verbosity options
+ QCommandLineOption quietOption(QStringLiteral("quiet"),
+ QCoreApplication::translate("main", "Suppress all output."));
+ parser.addOption(quietOption);
+ QCommandLineOption verboseOption(QStringLiteral("verbose"),
+ QCoreApplication::translate("main", "Print information about what qml is doing, like specific file URLs being loaded."));
+ parser.addOption(verboseOption);
+ QCommandLineOption slowAnimationsOption(QStringLiteral("slow-animations"),
+ QCoreApplication::translate("main", "Run all animations in slow motion."));
+ parser.addOption(slowAnimationsOption);
+ QCommandLineOption fixedAnimationsOption(QStringLiteral("fixed-animations"),
+ QCoreApplication::translate("main", "Run animations off animation tick rather than wall time."));
+ parser.addOption(fixedAnimationsOption);
+ QCommandLineOption rhiOption(QStringList() << QStringLiteral("r") << QStringLiteral("rhi"),
+ QCoreApplication::translate("main", "Use the Qt graphics abstraction (RHI) instead of OpenGL directly. "
+ "Backend is one of: default, vulkan, metal, d3d11, gl"),
+ QStringLiteral("backend"));
+ parser.addOption(rhiOption);
+
+ // Positional arguments
+ parser.addPositionalArgument("files",
+ QCoreApplication::translate("main", "Any number of QML files can be loaded. They will share the same engine."), "[files...]");
+ parser.addPositionalArgument("args",
+ QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]");
+
+ if (!parser.parse(QCoreApplication::arguments())) {
+ qWarning() << parser.errorText();
+ exit(1);
+ }
+ if (parser.isSet(versionOption))
+ parser.showVersion();
+ if (parser.isSet(helpOption))
+ parser.showHelp();
+ if (parser.isSet(listConfOption))
+ listConfFiles();
+ if (applicationType == QmlApplicationTypeUnknown) {
+#ifdef QT_WIDGETS_LIB
+ qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui widget\n");
+#else
+ qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui\n");
+#endif // QT_WIDGETS_LIB
+ parser.showHelp();
+ }
+ if (parser.isSet(verboseOption))
+ verboseMode = true;
+ if (parser.isSet(quietOption)) {
+ quietMode = true;
+ verboseMode = false;
+ }
#if QT_CONFIG(qml_animation)
- else if (arg == QLatin1String("-slow-animations"))
- QUnifiedTimer::instance()->setSlowModeEnabled(true);
- else if (arg == QLatin1String("-fixed-animations"))
- QUnifiedTimer::instance()->setConsistentTiming(true);
+ if (parser.isSet(slowAnimationsOption))
+ QUnifiedTimer::instance()->setSlowModeEnabled(true);
+ if (parser.isSet(fixedAnimationsOption))
+ QUnifiedTimer::instance()->setConsistentTiming(true);
#endif
- else if (arg == QLatin1String("-I")) {
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- e.addImportPath(argList[i+1]);
- i++;
- } else if (arg == QLatin1String("-f")) {
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- files << argList[i+1];
- i++;
- } else if (arg == QLatin1String("-config")){
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- confFile = argList[i+1];
- i++;
- } else if (arg == QLatin1String("-translation")){
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- translationFile = argList[i+1];
- i++;
- } else if (arg == QLatin1String("-dummy-data")){
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- dummyDir = argList[i+1];
- i++;
- } else if (arg == QLatin1String("-gles")) {
- QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
- } else if (arg == QLatin1String("-software")) {
- QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
- } else if (arg == QLatin1String("-desktop")) {
- QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
- } else if (arg == QLatin1String("-scaling")) {
- QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- } else if (arg == QLatin1String("-no-scaling")) {
- QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
- } else {
- files << arg;
- }
+ if (parser.isSet(glEsOption))
+ QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
+ if (parser.isSet(glSoftwareOption))
+ QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
+ if (parser.isSet(glDesktopOption))
+ QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
+ if (parser.isSet(scalingOption))
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ if (parser.isSet(noScalingOption))
+ QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
+ for (const QString &importPath : parser.values(importOption))
+ e.addImportPath(importPath);
+ files << parser.values(qmlFileOption);
+ if (parser.isSet(configOption))
+ confFile = parser.value(configOption);
+ if (parser.isSet(translationOption))
+ translationFile = parser.value(translationOption);
+ if (parser.isSet(dummyDataOption))
+ dummyDir = parser.value(dummyDataOption);
+ if (parser.isSet(rhiOption)) {
+ qputenv("QSG_RHI", "1");
+ const QString rhiBackend = parser.value(rhiOption);
+ if (rhiBackend == QLatin1String("default"))
+ qunsetenv("QSG_RHI_BACKEND");
+ else
+ qputenv("QSG_RHI_BACKEND", rhiBackend.toLatin1());
+ }
+ for (QString posArg : parser.positionalArguments()) {
+ if (posArg == QLatin1String("--"))
+ break;
+ else
+ files << posArg;
}
-
- if (quietMode && verboseMode)
- verboseMode = false;
#if QT_CONFIG(translation)
// Need to be installed before QQmlApplicationEngine's automatic translation loading
@@ -557,13 +592,15 @@ int main(int argc, char *argv[])
printf("qml: Translation file specified, but Qt built without translation support.\n");
#endif
- if (quietMode)
+ if (quietMode) {
qInstallMessageHandler(quietMessageHandler);
+ QLoggingCategory::setFilterRules(QStringLiteral("*=false"));
+ }
if (files.count() <= 0) {
#if defined(Q_OS_DARWIN)
if (applicationType == QmlApplicationTypeGui)
- exitTimerId = static_cast<LoaderApplication *>(app)->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
+ exitTimerId = static_cast<LoaderApplication *>(app.get())->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
else
#endif
noFilesGiven();
diff --git a/tools/qml/qml.qrc b/tools/qml/qml.qrc
index e4be1793d4..69aa4a5756 100644
--- a/tools/qml/qml.qrc
+++ b/tools/qml/qml.qrc
@@ -1,7 +1,9 @@
<RCC>
<qresource prefix="qt-project.org/QmlRuntime">
- <file>conf/configuration.qml</file>
- <file>conf/qtquick.qml</file>
+ <file>conf/default.qml</file>
+ <file>conf/resizeToItem.qml</file>
+ <file>conf/content/resizeItemToWindow.qml</file>
+ <file>conf/content/resizeWindowToItem.qml</file>
<file>resources/qml-64.png</file>
</qresource>
</RCC>
diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp
index 4fbbb27afb..1c8a5a016a 100644
--- a/tools/qmlcachegen/generateloader.cpp
+++ b/tools/qmlcachegen/generateloader.cpp
@@ -75,7 +75,7 @@ QString mangledIdentifier(const QString &str)
|| (c >= QLatin1Char('a') && c <= QLatin1Char('z'))
|| (c >= QLatin1Char('A') && c <= QLatin1Char('Z'))
|| c == QLatin1Char('_')) {
- mangled += c;
+ mangled += QChar(c);
} else {
mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');
}
@@ -434,7 +434,11 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR
}
}
+#if QT_CONFIG(temporaryfile)
QSaveFile f(outputFileName);
+#else
+ QFile f(outputFileName);
+#endif
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
*errorString = f.errorString();
return false;
@@ -445,10 +449,12 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR
return false;
}
+#if QT_CONFIG(temporaryfile)
if (!f.commit()) {
*errorString = f.errorString();
return false;
}
+#endif
return true;
}
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index df7468eaef..ac9cf039d3 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -45,6 +45,8 @@
#include <algorithm>
+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,
@@ -65,6 +67,7 @@ struct Error
void print();
Error augment(const QString &contextErrorMessage) const;
void appendDiagnostics(const QString &inputFileName, const QList<QQmlJS::DiagnosticMessage> &diagnostics);
+ void appendDiagnostic(const QString &inputFileName, const DiagnosticMessage &diagnostic);
};
void Error::print()
@@ -82,9 +85,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.loc.startLine) + QLatin1Char(':');
- if (m.loc.startColumn > 0)
- message += QString::number(m.loc.startColumn) + QLatin1Char(':');
+ message = fileName + QLatin1Char(':') + QString::number(m.line) + QLatin1Char(':');
+ if (m.column > 0)
+ message += QString::number(m.column) + QLatin1Char(':');
if (m.isError())
message += QLatin1String(" error: ");
@@ -94,13 +97,17 @@ QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::Diagnostic
return message;
}
+void Error::appendDiagnostic(const QString &inputFileName, const DiagnosticMessage &diagnostic)
+{
+ if (!message.isEmpty())
+ message += QLatin1Char('\n');
+ message += diagnosticErrorMessage(inputFileName, diagnostic);
+}
+
void Error::appendDiagnostics(const QString &inputFileName, const QList<DiagnosticMessage> &diagnostics)
{
- for (const QQmlJS::DiagnosticMessage &parseError: diagnostics) {
- if (!message.isEmpty())
- message += QLatin1Char('\n');
- message += diagnosticErrorMessage(inputFileName, parseError);
- }
+ for (const QQmlJS::DiagnosticMessage &diagnostic: diagnostics)
+ appendDiagnostic(inputFileName, diagnostic);
}
// Ensure that ListElement objects keep all property assignments in their string form
@@ -169,7 +176,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
return true;
}
-using SaveFunction = std::function<bool (const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QString *)>;
+using SaveFunction = std::function<bool(const QV4::CompiledData::SaveableUnitPointer &, QString *)>;
static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFunction, Error *error)
{
@@ -200,10 +207,7 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
annotateListElements(&irDocument);
{
- QmlIR::JSCodeGen v4CodeGen(irDocument.code,
- &irDocument.jsGenerator, &irDocument.jsModule,
- &irDocument.jsParserEngine, irDocument.program,
- &irDocument.jsGenerator.stringTable, illegalNames);
+ QmlIR::JSCodeGen v4CodeGen(&irDocument, illegalNames);
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
continue;
@@ -211,9 +215,8 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
functionsToCompile << *foe;
const QVector<int> runtimeFunctionIndices = v4CodeGen.generateJSCodeForFunctionsAndBindings(functionsToCompile);
- QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
- if (!jsErrors.isEmpty()) {
- error->appendDiagnostics(inputFileName, jsErrors);
+ if (v4CodeGen.hasError()) {
+ error->appendDiagnostic(inputFileName, v4CodeGen.error());
return false;
}
@@ -229,11 +232,13 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
QmlIR::QmlUnitGenerator generator;
irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
generator.generate(irDocument);
- QV4::CompiledData::Unit *unit = const_cast<QV4::CompiledData::Unit*>(irDocument.javaScriptCompilationUnit->data);
- unit->flags |= QV4::CompiledData::Unit::StaticData;
- unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation;
- if (!saveFunction(irDocument.javaScriptCompilationUnit, &error->message))
+ const quint32 saveFlags
+ = QV4::CompiledData::Unit::StaticData
+ | QV4::CompiledData::Unit::PendingTypeCompilation;
+ QV4::CompiledData::SaveableUnitPointer saveable(irDocument.javaScriptCompilationUnit.data,
+ saveFlags);
+ if (!saveFunction(saveable, &error->message))
return false;
}
return true;
@@ -241,7 +246,7 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
static bool compileJSFile(const QString &inputFileName, const QString &inputFileUrl, SaveFunction saveFunction, Error *error)
{
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
+ QV4::CompiledData::CompilationUnit unit;
QString sourceCode;
{
@@ -262,9 +267,10 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
QList<QQmlJS::DiagnosticMessage> diagnostics;
// Precompiled files are relocatable and the final location will be set when loading.
QString url;
- unit = QV4::ExecutionEngine::compileModule(/*debugMode*/false, url, sourceCode, QDateTime(), &diagnostics);
+ unit = QV4::Compiler::Codegen::compileModule(/*debugMode*/false, url, sourceCode,
+ QDateTime(), &diagnostics);
error->appendDiagnostics(inputFileName, diagnostics);
- if (!unit)
+ if (!unit.unitData())
return false;
} else {
QmlIR::Document irDocument(/*debugMode*/false);
@@ -302,14 +308,11 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
}
{
- QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator,
- &irDocument.jsModule, &irDocument.jsParserEngine,
- irDocument.program, &irDocument.jsGenerator.stringTable, illegalNames);
+ QmlIR::JSCodeGen v4CodeGen(&irDocument, illegalNames);
v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
&irDocument.jsModule, QV4::Compiler::ContextType::ScriptImportedByQML);
- QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
- if (!jsErrors.isEmpty()) {
- error->appendDiagnostics(inputFileName, jsErrors);
+ if (v4CodeGen.hasError()) {
+ error->appendDiagnostic(inputFileName, v4CodeGen.error());
return false;
}
@@ -320,19 +323,22 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
QmlIR::QmlUnitGenerator generator;
generator.generate(irDocument);
- QV4::CompiledData::Unit *unitData = const_cast<QV4::CompiledData::Unit*>(irDocument.javaScriptCompilationUnit->data);
- unitData->flags |= QV4::CompiledData::Unit::StaticData;
- unit = irDocument.javaScriptCompilationUnit;
+ unit = std::move(irDocument.javaScriptCompilationUnit);
}
}
- return saveFunction(unit, &error->message);
+ return saveFunction(QV4::CompiledData::SaveableUnitPointer(unit.data), &error->message);
}
static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFileName,
- const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, QString *errorString)
+ const QV4::CompiledData::SaveableUnitPointer &unit,
+ QString *errorString)
{
+#if QT_CONFIG(temporaryfile)
QSaveFile f(outputFileName);
+#else
+ QFile f(outputFileName);
+#endif
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
*errorString = f.errorString();
return false;
@@ -364,43 +370,38 @@ static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFil
if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [] = {\n")))
return false;
- QByteArray hexifiedData;
- {
- QByteArray modifiedUnit;
- modifiedUnit.resize(unit->data->unitSize);
- memcpy(modifiedUnit.data(), unit->data, unit->data->unitSize);
- const char *dataPtr = modifiedUnit.data();
- QV4::CompiledData::Unit *unitPtr;
- memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
- unitPtr->flags |= QV4::CompiledData::Unit::StaticData;
-
- QTextStream stream(&hexifiedData);
- const uchar *begin = reinterpret_cast<const uchar *>(modifiedUnit.constData());
- const uchar *end = begin + unit->data->unitSize;
- stream << hex;
- int col = 0;
- for (const uchar *data = begin; data < end; ++data, ++col) {
- if (data > begin)
- stream << ',';
- if (col % 8 == 0) {
- stream << '\n';
- col = 0;
+ unit.saveToDisk<uchar>([&writeStr](const uchar *begin, quint32 size) {
+ QByteArray hexifiedData;
+ {
+ QTextStream stream(&hexifiedData);
+ const uchar *end = begin + size;
+ stream << hex;
+ int col = 0;
+ for (const uchar *data = begin; data < end; ++data, ++col) {
+ if (data > begin)
+ stream << ',';
+ if (col % 8 == 0) {
+ stream << '\n';
+ col = 0;
+ }
+ stream << "0x" << *data;
}
- stream << "0x" << *data;
+ stream << '\n';
}
- stream << '\n';
- };
+ return writeStr(hexifiedData);
+ });
+
- if (!writeStr(hexifiedData))
- return false;
if (!writeStr("};\n}\n}\n"))
return false;
+#if QT_CONFIG(temporaryfile)
if (!f.commit()) {
*errorString = f.errorString();
return false;
}
+#endif
return true;
}
@@ -524,13 +525,20 @@ int main(int argc, char **argv)
inputFileUrl = QStringLiteral("qrc://") + inputResourcePath;
- saveFunction = [inputResourcePath, outputFileName](const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, QString *errorString) {
+ saveFunction = [inputResourcePath, outputFileName](
+ const QV4::CompiledData::SaveableUnitPointer &unit,
+ QString *errorString) {
return saveUnitAsCpp(inputResourcePath, outputFileName, unit, errorString);
};
} else {
- saveFunction = [outputFileName](const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, QString *errorString) {
- return unit->saveToDisk(outputFileName, errorString);
+ saveFunction = [outputFileName](const QV4::CompiledData::SaveableUnitPointer &unit,
+ QString *errorString) {
+ return unit.saveToDisk<char>(
+ [&outputFileName, errorString](const char *data, quint32 size) {
+ return QV4::CompiledData::SaveableUnitPointer::writeDataToFile(
+ outputFileName, data, size, errorString);
+ });
};
}
diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro
index bee0b9a37e..ec65cdb5e6 100644
--- a/tools/qmlcachegen/qmlcachegen.pro
+++ b/tools/qmlcachegen/qmlcachegen.pro
@@ -5,8 +5,10 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES = qmlcachegen.cpp \
resourcefilter.cpp \
- generateloader.cpp \
- resourcefilemapper.cpp
+ generateloader.cpp
+
+include(../shared/shared.pri)
+
TARGET = qmlcachegen
build_integration.files = qmlcache.prf qtquickcompiler.prf
@@ -38,5 +40,3 @@ QMAKE_TARGET_DESCRIPTION = QML Cache Generator
load(qt_tool)
-HEADERS += \
- resourcefilemapper.h
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
new file mode 100644
index 0000000000..6cdfaf8f6f
--- /dev/null
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
@@ -0,0 +1,184 @@
+include(CMakeParseArguments)
+
+function(QT5_IMPORT_QML_PLUGINS target)
+!!IF !isEmpty(CMAKE_STATIC_TYPE)
+ set(options)
+ set(oneValueArgs \"PATH_TO_SCAN\")
+ set(multiValueArgs)
+
+ cmake_parse_arguments(arg \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})
+ if(NOT arg_PATH_TO_SCAN)
+ set(arg_PATH_TO_SCAN \"${CMAKE_CURRENT_SOURCE_DIR}\")
+ endif()
+
+ # Find location of qmlimportscanner.
+ find_package(Qt5 COMPONENTS Core)
+!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE)
+ set(tool_path
+ \"${_qt5Core_install_prefix}/$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\")
+!!ELSE
+ set(tool_path \"$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\")
+!!ENDIF
+ if(NOT EXISTS \"${tool_path}\" )
+ message(FATAL_ERROR \"The package \\\"Qt5QmlImportScannerConfig\\\" references the file
+ \\\"${tool_path}\\\"
+but this file does not exist. Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty.
+\")
+ endif()
+
+ # Find location of qml dir.
+!!IF isEmpty(CMAKE_QML_DIR_IS_ABSOLUTE)
+ set(qml_path \"${_qt5Core_install_prefix}/$${CMAKE_QML_DIR}\")
+!!ELSE
+ set(qml_path \"$${CMAKE_QML_DIR}\")
+!!ENDIF
+
+ # Small macro to avoid duplicating code in two different loops.
+ macro(_qt5_QmlImportScanner_parse_entry)
+ set(entry_name \"qml_import_scanner_import_${idx}\")
+ cmake_parse_arguments(\"entry\"
+ \"\"
+ \"CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;\" \"\"
+ ${${entry_name}})
+ endmacro()
+
+ # Macro used to populate the dependency link flags for a certain configuriation (debug vs
+ # release) of a plugin.
+ macro(_qt5_link_to_QmlImportScanner_library_dependencies Plugin Configuration PluginLocation
+ IsDebugAndRelease)
+
+ set_property(TARGET \"${Plugin}\" APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
+ set(_imported_location \"${PluginLocation}\")
+ _qt5_Core_check_file_exists(\"${_imported_location}\")
+ set_target_properties(\"${Plugin}\" PROPERTIES
+ \"IMPORTED_LOCATION_${Configuration}\" \"${_imported_location}\"
+ )
+
+ set(_static_deps
+ ${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LIB_DEPENDENCIES}
+ )
+
+ if(NOT "${IsDebugAndRelease}")
+ set(_genex_condition \"1\")
+ else()
+ if("${Configuration}" STREQUAL "DEBUG")
+ set(_genex_condition \"$<CONFIG:Debug>\")
+ else()
+ set(_genex_condition \"$<NOT:$<CONFIG:Debug>>\")
+ endif()
+ endif()
+ if(_static_deps)
+ set(_static_deps_genex \"$<${_genex_condition}:${_static_deps}>\")
+ target_link_libraries(${imported_target} INTERFACE \"${_static_deps_genex}\")
+ endif()
+
+ set(_static_link_flags \"${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LINK_FLAGS}\")
+ if(NOT CMAKE_VERSION VERSION_LESS \"3.13\" AND _static_link_flags)
+ set(_static_link_flags_genex \"$<${_genex_condition}:${_static_link_flags}>\")
+ target_link_options(${imported_target} INTERFACE \"${_static_link_flags_genex}\")
+ endif()
+ endmacro()
+
+ # Run qmlimportscanner and include the generated cmake file.
+ set(qml_imports_file_path
+ \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cmake\")
+
+ message(STATUS \"Running qmlimportscanner to find used QML plugins. \")
+ execute_process(COMMAND
+ \"${tool_path}\" \"${arg_PATH_TO_SCAN}\" -importPath \"${qml_path}\"
+ -cmake-output
+ OUTPUT_FILE \"${qml_imports_file_path}\")
+
+ include(\"${qml_imports_file_path}\" OPTIONAL RESULT_VARIABLE qml_imports_file_path_found)
+ if(NOT qml_imports_file_path_found)
+ message(FATAL_ERROR \"Could not find ${qml_imports_file_path} which was supposed to be generated by qmlimportscanner.\")
+ endif()
+
+ # Parse the generate cmake file.
+ # It is possible for the scanner to find no usage of QML, in which case the import count is 0.
+ if(qml_import_scanner_imports_count)
+ set(added_plugins \"\")
+ foreach(idx RANGE \"${qml_import_scanner_imports_count}\")
+ _qt5_QmlImportScanner_parse_entry()
+ if(entry_PATH AND entry_PLUGIN)
+ # Sometimes a plugin appears multiple times with different versions.
+ # Make sure to process it only once.
+ list(FIND added_plugins \"${entry_PLUGIN}\" _index)
+ if(NOT _index EQUAL -1)
+ continue()
+ endif()
+ list(APPEND added_plugins \"${entry_PLUGIN}\")
+
+ # Add an imported target that will contain the link libraries and link options read
+ # from one plugin prl file. This target will point to the actual plugin and contain
+ # static dependency libraries and link flags.
+ # By creating a target for each qml plugin, CMake will take care of link flag
+ # deduplication.
+ set(imported_target \"${target}_QmlImport_${entry_PLUGIN}\")
+ add_library(\"${imported_target}\" MODULE IMPORTED)
+ target_link_libraries(\"${target}\" PRIVATE \"${imported_target}\")
+
+ # Read static library dependencies from the plugin .prl file.
+ # And then set the link flags to the library dependencies extracted from the .prl
+ # file.
+!!IF !isEmpty(CMAKE_RELEASE_TYPE)
+ _qt5_Core_process_prl_file(
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.prl\" RELEASE
+ _Qt5${entry_PLUGIN}_STATIC_RELEASE_LIB_DEPENDENCIES
+ _Qt5${entry_PLUGIN}_STATIC_RELEASE_LINK_FLAGS
+ )
+ _qt5_link_to_QmlImportScanner_library_dependencies(
+ \"${imported_target}\"
+ RELEASE
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.$$QMAKE_EXTENSION_STATICLIB\"
+ $${CMAKE_DEBUG_AND_RELEASE})
+!!ENDIF
+
+!!IF !isEmpty(CMAKE_DEBUG_TYPE)
+ _qt5_Core_process_prl_file(
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.prl\" DEBUG
+ _Qt5${entry_PLUGIN}_STATIC_DEBUG_LIB_DEPENDENCIES
+ _Qt5${entry_PLUGIN}_STATIC_DEBUG_LINK_FLAGS
+ )
+ _qt5_link_to_QmlImportScanner_library_dependencies(
+ \"${imported_target}\"
+ DEBUG
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.$$QMAKE_EXTENSION_STATICLIB\"
+ $${CMAKE_DEBUG_AND_RELEASE})
+!!ENDIF
+ endif()
+ endforeach()
+
+ # Generate content for plugin initialization cpp file.
+ set(added_imports \"\")
+ set(qt5_qml_import_cpp_file_content \"\")
+ foreach(idx RANGE \"${qml_import_scanner_imports_count}\")
+ _qt5_QmlImportScanner_parse_entry()
+ if(entry_PLUGIN)
+ if(entry_CLASSNAME)
+ list(FIND added_imports \"${entry_PLUGIN}\" _index)
+ if(_index EQUAL -1)
+ string(APPEND qt5_qml_import_cpp_file_content
+ \"Q_IMPORT_PLUGIN(${entry_CLASSNAME})\n\")
+ list(APPEND added_imports \"${entry_PLUGIN}\")
+ endif()
+ else()
+ message(FATAL_ERROR
+ \"Plugin ${entry_PLUGIN} is missing a classname entry, please add one to the qmldir file.\")
+ endif()
+ endif()
+ endforeach()
+
+ # Write to the generated file, and include it as a source for the given target.
+ set(generated_import_cpp_path
+ \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cpp\")
+ configure_file(\"${Qt5QmlImportScanner_DIR}/Qt5QmlImportScannerTemplate.cpp.in\"
+ \"${generated_import_cpp_path}\"
+ @ONLY)
+ target_sources(${target} PRIVATE \"${generated_import_cpp_path}\")
+ endif()
+!!ENDIF // !isEmpty(CMAKE_STATIC_TYPE)
+endfunction()
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in
new file mode 100644
index 0000000000..4ed747a555
--- /dev/null
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in
@@ -0,0 +1,5 @@
+// This file is autogenerated by CMake. It imports static plugin classes for
+// static plugins used by QML imports.
+#include <QtPlugin>
+
+@qt5_qml_import_cpp_file_content@
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 616de9e80d..c37910bdaf 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -30,8 +30,9 @@
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
#include <private/qv4codegen_p.h>
-#include <private/qv4value_p.h>
+#include <private/qv4staticvalue_p.h>
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
@@ -48,6 +49,8 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QLibraryInfo>
+#include <resourcefilemapper.h>
+
#include <iostream>
#include <algorithm>
@@ -79,13 +82,14 @@ void printUsage(const QString &appNameIn)
#endif
std::wcerr
<< "Usage: " << appName << " -rootPath path/to/app/qml/directory -importPath path/to/qt/qml/directory\n"
- " " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n\n"
+ " " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n"
+ " " << appName << " -qrcFiles file1.qrc file2.qrc -importPath path/to/qt/qml/directory\n\n"
"Example: " << appName << " -rootPath . -importPath "
<< QDir::toNativeSeparators(qmlPath).toStdWString()
<< '\n';
}
-QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, const QString &code, const QString &path)
+QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, const QString &path)
{
QVariantList imports;
@@ -119,7 +123,8 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
if (!name.isEmpty())
import[nameLiteral()] = name;
import[typeLiteral()] = moduleLiteral();
- import[versionLiteral()] = code.mid(importNode->versionToken.offset, importNode->versionToken.length);
+ auto versionString = importNode->version ? QString::number(importNode->version->majorVersion) + QLatin1Char('.') + QString::number(importNode->version->minorVersion) : QString();
+ import[versionLiteral()] = versionString;
}
imports.append(import);
@@ -272,11 +277,11 @@ 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.loc.startLine << ':' << m.message.toStdString() << std::endl;
+ << m.line << ':' << m.message.toStdString() << std::endl;
}
return QVariantList();
}
- return findImportsInAst(parser.ast()->headers, code, filePath);
+ return findImportsInAst(parser.ast()->headers, filePath);
}
// Scan a single qml file for import statements
@@ -494,6 +499,36 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString
return ret;
}
+
+QString generateCmakeIncludeFileContent(const QVariantList &importList) {
+ // The function assumes that "list" is a QVariantList with 0 or more QVariantMaps, where
+ // each map contains QString -> QVariant<QString> mappings. This matches with the structure
+ // that qmake parses for static qml plugin auto imporitng.
+ // So: [ {"a": "a","b": "b"}, {"c": "c"} ]
+ QString content;
+ QTextStream s(&content);
+ int importsCount = 0;
+ for (const QVariant &importVariant: importList) {
+ if (static_cast<QMetaType::Type>(importVariant.type()) == QMetaType::QVariantMap) {
+ s << QStringLiteral("set(qml_import_scanner_import_") << importsCount
+ << QStringLiteral(" \"");
+
+ const QMap<QString, QVariant> &importDict = importVariant.toMap();
+ for (auto it = importDict.cbegin(); it != importDict.cend(); ++it) {
+ s << it.key().toUpper() << QLatin1Char(';')
+ << it.value().toString() << QLatin1Char(';');
+ }
+ s << QStringLiteral("\")\n");
+ ++importsCount;
+ }
+ }
+ if (importsCount >= 0) {
+ content.prepend(QString(QStringLiteral("set(qml_import_scanner_imports_count %1)\n"))
+ .arg(importsCount));
+ }
+ return content;
+}
+
} // namespace
int main(int argc, char *argv[])
@@ -510,6 +545,8 @@ int main(int argc, char *argv[])
QStringList qmlRootPaths;
QStringList scanFiles;
QStringList qmlImportPaths;
+ QStringList qrcFiles;
+ bool generateCmakeContent = false;
int i = 1;
while (i < args.count()) {
@@ -534,6 +571,10 @@ int main(int argc, char *argv[])
if (i >= args.count())
std::cerr << "-importPath requires an argument\n";
argReceiver = &qmlImportPaths;
+ } else if (arg == QLatin1String("-cmake-output")) {
+ generateCmakeContent = true;
+ } else if (arg == QLatin1String("-qrcFiles")) {
+ argReceiver = &qrcFiles;
} else {
std::cerr << qPrintable(appName) << ": Invalid argument: \""
<< qPrintable(arg) << "\"\n";
@@ -555,13 +596,23 @@ int main(int argc, char *argv[])
}
}
+ if (!qrcFiles.isEmpty())
+ scanFiles << ResourceFileMapper(qrcFiles).qmlCompilerFiles(ResourceFileMapper::FileOutput::AbsoluteFilePath);
+
g_qmlImportPaths = qmlImportPaths;
// Find the imports!
QVariantList imports = findQmlImportsRecursively(qmlRootPaths, scanFiles);
- // Convert to JSON
- QByteArray json = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
- std::cout << json.constData() << std::endl;
+ QByteArray content;
+ if (generateCmakeContent) {
+ // Convert to CMake code
+ content = generateCmakeIncludeFileContent(imports).toUtf8();
+ } else {
+ // Convert to JSON
+ content = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
+ }
+
+ std::cout << content.constData() << std::endl;
return 0;
}
diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro
index 0b3a03abf3..d69f1a3b0b 100644
--- a/tools/qmlimportscanner/qmlimportscanner.pro
+++ b/tools/qmlimportscanner/qmlimportscanner.pro
@@ -4,6 +4,52 @@ QT = core qmldevtools-private
DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES += main.cpp
+include(../shared/shared.pri)
+
+load(cmake_functions)
+
+CMAKE_BIN_DIR = $$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])
+contains(CMAKE_BIN_DIR, "^\\.\\./.*") {
+ CMAKE_BIN_DIR = $$[QT_HOST_BINS]/
+ CMAKE_BIN_DIR_IS_ABSOLUTE = True
+}
+
+CMAKE_QML_DIR = $$cmakeRelativePath($$[QT_INSTALL_QML/get], $$[QT_INSTALL_PREFIX])
+contains(CMAKE_QML_DIR, "^\\.\\./.*") {
+ CMAKE_QML_DIR = $$[QT_INSTALL_QML/get]/
+ CMAKE_QML_DIR_IS_ABSOLUTE = True
+}
+load(qt_build_paths)
+
+static|staticlib:CMAKE_STATIC_TYPE = true
+
+# Compute the platform target suffix.
+CMAKE_QML_PLUGIN_SUFFIX_RELEASE =
+win32: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = d
+else:darwin: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = _debug
+else: CMAKE_QML_PLUGIN_SUFFIX_DEBUG =
+
+# Find out which configurations should be handled in the generated Config.cmake file.
+CMAKE_DEBUG_TYPE =
+CMAKE_RELEASE_TYPE =
+if(qtConfig(debug_and_release)|contains(QT_CONFIG, debug, debug|release)): CMAKE_DEBUG_TYPE = debug
+if(qtConfig(debug_and_release)|contains(QT_CONFIG, release, debug|release)): CMAKE_RELEASE_TYPE = release
+
+qtConfig(debug_and_release) {
+ CMAKE_DEBUG_AND_RELEASE = TRUE
+} else {
+ CMAKE_DEBUG_AND_RELEASE = FALSE
+}
+
+equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe"
+cmake_config_file.input = $$PWD/Qt5QmlImportScannerConfig.cmake.in
+cmake_config_file.output = $$MODULE_BASE_OUTDIR/lib/cmake/Qt5QmlImportScanner/Qt5QmlImportScannerConfig.cmake
+QMAKE_SUBSTITUTES += cmake_config_file
+
+cmake_build_integration.files = $$cmake_config_file.output $$PWD/Qt5QmlImportScannerTemplate.cpp.in
+cmake_build_integration.path = $$[QT_INSTALL_LIBS]/cmake/Qt5QmlImportScanner
+prefix_build: INSTALLS += cmake_build_integration
+else: COPIES += cmake_build_integration
QMAKE_TARGET_DESCRIPTION = QML Import Scanner
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index 4b581fff05..ba5e5f553c 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -137,7 +137,8 @@ int main(int argc, char *argv[])
}
QScopedPointer<QV4::Script> script;
if (cache && QFile::exists(fn + QLatin1Char('c'))) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
QString error;
if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), &error)) {
script.reset(new QV4::Script(&vm, nullptr, unit));
diff --git a/tools/qmllint/componentversion.cpp b/tools/qmllint/componentversion.cpp
new file mode 100644
index 0000000000..3dc4ac37d0
--- /dev/null
+++ b/tools/qmllint/componentversion.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "componentversion.h"
+
+#include <QString>
+#include <QCryptographicHash>
+
+#include <limits>
+
+using namespace LanguageUtils;
+
+const int ComponentVersion::NoVersion = -1;
+const int ComponentVersion::MaxVersion = std::numeric_limits<int>::max();
+
+ComponentVersion::ComponentVersion()
+ : _major(NoVersion), _minor(NoVersion)
+{
+}
+
+ComponentVersion::ComponentVersion(int major, int minor)
+ : _major(major), _minor(minor)
+{
+}
+
+ComponentVersion::ComponentVersion(const QString &versionString)
+ : _major(NoVersion), _minor(NoVersion)
+{
+ int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx == -1)
+ return;
+ bool ok = false;
+ int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok);
+ if (!ok)
+ return;
+ int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok);
+ if (!ok)
+ return;
+ _major = maybeMajor;
+ _minor = maybeMinor;
+}
+
+ComponentVersion::~ComponentVersion()
+{
+}
+
+bool ComponentVersion::isValid() const
+{
+ return _major >= 0 && _minor >= 0;
+}
+
+QString ComponentVersion::toString() const
+{
+ return QString::fromLatin1("%1.%2").arg(QString::number(_major),
+ QString::number(_minor));
+}
+
+void ComponentVersion::addToHash(QCryptographicHash &hash) const
+{
+ hash.addData(reinterpret_cast<const char *>(&_major), sizeof(_major));
+ hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
+}
+
+namespace LanguageUtils {
+
+bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.majorVersion() < rhs.majorVersion()
+ || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() < rhs.minorVersion());
+}
+
+bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.majorVersion() < rhs.majorVersion()
+ || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion());
+}
+
+bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return rhs < lhs;
+}
+
+bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return rhs <= lhs;
+}
+
+bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.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
new file mode 100644
index 0000000000..9d079f1d30
--- /dev/null
+++ b/tools/qmllint/componentversion.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 COMPONENTVERSION_H
+#define COMPONENTVERSION_H
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+class QCryptographicHash;
+QT_END_NAMESPACE
+
+namespace LanguageUtils {
+
+class ComponentVersion
+{
+ int _major;
+ int _minor;
+
+public:
+ static const int NoVersion;
+ static const int MaxVersion;
+
+ ComponentVersion();
+ ComponentVersion(int major, int minor);
+ explicit ComponentVersion(const QString &versionString);
+ ~ComponentVersion();
+
+ int majorVersion() const
+ { return _major; }
+ int minorVersion() const
+ { return _minor; }
+
+ bool isValid() const;
+ QString toString() const;
+ void addToHash(QCryptographicHash &hash) const;
+};
+
+bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+
+} // namespace LanguageUtils
+
+#endif // COMPONENTVERSION_H
diff --git a/tools/qmllint/fakemetaobject.cpp b/tools/qmllint/fakemetaobject.cpp
new file mode 100644
index 0000000000..514bb2fe42
--- /dev/null
+++ b/tools/qmllint/fakemetaobject.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+**
+** 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
new file mode 100644
index 0000000000..4e0ea1f8b3
--- /dev/null
+++ b/tools/qmllint/fakemetaobject.h
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** 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
new file mode 100644
index 0000000000..49d64adb6e
--- /dev/null
+++ b/tools/qmllint/findunqualified.cpp
@@ -0,0 +1,783 @@
+/****************************************************************************
+**
+** 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 "findunqualified.h"
+#include "scopetree.h"
+
+#include "qmljstypedescriptionreader.h"
+
+#include <QFile>
+#include <QDirIterator>
+#include <QScopedValueRollback>
+
+#include <private/qqmljsast_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qv4codegen_p.h>
+
+QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc);
+
+static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename)
+{
+ QFile f(filename);
+ f.open(QFile::ReadOnly);
+ QQmlJS::TypeDescriptionReader reader { filename, f.readAll() };
+ return reader;
+}
+
+void FindUnqualifiedIDVisitor::enterEnvironment(ScopeType type, QString name)
+{
+ m_currentScope = m_currentScope->createNewChildScope(type, name);
+}
+
+void FindUnqualifiedIDVisitor::leaveEnvironment()
+{
+ m_currentScope = m_currentScope->parentScope();
+}
+
+enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned };
+
+QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePaths, 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);
+
+ QStringList qmlDirPathsPaths;
+ // fully & partially versioned parts + 1 unversioned for each base path
+ qmlDirPathsPaths.reserve(basePaths.count() * (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) {
+ // extension with encoded version major (eg. MyModule.3)
+ return QString::asprintf(".%d", vmaj);
+ } // else extension without version number (eg. MyModule)
+ return QString();
+ };
+ auto joinStringRefs = [](const QVector<QStringRef> &refs, const QChar &sep)
+ {
+ QString str;
+ for (auto it = refs.cbegin(); it != refs.cend(); ++it) {
+ if (it != refs.cbegin())
+ str += sep;
+ str += *it;
+ }
+ return str;
+ };
+
+ for (int version = FullyVersioned; version <= Unversioned; ++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;
+
+ // 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;
+ }
+ }
+ }
+ }
+
+ return qmlDirPathsPaths;
+}
+
+void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int major, int minor)
+{
+ 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);
+
+ 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;
+ }
+ }
+ for (auto const &dependency : qAsConst(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);
+ }
+ // 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());
+ }
+ }
+}
+
+LanguageUtils::FakeMetaObject *
+FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
+{
+ using namespace QQmlJS::AST;
+ auto fake = new LanguageUtils::FakeMetaObject;
+ fake->setClassName(QFileInfo { filePath }.baseName());
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly)) {
+ return fake;
+ }
+ QString code = file.readAll();
+ file.close();
+
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ lexer.setCode(code, 1, true);
+ QQmlJS::Parser parser(&engine);
+ if (!parser.parse()) {
+ return fake;
+ }
+ 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;
+ }
+ 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;
+ }
+ return fake;
+}
+
+void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString name)
+{
+ 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);
+
+ name = metaObject->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_unknownImports.insert(name);
+ break;
+ }
+ }
+}
+
+void FindUnqualifiedIDVisitor::throwRecursionDepthError()
+{
+ m_colorOut.write(QStringLiteral("Error"), Error);
+ m_colorOut.write(QStringLiteral("Maximum statement or expression depth exceeded"), Error);
+ m_visitFailed = true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
+{
+ enterEnvironment(ScopeType::QMLScope, "program");
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects;
+ QList<QQmlJS::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 succ = reader(&objects, &moduleApis, &dependencies);
+ if (!succ) {
+ qDebug() << 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());
+ }
+ }
+ // 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
+
+ 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 };
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiProgram *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ClassExpression *ast)
+{
+ enterEnvironment(ScopeType::JSFunctionScope, ast->name.toString());
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ClassExpression *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
+{
+ enterEnvironment(ScopeType::JSFunctionScope, ast->name.toString());
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ForStatement *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "forloop");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ForStatement *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ForEachStatement *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "foreachloop");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Block *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "block");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Block *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::CaseBlock *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "case");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::CaseBlock *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *catchStatement)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "catch");
+ m_currentScope->insertJSIdentifier(catchStatement->patternElement->bindingIdentifier.toString(), QQmlJS::AST::VariableScope::Let);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Catch *)
+{
+ leaveEnvironment();
+}
+
+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);
+ enterEnvironment(ScopeType::JSLexicalScope, "with");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::WithStatement *)
+{
+ leaveEnvironment();
+}
+
+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);
+ QString elementName = m_currentScope->name();
+ m_qmlid2meta.insert(identexp->name.toString(), m_exportedName2MetaObject[elementName]);
+ if (m_currentScope->isVisualRootScope()) {
+ m_rootId = identexp->name.toString();
+ }
+ } else if (name.startsWith("on") && name.size() > 2 && name.at(2).isUpper()) {
+ auto statement = uisb->statement;
+ if (statement->kind == Node::Kind::Kind_ExpressionStatement) {
+ if (static_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);
+ }
+ }
+ return true;
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiPublicMember *uipm)
+{
+ // property bool inactive: !active
+ // extract name inactive
+ m_currentScope->insertPropertyIdentifier(uipm->name.toString());
+ 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);
+ }
+ return true;
+}
+
+FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs,
+ const QString &code, const QString &fileName)
+ : m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }),
+ m_currentScope(m_rootScope.get()),
+ m_qmltypeDirs(qmltypeDirs),
+ m_code(code),
+ m_rootId(QLatin1String("<id>")),
+ m_filePath(fileName)
+{
+ // 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);
+ QLatin1String jsGlobVars[] = {
+ /* Not listed on the MDN page; browser and QML extensions: */
+ // console/debug api
+ QLatin1String("console"), QLatin1String("print"),
+ // garbage collector
+ QLatin1String("gc"),
+ // i18n
+ 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 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);
+ 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);
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
+{
+ while (vdl) {
+ m_currentScope->insertJSIdentifier(vdl->declaration->bindingIdentifier.toString(),
+ vdl->declaration->scope);
+ vdl = vdl->next;
+ }
+ return true;
+}
+
+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 {
+ m_currentScope->insertJSIdentifier(name, VariableScope::Const);
+ }
+ }
+ QString name = fexpr->name.toString();
+ if (name.isEmpty())
+ name = "<anon>";
+ enterEnvironment(ScopeType::JSFunctionScope, name);
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
+{
+ visitFunctionExpressionHelper(fexpr);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::FunctionExpression *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
+{
+ visitFunctionExpressionHelper(fdecl);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
+{
+ for (auto const &boundName : fpl->boundNames()) {
+ m_currentScope->insertJSIdentifier(boundName.id, QQmlJS::AST::VariableScope::Const);
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
+{
+ // construct path
+ QString prefix = QLatin1String("");
+ if (import->asToken.isValid()) {
+ prefix += import->importId + QLatin1Char('.');
+ }
+ 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));
+ }
+ }
+ 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
+ }
+ if (import->version) {
+ auto uri = import->importUri;
+ while (uri) {
+ path.append(uri->name);
+ path.append("/");
+ uri = uri->next;
+ }
+ path.chop(1);
+
+ importHelper(path, prefix, import->version->majorVersion, import->version->minorVersion);
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
+{
+ m_currentScope->insertQMLIdentifier(uied->name.toString());
+ 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;
+ while (id) {
+ name += id->name.toString() + QLatin1Char('.');
+ id = id->next;
+ }
+ name.chop(1);
+ 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 *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
+{
+ QString name {};
+ auto id = uiod->qualifiedTypeNameId;
+ QStringRef prefix = uiod->qualifiedTypeNameId->name;
+ while (id) {
+ name += id->name.toString() + QLatin1Char('.');
+ id = id->next;
+ }
+ name.chop(1);
+ 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;
+ auto member = uiod->initializer->members;
+ while (member) {
+ if (member->member->kind == QQmlJS::AST::Node::Kind_UiScriptBinding) {
+ auto asBinding = static_cast<QQmlJS::AST::UiScriptBinding*>(member->member);
+ if (asBinding->qualifiedId->name == QLatin1String("target")) {
+ if (asBinding->statement->kind == QQmlJS::AST::Node::Kind_ExpressionStatement) {
+ auto expr = static_cast<QQmlJS::AST::ExpressionStatement*>(asBinding->statement)->expression;
+ if (auto idexpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression*>(expr)) {
+ target = idexpr->name.toString();
+ } else {
+ // more complex expressions are not supported
+ }
+ }
+ break;
+ }
+ }
+ member = member->next;
+ }
+ LanguageUtils::FakeMetaObject::ConstPtr metaObject {};
+ 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()];
+ } 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;
+ } else {
+ m_outstandingConnections.push_back({target, m_currentScope, uiod});
+ return false; // visit children later once target is known
+ }
+ }
+ m_currentScope->addMethodsFromMetaObject(metaObject);
+ }
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
+{
+ leaveEnvironment();
+}
+
+QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << loc.startLine;
+ dbg.nospace() << ":";
+ dbg.nospace() << loc.startColumn;
+ return dbg.maybeSpace();
+}
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
new file mode 100644
index 0000000000..181f42f265
--- /dev/null
+++ b/tools/qmllint/findunqualified.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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 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 {
+
+public:
+ explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, const QString& fileName);
+ ~FindUnqualifiedIDVisitor() override;
+ bool check();
+
+private:
+ QScopedPointer<ScopeTree> m_rootScope;
+ ScopeTree *m_currentScope;
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_exportedName2MetaObject;
+ QStringList m_qmltypeDirs;
+ const QString& m_code;
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_qmlid2meta;
+ QString m_rootId;
+ QString m_filePath;
+ QSet<QPair<QString, QString>> m_alreadySeenImports;
+ QSet<QString> m_unknownImports;
+ ColorOutput m_colorOut;
+ bool m_visitFailed = false;
+
+ 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 leaveEnvironment();
+ void importHelper(QString id, QString prefix, int major, int minor);
+ LanguageUtils::FakeMetaObject* localQmlFile2FakeMetaObject(QString filePath);
+
+
+ void importExportedNames(QStringRef prefix, QString name);
+
+ void throwRecursionDepthError() override;
+
+ // start block/scope handling
+ bool visit(QQmlJS::AST::UiProgram *ast) override;
+ void endVisit(QQmlJS::AST::UiProgram *ast) override;
+
+ bool visit(QQmlJS::AST::ClassExpression *ast) override;
+ void endVisit(QQmlJS::AST::ClassExpression *ast) override;
+
+ bool visit(QQmlJS::AST::ClassDeclaration *ast) override;
+ void endVisit(QQmlJS::AST::ClassDeclaration *ast) override;
+
+ bool visit(QQmlJS::AST::ForStatement *ast) override;
+ void endVisit(QQmlJS::AST::ForStatement *ast) override;
+
+ bool visit(QQmlJS::AST::ForEachStatement *ast) override;
+ void endVisit(QQmlJS::AST::ForEachStatement *ast) override;
+
+ bool visit(QQmlJS::AST::Block *ast) override;
+ void endVisit(QQmlJS::AST::Block *ast) override;
+
+ bool visit(QQmlJS::AST::CaseBlock *ast) override;
+ void endVisit(QQmlJS::AST::CaseBlock *ast) override;
+
+ bool visit(QQmlJS::AST::Catch *ast) override;
+ void endVisit(QQmlJS::AST::Catch *ast) override;
+
+ bool visit(QQmlJS::AST::WithStatement *withStatement) override;
+ void endVisit(QQmlJS::AST::WithStatement *ast) override;
+
+ void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr);
+ bool visit(QQmlJS::AST::FunctionExpression *fexpr) override;
+ void endVisit(QQmlJS::AST::FunctionExpression *fexpr) override;
+
+ bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override;
+ void endVisit(QQmlJS::AST::FunctionDeclaration *fdecl) override;
+ /* --- end block handling --- */
+
+ bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override;
+ bool visit(QQmlJS::AST::FormalParameterList *fpl) override;
+
+ bool visit(QQmlJS::AST::UiImport *import) override;
+ bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override;
+ bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
+ void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override;
+ bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *uisb) override;
+ bool visit(QQmlJS::AST::UiPublicMember *uipm) override;
+
+ // expression handling
+ bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
+};
+
+
+#endif // FINDUNQUALIFIED_H
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 791fb71685..235ec16c6e 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -34,12 +34,19 @@
#endif
#include <QCoreApplication>
-#include <private/qv4value_p.h>
+#ifndef QT_BOOTSTRAPPED
+#include <QLibraryInfo>
+#endif
+
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsengine_p.h>
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+
+#include "findunqualified.h"
-static bool lint_file(const QString &filename, bool silent)
+static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied, QStringList const &qmltypeDirs)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
@@ -63,10 +70,17 @@ static bool lint_file(const QString &filename, bool silent)
if (!success && !silent) {
const auto diagnosticMessages = parser.diagnosticMessages();
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- qWarning("%s:%d : %s", qPrintable(filename), m.loc.startLine, qPrintable(m.message));
+ qWarning("%s:%d : %s", qPrintable(filename), m.line, qPrintable(m.message));
}
}
+ if (success && !isJavaScript && warnUnqualied) {
+ auto root = parser.rootNode();
+ FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename};
+ root->accept(&v);
+ success = v.check();
+ }
+
return success;
}
@@ -80,8 +94,20 @@ int main(int argv, char *argc[])
parser.setApplicationDescription(QLatin1String("QML syntax verifier"));
parser.addHelpOption();
parser.addVersionOption();
+
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"));
+ parser.addOption(checkUnqualified);
+
+ QCommandLineOption qmltypesDirsOption(
+ QStringList() << "I"
+ << "qmldirs",
+ QLatin1String("Look for qmltypes files in specified directory"),
+ QLatin1String("directory"));
+ parser.addOption(qmltypesDirsOption);
+
parser.addPositionalArgument(QLatin1String("files"), QLatin1String("list of qml or js files to verify"));
parser.process(app);
@@ -92,8 +118,18 @@ 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
#else
bool silent = false;
+ bool warnUnqualified = false;
+ QStringList qmltypeDirs {};
#endif
bool success = true;
#if QT_CONFIG(commandlineparser)
@@ -102,7 +138,7 @@ int main(int argv, char *argc[])
const auto arguments = app.arguments();
for (const QString &filename : arguments)
#endif
- success &= lint_file(filename, silent);
+ success &= lint_file(filename, silent, warnUnqualified, qmltypeDirs);
return success ? 0 : -1;
}
diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp
new file mode 100644
index 0000000000..d2e723700a
--- /dev/null
+++ b/tools/qmllint/qcoloroutput.cpp
@@ -0,0 +1,342 @@
+/****************************************************************************
+**
+** 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 <QFile>
+#include <QHash>
+#include <QTextCodec>
+
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#endif
+
+#include "qcoloroutput_p.h"
+
+class ColorOutputPrivate
+{
+public:
+ ColorOutputPrivate() : currentColorID(-1)
+
+ {
+ /* - 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();
+ }
+
+ 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());
+ }
+
+ static QString escapeCode(const QString &in)
+ {
+ QString result;
+ result.append(QChar(0x1B));
+ result.append(QLatin1Char('['));
+ result.append(in);
+ result.append(QLatin1Char('m'));
+ return result;
+ }
+
+private:
+ QFile m_out;
+
+ /*!
+ 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
+ }
+};
+
+const char *const ColorOutputPrivate::foregrounds[] =
+{
+ "0;30",
+ "0;34",
+ "0;32",
+ "0;36",
+ "0;31",
+ "0;35",
+ "0;33",
+ "0;37",
+ "1;30",
+ "1;34",
+ "1;32",
+ "1;36",
+ "1;31",
+ "1;35",
+ "1;33",
+ "1;37"
+};
+
+const char *const ColorOutputPrivate::backgrounds[] =
+{
+ "0;40",
+ "0;44",
+ "0;42",
+ "0;46",
+ "0;41",
+ "0;45",
+ "0;43"
+};
+
+/*!
+ \class ColorOutput
+ \since 4.4
+ \nonreentrant
+ \brief Outputs colored messages to \c stderr.
+ \internal
+
+ ColorOutput is a convenience class for outputting messages to \c
+ stderr using color escape codes, as mandated in ECMA-48. ColorOutput
+ will only color output when it is detected to be suitable. For
+ instance, if \c stderr is detected to be attached to a file instead
+ of a TTY, no coloring will be done.
+
+ ColorOutput does its best attempt. but it is generally undefined
+ what coloring or effect the various coloring flags has. It depends
+ strongly on what terminal software that is being used.
+
+ When using `echo -e 'my escape sequence'`, \c{\033} works as an
+ initiator but not when printing from a C++ program, despite having
+ escaped the backslash. That's why we below use characters with
+ value 0x1B.
+
+ It can be convenient to subclass ColorOutput with a private scope,
+ such that the functions are directly available in the class using
+ it.
+
+ \section1 Usage
+
+ To output messages, call write() or writeUncolored(). write() takes
+ as second argument an integer, which ColorOutput uses as a lookup
+ key to find the color it should color the text in. The mapping from
+ keys to colors is done using insertMapping(). Typically this is used
+ by having enums for the various kinds of messages, which
+ subsequently are registered.
+
+ \code
+ enum MyMessage
+ {
+ Error,
+ Important
+ };
+
+ ColorOutput output;
+ output.insertMapping(Error, ColorOutput::RedForeground);
+ output.insertMapping(Import, ColorOutput::BlueForeground);
+
+ output.write("This is important", Important);
+ output.write("Jack, I'm only the selected official!", Error);
+ \endcode
+
+ \sa {http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html}{Bash Prompt HOWTO, 6.1. Colors},
+ {http://linuxgazette.net/issue51/livingston-blade.html}{Linux Gazette, Tweaking Eterm, Edward Livingston-Blade},
+ {http://www.ecma-international.org/publications/standards/Ecma-048.htm}{Standard ECMA-48, Control Functions for Coded Character Sets, ECMA International},
+ {http://en.wikipedia.org/wiki/ANSI_escape_code}{Wikipedia, ANSI escape code},
+ {http://linuxgazette.net/issue65/padala.html}{Linux Gazette, So You Like Color!, Pradeep Padala}
+ */
+
+/*!
+ \enum ColorOutput::ColorCodeComponent
+ \value BlackForeground
+ \value BlueForeground
+ \value GreenForeground
+ \value CyanForeground
+ \value RedForeground
+ \value PurpleForeground
+ \value BrownForeground
+ \value LightGrayForeground
+ \value DarkGrayForeground
+ \value LightBlueForeground
+ \value LightGreenForeground
+ \value LightCyanForeground
+ \value LightRedForeground
+ \value LightPurpleForeground
+ \value YellowForeground
+ \value WhiteForeground
+ \value BlackBackground
+ \value BlueBackground
+ \value GreenBackground
+ \value CyanBackground
+ \value RedBackground
+ \value PurpleBackground
+ \value BrownBackground
+
+ \value DefaultColor ColorOutput performs no coloring. This typically
+ means black on white or white on black, depending
+ on the settings of the user's terminal.
+ */
+
+/*!
+ Sets the color mapping to be \a cMapping.
+
+ Negative values are disallowed.
+
+ \sa colorMapping(), insertMapping()
+ */
+void ColorOutput::setColorMapping(const ColorMapping &cMapping)
+{
+ d->colorMapping = cMapping;
+}
+
+/*!
+ Returns the color mappings in use.
+
+ \sa setColorMapping(), insertMapping()
+ */
+ColorOutput::ColorMapping ColorOutput::colorMapping() const
+{
+ return d->colorMapping;
+}
+
+/*!
+ Constructs a ColorOutput instance, ready for use.
+ */
+ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
+{
+}
+
+ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer has access to the complete type
+
+/*!
+ Sends \a message to \c stderr, using the color looked up in colorMapping() using \a colorID.
+
+ If \a color isn't available in colorMapping(), 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.
+
+ If \a message is empty, effects are undefined.
+
+ \a message will be printed as is. For instance, no line endings will be inserted.
+ */
+void ColorOutput::write(const QString &message, int colorID)
+{
+ d->write(colorify(message, colorID));
+}
+
+/*!
+ Writes \a message to \c stderr as if for instance
+ QTextStream would have been used, and adds a line ending at the end.
+
+ This function can be practical to use such that one can use ColorOutput for all forms of writing.
+ */
+void ColorOutput::writeUncolored(const QString &message)
+{
+ d->write(message + QLatin1Char('\n'));
+}
+
+/*!
+ Treats \a message and \a colorID identically to write(), but instead of writing
+ \a message to \c stderr, it is prepared for being written to \c stderr, but is then
+ returned.
+
+ This is useful when the colored string is inserted into a translated string(dividing
+ the string into several small strings prevents proper translation).
+ */
+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.");
+
+ if (colorID != -1)
+ d->currentColorID = colorID;
+
+ if (d->coloringEnabled && colorID != -1)
+ {
+ const int color(d->colorMapping.value(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;
+ QString finalMessage;
+ bool closureNeeded = false;
+
+ if (foregroundCode)
+ {
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
+ closureNeeded = true;
+ }
+
+ if (backgroundCode)
+ {
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
+ closureNeeded = true;
+ }
+
+ finalMessage.append(message);
+
+ if (closureNeeded)
+ {
+ finalMessage.append(QChar(0x1B));
+ finalMessage.append(QLatin1String("[0m"));
+ }
+
+ return finalMessage;
+ }
+ else
+ 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)
+{
+ d->colorMapping.insert(colorID, colorCode);
+}
diff --git a/tools/qmllint/qcoloroutput_p.h b/tools/qmllint/qcoloroutput_p.h
new file mode 100644
index 0000000000..710bf5db74
--- /dev/null
+++ b/tools/qmllint/qcoloroutput_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+//
+// 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.
+
+#ifndef QCOLOROUTPUT_P_H
+#define QCOLOROUTPUT_P_H
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QHash>
+#include <QScopedPointer>
+
+class ColorOutputPrivate;
+
+class ColorOutput
+{
+ enum
+ {
+ ForegroundShift = 10,
+ BackgroundShift = 20,
+ SpecialShift = 20,
+ ForegroundMask = 0x1f << ForegroundShift,
+ BackgroundMask = 0x7 << BackgroundShift
+ };
+
+public:
+ enum ColorCodeComponent
+ {
+ BlackForeground = 1 << ForegroundShift,
+ BlueForeground = 2 << ForegroundShift,
+ GreenForeground = 3 << ForegroundShift,
+ CyanForeground = 4 << ForegroundShift,
+ RedForeground = 5 << ForegroundShift,
+ PurpleForeground = 6 << ForegroundShift,
+ BrownForeground = 7 << ForegroundShift,
+ LightGrayForeground = 8 << ForegroundShift,
+ DarkGrayForeground = 9 << ForegroundShift,
+ LightBlueForeground = 10 << ForegroundShift,
+ LightGreenForeground = 11 << ForegroundShift,
+ LightCyanForeground = 12 << ForegroundShift,
+ LightRedForeground = 13 << ForegroundShift,
+ LightPurpleForeground = 14 << ForegroundShift,
+ YellowForeground = 15 << ForegroundShift,
+ WhiteForeground = 16 << ForegroundShift,
+
+ BlackBackground = 1 << BackgroundShift,
+ BlueBackground = 2 << BackgroundShift,
+ GreenBackground = 3 << BackgroundShift,
+ CyanBackground = 4 << BackgroundShift,
+ RedBackground = 5 << BackgroundShift,
+ PurpleBackground = 6 << BackgroundShift,
+ BrownBackground = 7 << BackgroundShift,
+ DefaultColor = 1 << SpecialShift
+ };
+
+ typedef QFlags<ColorCodeComponent> ColorCode;
+ typedef QHash<int, ColorCode> ColorMapping;
+
+ ColorOutput();
+ ~ColorOutput();
+
+ void setColorMapping(const ColorMapping &cMapping);
+ ColorMapping colorMapping() const;
+ void insertColorMapping(int colorID, const ColorCode colorCode);
+
+ void writeUncolored(const QString &message);
+ void write(const QString &message, int color = -1);
+ QString colorify(const QString &message, int color = -1) const;
+
+private:
+ QScopedPointer<ColorOutputPrivate> d;
+ Q_DISABLE_COPY(ColorOutput)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ColorOutput::ColorCode)
+
+#endif
diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/qmljstypedescriptionreader.cpp
new file mode 100644
index 0000000000..542cdf99eb
--- /dev/null
+++ b/tools/qmllint/qmljstypedescriptionreader.cpp
@@ -0,0 +1,704 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmljstypedescriptionreader.h"
+
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsengine_p.h>
+
+#include <QDir>
+
+#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
+#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
+#define QTC_ASSERT_STRING(cond) qDebug() << (\
+ "\"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
+
+using namespace QQmlJS;
+using namespace QQmlJS::AST;
+using namespace LanguageUtils;
+
+QString toString(const AST::UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
+{
+ QString result;
+
+ for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
+ if (iter != qualifiedId)
+ result += delimiter;
+
+ result += iter->name;
+ }
+
+ return result;
+}
+
+TypeDescriptionReader::TypeDescriptionReader(const QString &fileName, const QString &data)
+ : _fileName (fileName), _source(data), _objects(0)
+{
+}
+
+TypeDescriptionReader::~TypeDescriptionReader()
+{
+}
+
+bool TypeDescriptionReader::operator()(
+ QHash<QString, FakeMetaObject::ConstPtr> *objects,
+ QList<ModuleApiInfo> *moduleApis,
+ QStringList *dependencies)
+{
+ Engine engine;
+
+ Lexer lexer(&engine);
+ Parser parser(&engine);
+
+ lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true);
+
+ if (!parser.parse()) {
+ _errorMessage = QString::fromLatin1("%1:%2: %3").arg(
+ QString::number(parser.errorLineNumber()),
+ QString::number(parser.errorColumnNumber()),
+ parser.errorMessage());
+ return false;
+ }
+
+ _objects = objects;
+ _moduleApis = moduleApis;
+ _dependencies = dependencies;
+ readDocument(parser.ast());
+
+ return _errorMessage.isEmpty();
+}
+
+QString TypeDescriptionReader::errorMessage() const
+{
+ return _errorMessage;
+}
+
+QString TypeDescriptionReader::warningMessage() const
+{
+ return _warningMessage;
+}
+
+void TypeDescriptionReader::readDocument(UiProgram *ast)
+{
+ if (!ast) {
+ addError(SourceLocation(), tr("Could not parse document."));
+ return;
+ }
+
+ if (!ast->headers || ast->headers->next || !AST::cast<AST::UiImport *>(ast->headers->headerItem)) {
+ addError(SourceLocation(), tr("Expected a single import."));
+ return;
+ }
+
+ UiImport *import = AST::cast<AST::UiImport *>(ast->headers->headerItem);
+ if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
+ addError(import->importToken, tr("Expected import of QtQuick.tooling."));
+ return;
+ }
+
+ ComponentVersion version;
+ const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
+ const int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1) {
+ version = ComponentVersion(versionString.leftRef(dotIdx).toInt(),
+ versionString.midRef(dotIdx + 1).toInt());
+ }
+ if (version.majorVersion() != 1) {
+ addError(import->versionToken, tr("Major version different from 1 not supported."));
+ return;
+ }
+
+ if (!ast->members || !ast->members->member || ast->members->next) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ UiObjectDefinition *module = AST::cast<UiObjectDefinition *>(ast->members->member);
+ if (!module) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
+ addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
+ return;
+ }
+
+ readModule(module);
+}
+
+void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
+{
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
+ readDependencies(script);
+ continue;
+ }
+
+ QString typeName;
+ if (component)
+ typeName = toString(component->qualifiedTypeNameId);
+
+ if (!component || (typeName != QLatin1String("Component") && typeName != QLatin1String("ModuleApi"))) {
+ continue;
+ }
+
+ if (typeName == QLatin1String("Component"))
+ readComponent(component);
+ else if (typeName == QLatin1String("ModuleApi"))
+ readModuleApi(component);
+ }
+}
+
+void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
+{
+ _errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
+{
+ _warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
+{
+ ExpressionStatement *stmt = AST::cast<ExpressionStatement*>(ast->statement);
+ if (!stmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ ArrayPattern *exp = AST::cast<ArrayPattern *>(stmt->expression);
+ if (!exp) {
+ addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ for (PatternElementList *l = exp->elements; l; l = l->next) {
+ //StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
+ StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
+ *_dependencies << str->value.toString();
+ }
+}
+
+void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
+{
+ FakeMetaObject::Ptr fmo(new FakeMetaObject);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Property"))
+ readProperty(component, fmo);
+ else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
+ readSignalOrMethod(component, name == QLatin1String("Method"), fmo);
+ else if (name == QLatin1String("Enum"))
+ readEnum(component, fmo);
+ else
+ addWarning(component->firstSourceLocation(),
+ tr("Expected only Property, Method, Signal and Enum object definitions, not \"%1\".")
+ .arg(name));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name")) {
+ fmo->setClassName(readStringBinding(script));
+ } else if (name == QLatin1String("prototype")) {
+ fmo->setSuperclassName(readStringBinding(script));
+ } else if (name == QLatin1String("defaultProperty")) {
+ fmo->setDefaultPropertyName(readStringBinding(script));
+ } else if (name == QLatin1String("exports")) {
+ readExports(script, fmo);
+ } else if (name == QLatin1String("exportMetaObjectRevisions")) {
+ readMetaObjectRevisions(script, fmo);
+ } else if (name == QLatin1String("attachedType")) {
+ fmo->setAttachedTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("isSingleton")) {
+ fmo->setIsSingleton(readBoolBinding(script));
+ } else if (name == QLatin1String("isCreatable")) {
+ fmo->setIsCreatable(readBoolBinding(script));
+ } else if (name == QLatin1String("isComposite")) {
+ fmo->setIsComposite(readBoolBinding(script));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name, prototype, defaultProperty, attachedType, exports, "
+ "isSingleton, isCreatable, isComposite and exportMetaObjectRevisions "
+ "script bindings, not \"%1\".").arg(name));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (fmo->className().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
+ return;
+ }
+
+ // ### add implicit export into the package of c++ types
+ fmo->addExport(fmo->className(), QStringLiteral("<cpp>"), ComponentVersion());
+ fmo->updateFingerprint();
+ _objects->insert(fmo->className(), fmo);
+}
+
+void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
+{
+ ModuleApiInfo apiInfo;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+
+ if (script) {
+ const QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("uri")) {
+ apiInfo.uri = readStringBinding(script);
+ } else if (name == QLatin1String("version")) {
+ apiInfo.version = readNumericVersionBinding(script);
+ } else if (name == QLatin1String("name")) {
+ apiInfo.cppName = readStringBinding(script);
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only uri, version and name script bindings."));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings."));
+ }
+ }
+
+ if (!apiInfo.version.isValid()) {
+ addError(ast->firstSourceLocation(), tr("ModuleApi definition has no or invalid version binding."));
+ return;
+ }
+
+ if (_moduleApis)
+ _moduleApis->append(apiInfo);
+}
+
+void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
+{
+ FakeMetaMethod fmm;
+ // ### confusion between Method and Slot. Method should be removed.
+ if (isMethod)
+ fmm.setMethodType(FakeMetaMethod::Slot);
+ else
+ fmm.setMethodType(FakeMetaMethod::Signal);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Parameter"))
+ readParameter(component, &fmm);
+ else
+ addWarning(component->firstSourceLocation(), tr("Expected only Parameter object definitions."));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name"))
+ fmm.setMethodName(readStringBinding(script));
+ else if (name == QLatin1String("type"))
+ fmm.setReturnType(readStringBinding(script));
+ else if (name == QLatin1String("revision"))
+ fmm.setRevision(readIntBinding(script));
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
+
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (fmm.methodName().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Method or signal is missing a name script binding."));
+ return;
+ }
+
+ fmo->addMethod(fmm);
+}
+
+void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
+{
+ QString name;
+ QString type;
+ bool isPointer = false;
+ bool isReadonly = false;
+ bool isList = false;
+ int revision = 0;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name"))
+ name = readStringBinding(script);
+ else if (id == QLatin1String("type"))
+ type = readStringBinding(script);
+ else if (id == QLatin1String("isPointer"))
+ isPointer = readBoolBinding(script);
+ else if (id == QLatin1String("isReadonly"))
+ isReadonly = readBoolBinding(script);
+ else if (id == QLatin1String("isList"))
+ isList = readBoolBinding(script);
+ else if (id == QLatin1String("revision"))
+ revision = readIntBinding(script);
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only type, name, revision, isPointer, isReadonly and isList script bindings."));
+ }
+
+ if (name.isEmpty() || type.isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Property object is missing a name or type script binding."));
+ return;
+ }
+
+ fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision));
+}
+
+void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
+{
+ FakeMetaEnum fme;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name"))
+ fme.setName(readStringBinding(script));
+ else if (name == QLatin1String("values"))
+ readEnumValues(script, &fme);
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only name and values script bindings."));
+ }
+
+ fmo->addEnum(fme);
+}
+
+void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm)
+{
+ QString name;
+ QString type;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ const QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name")) {
+ name = readStringBinding(script);
+ } else if (id == QLatin1String("type")) {
+ type = readStringBinding(script);
+ } else if (id == QLatin1String("isPointer")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isReadonly")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isList")) {
+ // ### unhandled
+ } else {
+ addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
+ }
+ }
+
+ fmm->addParameter(name, type);
+}
+
+QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return QString());
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected string after colon."));
+ return QString();
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ StringLiteral *stringLit = AST::cast<StringLiteral *>(expStmt->expression);
+ if (!stringLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ return stringLit->value.toString();
+}
+
+bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return false);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected boolean after colon."));
+ return false;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
+ return false;
+ }
+
+ TrueLiteral *trueLit = AST::cast<TrueLiteral *>(expStmt->expression);
+ FalseLiteral *falseLit = AST::cast<FalseLiteral *>(expStmt->expression);
+ if (!trueLit && !falseLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
+ return false;
+ }
+
+ return trueLit;
+}
+
+double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return qQNaN());
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ return numericLit->value;
+}
+
+ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
+{
+ ComponentVersion invalidVersion;
+
+ if (!ast || !ast->statement) {
+ addError((ast ? ast->colonToken : SourceLocation()), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length));
+}
+
+int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
+{
+ double v = readNumericBinding(ast);
+ int i = static_cast<int>(v);
+
+ if (i != v) {
+ addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
+ return 0;
+ }
+
+ return i;
+}
+
+void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
+{
+ QTC_ASSERT(ast, return);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of strings after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ StringLiteral *stringLit = AST::cast<StringLiteral *>(it->element->initializer);
+ if (!stringLit) {
+ addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only string literal members."));
+ return;
+ }
+ QString exp = stringLit->value.toString();
+ int slashIdx = exp.indexOf(QLatin1Char('/'));
+ int spaceIdx = exp.indexOf(QLatin1Char(' '));
+ ComponentVersion version(exp.mid(spaceIdx + 1));
+
+ if (spaceIdx == -1 || !version.isValid()) {
+ addError(stringLit->firstSourceLocation(), tr("Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'."));
+ continue;
+ }
+ QString package;
+ if (slashIdx != -1)
+ package = exp.left(slashIdx);
+ QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
+
+ // ### relocatable exports where package is empty?
+ fmo->addExport(name, package, version);
+ }
+}
+
+void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
+{
+ QTC_ASSERT(ast, return);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ int exportIndex = 0;
+ const int exportCount = fmo->exports().size();
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
+ NumericLiteral *numberLit = cast<NumericLiteral *>(it->element->initializer);
+ if (!numberLit) {
+ addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only number literal members."));
+ return;
+ }
+
+ if (exportIndex >= exportCount) {
+ addError(numberLit->firstSourceLocation(), tr("Meta object revision without matching export."));
+ return;
+ }
+
+ const double v = numberLit->value;
+ const int metaObjectRevision = static_cast<int>(v);
+ if (metaObjectRevision != v) {
+ addError(numberLit->firstSourceLocation(), tr("Expected integer."));
+ return;
+ }
+
+ fmo->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
+ }
+}
+
+void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
+{
+ if (!ast)
+ return;
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected object literal after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
+ return;
+ }
+
+ ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
+ if (!objectLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
+ return;
+ }
+
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
+ if (assignement) {
+ StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
+ NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
+ UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
+ if (minus)
+ value = AST::cast<NumericLiteral *>(minus->expression);
+ if (!propName || !value) {
+ addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
+ continue;
+ }
+
+ double v = value->value;
+ if (minus)
+ v = -v;
+ fme->addKey(propName->id.toString(), v);
+ continue;
+ }
+ PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
+ if (getterSetter)
+ addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
+ }
+}
diff --git a/tools/qmllint/qmljstypedescriptionreader.h b/tools/qmllint/qmljstypedescriptionreader.h
new file mode 100644
index 0000000000..df215af8d2
--- /dev/null
+++ b/tools/qmllint/qmljstypedescriptionreader.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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 91ab2f8afc..76363a7cd8 100644
--- a/tools/qmllint/qmllint.pro
+++ b/tools/qmllint/qmllint.pro
@@ -2,8 +2,22 @@ option(host_build)
QT = core qmldevtools-private
-SOURCES += main.cpp
+SOURCES += main.cpp \
+ componentversion.cpp \
+ fakemetaobject.cpp \
+ findunqualified.cpp \
+ qmljstypedescriptionreader.cpp \
+ qcoloroutput.cpp \
+ scopetree.cpp
QMAKE_TARGET_DESCRIPTION = QML Syntax Verifier
load(qt_tool)
+
+HEADERS += \
+ componentversion.h \
+ fakemetaobject.h \
+ findunqualified.h \
+ qmljstypedescriptionreader.h \
+ qcoloroutput_p.h \
+ scopetree.h
diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp
new file mode 100644
index 0000000000..2eff3fa319
--- /dev/null
+++ b/tools/qmllint/scopetree.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "scopetree.h"
+
+#include "qcoloroutput_p.h"
+
+#include <algorithm>
+
+#include <QQueue>
+
+ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope)
+ : m_parentScope(parentScope), m_name(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};
+ m_childScopes.push_back(childScope);
+ return childScope;
+}
+
+ScopeTree *ScopeTree::parentScope() {
+ return m_parentScope;
+}
+
+void ScopeTree::insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope)
+{
+ Q_ASSERT(m_scopeType != ScopeType::QMLScope);
+ if (scope == QQmlJS::AST::VariableScope::Var) {
+ auto targetScope = this;
+ while (targetScope->scopeType() != ScopeType::JSFunctionScope) {
+ targetScope = targetScope->m_parentScope;
+ }
+ targetScope->m_currentScopeJSIdentifiers.insert(id);
+ } else {
+ m_currentScopeJSIdentifiers.insert(id);
+ }
+}
+
+void ScopeTree::insertQMLIdentifier(QString id)
+{
+ Q_ASSERT(m_scopeType == ScopeType::QMLScope);
+ m_currentScopeQMLIdentifiers.insert(id);
+}
+
+void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody)
+{
+ Q_ASSERT(m_scopeType == ScopeType::QMLScope);
+ m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
+}
+
+void ScopeTree::insertPropertyIdentifier(QString id)
+{
+ this->insertQMLIdentifier(id);
+ LanguageUtils::FakeMetaMethod method( id + QLatin1String("Changed"), "void");
+ this->addMethod(method);
+}
+
+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);
+ }
+}
+
+bool ScopeTree::isVisualRootScope() const
+{
+ return m_parentScope && m_parentScope->m_parentScope && m_parentScope->m_parentScope->m_parentScope == nullptr;
+}
+
+QString ScopeTree::name() const
+{
+ return m_name;
+}
+
+struct IssueLocationWithContext
+{
+ IssueLocationWithContext(const QString& code, QQmlJS::AST::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));
+ }
+
+ QStringRef beforeText;
+ QStringRef issueText;
+ QStringRef afterText;
+};
+
+bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> &qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput& colorOut) const
+{
+ bool noUnqualifiedIdentifier = true;
+
+ // revisit all scopes
+ 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))
+ continue;
+ if (currentScope->isIdInCurrentScope(idLocationPair.first)) {
+ 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);
+ // 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;
+ }
+ colorOut.write("Note: ", Info);
+ colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal );
+ colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal);
+ 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);
+ 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;
+ // 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);
+ });
+ 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 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("Consider using a function instead\n", Normal);
+ IssueLocationWithContext context {code, methodUsage.loc};
+ 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) {
+ 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);
+ }
+ }
+ return noUnqualifiedIdentifier;
+}
+
+QMap<QString, LanguageUtils::FakeMetaMethod>const &ScopeTree::methods() const
+{
+ return m_methods;
+}
+
+bool ScopeTree::isIdInCurrentQMlScopes(QString id) const
+{
+ auto qmlScope = getCurrentQMLScope();
+ return qmlScope->m_currentScopeQMLIdentifiers.contains(id);
+}
+
+bool ScopeTree::isIdInCurrentJSScopes(QString id) const
+{
+ auto jsScope = this;
+ while (jsScope) {
+ if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_currentScopeJSIdentifiers.contains(id))
+ return true;
+ jsScope = jsScope->m_parentScope;
+ }
+ return false;
+}
+
+bool ScopeTree::isIdInjectedFromSignal(QString id) const
+{
+ auto qmlScope = getCurrentQMLScope();
+ return qmlScope->m_injectedSignalIdentifiers.contains(id);
+}
+
+const ScopeTree *ScopeTree::getCurrentQMLScope() const
+{
+ auto qmlScope = this;
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
+ qmlScope = qmlScope->m_parentScope;
+ }
+ return qmlScope;
+}
+
+ScopeTree *ScopeTree::getCurrentQMLScope()
+{
+ auto qmlScope = this;
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
+ qmlScope = qmlScope->m_parentScope;
+ }
+ return qmlScope;
+}
+
+ScopeType ScopeTree::scopeType() {return m_scopeType;}
+
+void ScopeTree::addMethod(LanguageUtils::FakeMetaMethod method)
+{
+ m_methods.insert(method.methodName(), method);
+}
+
+void ScopeTree::addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject)
+{
+ if (metaObject) {
+ auto methodCount = metaObject->methodCount();
+ for (auto i = 0; i < methodCount; ++i) {
+ auto method = metaObject->method(i);
+ this->addMethod(method);
+ }
+ }
+}
diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h
new file mode 100644
index 0000000000..872a509123
--- /dev/null
+++ b/tools/qmllint/scopetree.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SCOPETREE_H
+#define SCOPETREE_H
+
+#include "fakemetaobject.h"
+#include "private/qqmljsast_p.h"
+#include "private/qqmljssourcelocation_p.h"
+
+#include <QSet>
+#include <QString>
+#include <QMap>
+
+enum MessageColors{
+ Error,
+ Warning,
+ Info,
+ Normal,
+ Hint
+};
+
+enum class ScopeType
+{
+ JSFunctionScope,
+ JSLexicalScope,
+ QMLScope
+};
+
+struct MethodUsage
+{
+ LanguageUtils::FakeMetaMethod method;
+ QQmlJS::AST::SourceLocation loc;
+ bool hasMultilineHandlerBody;
+};
+
+class ColorOutput;
+
+class ScopeTree {
+public:
+ ScopeTree(ScopeType type, QString name="<none given>", ScopeTree* parentScope=nullptr);
+ ~ScopeTree() {qDeleteAll(m_childScopes);}
+
+ ScopeTree* createNewChildScope(ScopeType type, QString name);
+ ScopeTree* parentScope();
+
+ 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
+
+ bool isIdInCurrentScope(QString const &id) const;
+ void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports);
+
+ bool isVisualRootScope() const;
+ QString name() const;
+
+ 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;
+
+private:
+ QSet<QString> m_currentScopeJSIdentifiers;
+ QSet<QString> m_currentScopeQMLIdentifiers;
+ QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers;
+ QMap<QString, LanguageUtils::FakeMetaMethod> m_methods;
+ QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers;
+ QVector<ScopeTree*> m_childScopes;
+ ScopeTree *m_parentScope;
+ QString m_name;
+ ScopeType m_scopeType;
+
+ bool isIdInCurrentQMlScopes(QString id) const;
+ bool isIdInCurrentJSScopes(QString id) const;
+ bool isIdInjectedFromSignal(QString id) const;
+ const ScopeTree* getCurrentQMLScope() const;
+ ScopeTree* getCurrentQMLScope();
+};
+#endif // SCOPETREE_H
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index 0a66aa419b..1556718471 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -100,21 +100,51 @@ static QString enquote(const QString &string)
.replace(QLatin1Char('"'),QLatin1String("\\\"")));
}
-void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, bool extended = false)
+struct QmlVersionInfo
{
+ QString pluginImportUri;
+ int majorVersion;
+ int minorVersion;
+ bool strict;
+};
+
+static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) {
+ if (versionInfo.strict) {
+ return (versionInfo.pluginImportUri == ty.module()
+ && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1))
+ || ty.module().isEmpty();
+ }
+ return ty.module().isEmpty()
+ || versionInfo.pluginImportUri == ty.module()
+ || ty.module().startsWith(versionInfo.pluginImportUri + QLatin1Char('.'));
+}
+
+void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false)
+{
+ auto ty = QQmlMetaType::qmlType(meta);
if (! meta || metas->contains(meta))
return;
- // dynamic meta objects can break things badly
- // but extended types are usually fine
- const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
- if (extended || !(mop->flags & DynamicMetaObject))
- metas->insert(meta);
+ if (matchingImportUri(ty, info)) {
+ if (!alreadyChangedModule) {
+ // dynamic meta objects can break things badly
+ // but extended types are usually fine
+ const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
+ if (extended || !(mop->flags & DynamicMetaObject))
+ metas->insert(meta);
+ } else if (!ty.module().isEmpty()) { // empty module (e.g. from an attached property) would cause a (false) match; do not warn about them
+ qWarning() << "Circular module dependency cannot be expressed in plugin.qmltypes file"
+ << "Object was:" << meta->className()
+ << ty.module() << info.pluginImportUri;
+ }
+ } else if (!ty.module().isEmpty()) {
+ alreadyChangedModule = true;
+ }
- collectReachableMetaObjects(meta->superClass(), metas);
+ collectReachableMetaObjects(meta->superClass(), metas, info, /*extended=*/ false, alreadyChangedModule);
}
-void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas)
+void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info)
{
if (! object)
return;
@@ -122,7 +152,7 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met
const QMetaObject *meta = object->metaObject();
if (verbose)
std::cerr << "Processing object " << qPrintable( meta->className() ) << std::endl;
- collectReachableMetaObjects(meta, metas);
+ collectReachableMetaObjects(meta, metas, info);
for (int index = 0; index < meta->propertyCount(); ++index) {
QMetaProperty prop = meta->property(index);
@@ -135,17 +165,18 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met
// accessing a member of oo is going to cause a segmentation fault
QObject *oo = QQmlMetaType::toQObject(prop.read(object));
if (oo && !metas->contains(oo->metaObject()))
- collectReachableMetaObjects(oo, metas);
+ collectReachableMetaObjects(oo, metas, info);
currentProperty.clear();
}
}
}
-void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas)
+void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas, const QmlVersionInfo& info)
{
- collectReachableMetaObjects(ty.baseMetaObject(), metas, ty.isExtendedType());
- if (ty.attachedPropertiesType(engine))
- collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas);
+ collectReachableMetaObjects(ty.baseMetaObject(), metas, info, ty.isExtendedType());
+ if (ty.attachedPropertiesType(engine) && matchingImportUri(ty, info)) {
+ collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas, info);
+ }
}
/* We want to add the MetaObject for 'Qt' to the list, this is a
@@ -205,14 +236,14 @@ QByteArray convertToId(const QMetaObject *mo)
// Collect all metaobjects for types registered with qmlRegisterType() without parameters
void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<const QMetaObject *>& metas,
- QMap<QString, QSet<QQmlType>> &compositeTypes) {
+ QMap<QString, QList<QQmlType>> &compositeTypes, const QmlVersionInfo &info) {
const auto qmlAllTypes = QQmlMetaType::qmlAllTypes();
for (const QQmlType &ty : qmlAllTypes) {
if (!metas.contains(ty.baseMetaObject())) {
if (!ty.isComposite()) {
- collectReachableMetaObjects(engine, ty, &metas);
- } else {
- compositeTypes[ty.elementName()].insert(ty);
+ collectReachableMetaObjects(engine, ty, &metas, info);
+ } else if (matchingImportUri(ty, info)) {
+ compositeTypes[ty.elementName()].append(ty);
}
}
}
@@ -221,23 +252,27 @@ void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<c
QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
QSet<const QMetaObject *> &noncreatables,
QSet<const QMetaObject *> &singletons,
- QMap<QString, QSet<QQmlType>> &compositeTypes,
- const QList<QQmlType> &skip = QList<QQmlType>())
+ QMap<QString, QList<QQmlType>> &compositeTypes,
+ const QmlVersionInfo &info,
+ const QList<QQmlType> &skip = QList<QQmlType>()
+ )
{
QSet<const QMetaObject *> metas;
metas.insert(FriendlyQObject::qtMeta());
const auto qmlTypes = QQmlMetaType::qmlTypes();
for (const QQmlType &ty : qmlTypes) {
+ if (!matchingImportUri(ty,info))
+ continue;
if (!ty.isCreatable())
noncreatables.insert(ty.baseMetaObject());
if (ty.isSingleton())
singletons.insert(ty.baseMetaObject());
if (!ty.isComposite()) {
qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty);
- collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas);
+ collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas, info);
} else {
- compositeTypes[ty.elementName()].insert(ty);
+ compositeTypes[ty.elementName()].append(ty);
}
}
@@ -245,6 +280,8 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
// find even more QMetaObjects by instantiating QML types and running
// over the instances
for (const QQmlType &ty : qmlTypes) {
+ if (!matchingImportUri(ty, info))
+ continue;
if (skip.contains(ty))
continue;
if (ty.isExtendedType())
@@ -270,13 +307,12 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
<< " is singleton, but has no singletonInstanceInfo" << std::endl;
continue;
}
- if (siinfo->qobjectCallback) {
+ if (ty.isQObjectSingleton()) {
if (verbose)
std::cerr << "Trying to get singleton for " << qPrintable(tyName)
<< " (" << qPrintable( siinfo->typeName ) << ")" << std::endl;
- siinfo->init(engine);
- collectReachableMetaObjects(object, &metas);
- object = siinfo->qobjectApi(engine);
+ collectReachableMetaObjects(object, &metas, info);
+ object = QQmlEnginePrivate::get(engine)->singletonInstance<QObject*>(ty);
} else {
inObjectInstantiation.clear();
continue; // we don't handle QJSValue singleton types.
@@ -294,7 +330,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
if (verbose)
std::cerr << "Got " << qPrintable( tyName )
<< " (" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")" << std::endl;
- collectReachableMetaObjects(object, &metas);
+ collectReachableMetaObjects(object, &metas, info);
object->deleteLater();
} else {
std::cerr << "Could not create " << qPrintable(tyName) << std::endl;
@@ -302,7 +338,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
}
}
- collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes);
+ collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes, info);
return metas;
}
@@ -344,23 +380,21 @@ public:
relocatableModuleUri = uri;
}
- const QString getExportString(QString qmlTyName, int majorVersion, int minorVersion)
+ QString getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
{
- if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
- qmlTyName.remove(0, relocatableModuleUri.size() + 1);
- }
- if (qmlTyName.startsWith("./")) {
- qmlTyName.remove(0, 2);
- }
- if (qmlTyName.startsWith(QLatin1Char('/'))) {
- qmlTyName.remove(0, 1);
- }
- const QString exportString = enquote(
- QString("%1 %2.%3").arg(
- qmlTyName,
- QString::number(majorVersion),
- QString::number(minorVersion)));
- return exportString;
+ const QString module = type.module().isEmpty() ? versionInfo.pluginImportUri
+ : type.module();
+ const int majorVersion = type.majorVersion() >= 0 ? type.majorVersion()
+ : versionInfo.majorVersion;
+ const int minorVersion = type.minorVersion() >= 0 ? type.minorVersion()
+ : versionInfo.minorVersion;
+
+ const QString versionedElement = type.elementName()
+ + QString::fromLatin1(" %1.%2").arg(majorVersion).arg(minorVersion);
+
+ return enquote((module == relocatableModuleUri)
+ ? versionedElement
+ : module + QLatin1Char('/') + versionedElement);
}
void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr)
@@ -405,11 +439,13 @@ public:
}
}
- QString getPrototypeNameForCompositeType(const QMetaObject *metaObject, QSet<QByteArray> &defaultReachableNames,
- QList<const QMetaObject *> *objectsToMerge)
+ QString getPrototypeNameForCompositeType(
+ const QMetaObject *metaObject, QList<const QMetaObject *> *objectsToMerge,
+ const QmlVersionInfo &versionInfo)
{
+ auto ty = QQmlMetaType::qmlType(metaObject);
QString prototypeName;
- if (!defaultReachableNames.contains(metaObject->className())) {
+ if (matchingImportUri(ty, versionInfo)) {
// dynamic meta objects can break things badly
// but extended types are usually fine
const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(metaObject->d.data);
@@ -417,24 +453,28 @@ public:
&& !objectsToMerge->contains(metaObject))
objectsToMerge->append(metaObject);
const QMetaObject *superMetaObject = metaObject->superClass();
- if (!superMetaObject)
+ if (!superMetaObject) {
prototypeName = "QObject";
- else
+ } else {
+ QQmlType superType = QQmlMetaType::qmlType(superMetaObject);
+ if (superType.isValid() && !superType.isComposite())
+ return convertToId(superMetaObject->className());
prototypeName = getPrototypeNameForCompositeType(
- superMetaObject, defaultReachableNames, objectsToMerge);
+ superMetaObject, objectsToMerge, versionInfo);
+ }
} else {
prototypeName = convertToId(metaObject->className());
}
return prototypeName;
}
- void dumpComposite(QQmlEngine *engine, const QSet<QQmlType> &compositeType, QSet<QByteArray> &defaultReachableNames)
+ void dumpComposite(QQmlEngine *engine, const QList<QQmlType> &compositeType, const QmlVersionInfo &versionInfo)
{
for (const QQmlType &type : compositeType)
- dumpCompositeItem(engine, type, defaultReachableNames);
+ dumpCompositeItem(engine, type, versionInfo);
}
- void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, QSet<QByteArray> &defaultReachableNames)
+ void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, const QmlVersionInfo &versionInfo)
{
QQmlComponent e(engine, compositeType.sourceUrl());
if (!e.isReady()) {
@@ -455,13 +495,17 @@ public:
QList<const QMetaObject *> objectsToMerge;
KnownAttributes knownAttributes;
// Get C++ base class name for the composite type
- QString prototypeName = getPrototypeNameForCompositeType(mainMeta, defaultReachableNames,
- &objectsToMerge);
+ QString prototypeName = getPrototypeNameForCompositeType(mainMeta, &objectsToMerge,
+ versionInfo);
qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName));
QString qmlTyName = compositeType.qmlTypeName();
- const QString exportString = getExportString(qmlTyName, compositeType.majorVersion(), compositeType.minorVersion());
+ const QString exportString = getExportString(compositeType, versionInfo);
+
+ // TODO: why don't we simply output the compositeType.elementName() here?
+ // That would make more sense, but it would change the format quite a bit.
qml->writeScriptBinding(QLatin1String("name"), exportString);
+
qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString);
qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList() << QString::number(compositeType.minorVersion()));
qml->writeBooleanBinding(QLatin1String("isComposite"), true);
@@ -528,7 +572,7 @@ public:
if (attachedType != meta)
attachedTypeId = convertToId(attachedType);
}
- const QString exportString = getExportString(type.qmlTypeName(), type.majorVersion(), type.minorVersion());
+ const QString exportString = getExportString(type, { QString(), -1, -1, false });
int metaObjectRevision = type.metaObjectRevision();
if (extendedObject) {
// emulate custom metaobjectrevision out of import
@@ -961,6 +1005,16 @@ void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg
// In case of QtFatalMsg the calling code will abort() when appropriate.
}
+QT_BEGIN_NAMESPACE
+static bool operator<(const QQmlType &a, const QQmlType &b)
+{
+ return a.qmlTypeName() < b.qmlTypeName()
+ || (a.qmlTypeName() == b.qmlTypeName()
+ && ((a.majorVersion() < b.majorVersion())
+ || (a.majorVersion() == b.majorVersion()
+ && a.minorVersion() < b.minorVersion())));
+}
+QT_END_NAMESPACE
int main(int argc, char *argv[])
{
@@ -1026,6 +1080,7 @@ int main(int argc, char *argv[])
QString dependenciesFile;
QString mergeFile;
bool forceQtQuickDependency = true;
+ bool strict = false;
enum Action { Uri, Path, Builtins };
Action action = Uri;
{
@@ -1090,6 +1145,10 @@ int main(int argc, char *argv[])
} else if (arg == QLatin1String("--qapp")
|| arg == QLatin1String("-qapp")) {
continue;
+ } else if (arg == QLatin1String("--strict")
+ || arg == QLatin1String("-strict")) {
+ strict = true;
+ continue;
} else {
std::cerr << "Invalid argument: " << qPrintable(arg) << std::endl;
return EXIT_INVALIDARGUMENTS;
@@ -1186,50 +1245,34 @@ int main(int argc, char *argv[])
// find all QMetaObjects reachable from the builtin module
QSet<const QMetaObject *> uncreatableMetas;
QSet<const QMetaObject *> singletonMetas;
- QMap<QString, QSet<QQmlType>> defaultCompositeTypes;
- QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes);
- QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes();
-
- // add some otherwise unreachable QMetaObjects
- defaultReachable.insert(&QQuickMouseEvent::staticMetaObject);
- // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported
- QSet<QByteArray> defaultReachableNames;
// this will hold the meta objects we want to dump information of
QSet<const QMetaObject *> metas;
// composite types we want to dump information of
- QMap<QString, QSet<QQmlType>> compositeTypes;
+ QMap<QString, QList<QQmlType>> compositeTypes;
+ int majorVersion = qtQmlMajorVersion, minorVersion = qtQmlMinorVersion;
+ QmlVersionInfo info;
if (action == Builtins) {
- for (const QMetaObject *m : qAsConst(defaultReachable)) {
- if (m->className() == QLatin1String("Qt")) {
- metas.insert(m);
- break;
- }
- }
- } else if (pluginImportUri == QLatin1String("QtQml")) {
- bool ok = false;
- const uint major = pluginImportVersion.splitRef('.').at(0).toUInt(&ok, 10);
- if (!ok) {
- std::cerr << "Malformed version string \""<< qPrintable(pluginImportVersion) << "\"."
- << std::endl;
- return EXIT_INVALIDARGUMENTS;
- }
- if (major != qtQmlMajorVersion) {
- std::cerr << "Unsupported version \"" << qPrintable(pluginImportVersion)
- << "\": Major version number must be \"" << qtQmlMajorVersion << "\"."
- << std::endl;
- return EXIT_INVALIDARGUMENTS;
- }
- metas = defaultReachable;
- for (const QMetaObject *m : qAsConst(defaultReachable)) {
- if (m->className() == QLatin1String("Qt")) {
- metas.remove(m);
- break;
- }
- }
+ QMap<QString, QList<QQmlType>> defaultCompositeTypes;
+ QSet<const QMetaObject *> builtins = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes, {QLatin1String("Qt"), majorVersion, minorVersion, strict});
+ Q_ASSERT(builtins.size() == 1);
+ metas.insert(*builtins.begin());
} else {
+ auto versionSplitted = pluginImportVersion.split(".");
+ bool ok = versionSplitted.size() == 2;
+ if (!ok)
+ qCritical("Invalid version number");
+ else {
+ majorVersion = versionSplitted.at(0).toInt(&ok);
+ if (!ok)
+ qCritical("Invalid major version");
+ minorVersion = versionSplitted.at(1).toInt(&ok);
+ if (!ok)
+ qCritical("Invalid minor version");
+ }
+ QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes();
// find a valid QtQuick import
QByteArray importCode;
QQmlType qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject);
@@ -1273,25 +1316,16 @@ int main(int argc, char *argv[])
return EXIT_IMPORTERROR;
}
}
+ info = {pluginImportUri, majorVersion, minorVersion, strict};
+ QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, info, defaultTypes);
- QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, defaultTypes);
- candidates.subtract(defaultReachable);
-
- for (QString iter: compositeTypes.keys()) {
- if (defaultCompositeTypes.contains(iter)) {
- QSet<QQmlType> compositeTypesByName = compositeTypes.value(iter);
- compositeTypesByName.subtract(defaultCompositeTypes.value(iter));
- compositeTypes[iter] = compositeTypesByName;
- }
+ for (auto it = compositeTypes.begin(), end = compositeTypes.end(); it != end; ++it) {
+ std::sort(it->begin(), it->end());
+ it->erase(std::unique(it->begin(), it->end()), it->end());
}
- // Also eliminate meta objects with the same classname.
- // This is required because extended objects seem not to share
- // a single meta object instance.
- for (const QMetaObject *mo : qAsConst(defaultReachable))
- defaultReachableNames.insert(QByteArray(mo->className()));
for (const QMetaObject *mo : qAsConst(candidates)) {
- if (!defaultReachableNames.contains(mo->className()))
+ if (mo->className() != QLatin1String("Qt"))
metas.insert(mo);
}
}
@@ -1338,9 +1372,9 @@ int main(int argc, char *argv[])
dumper.dump(QQmlEnginePrivate::get(&engine), meta, uncreatableMetas.contains(meta), singletonMetas.contains(meta));
}
- QMap<QString, QSet<QQmlType> >::const_iterator iter = compositeTypes.constBegin();
+ QMap<QString, QList<QQmlType>>::const_iterator iter = compositeTypes.constBegin();
for (; iter != compositeTypes.constEnd(); ++iter)
- dumper.dumpComposite(&engine, iter.value(), defaultReachableNames);
+ dumper.dumpComposite(&engine, iter.value(), info);
// define QEasingCurve as an extension of QQmlEasingValueType, this way
// properties using the QEasingCurve type get useful type information.
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index f92ffa9ff5..7b010546c3 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -302,7 +302,7 @@ void QmlProfilerApplication::flush()
{
if (m_recording) {
m_pendingRequest = REQUEST_FLUSH;
- m_qmlProfilerClient->sendRecordingStatus(false);
+ m_qmlProfilerClient->setRecording(false);
} else {
if (m_profilerData->save(m_interactiveOutputFile)) {
m_profilerData->clear();
@@ -392,7 +392,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
if (cmd == Constants::CMD_RECORD || cmd == Constants::CMD_RECORD2) {
m_pendingRequest = REQUEST_TOGGLE_RECORDING;
- m_qmlProfilerClient->sendRecordingStatus(!m_recording);
+ m_qmlProfilerClient->setRecording(!m_recording);
} else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) {
m_pendingRequest = REQUEST_QUIT;
if (m_recording) {
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index d5662a0182..9ec143975e 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -101,7 +101,6 @@ QmlProfilerData::~QmlProfilerData()
void QmlProfilerData::clear()
{
- d->eventTypes.clear();
d->events.clear();
d->traceEndTime = std::numeric_limits<qint64>::min();
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index af50acaa5a..260c5bb7d1 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -29,7 +29,7 @@
#include <QtCore/qabstractanimation.h>
#include <QtCore/qdir.h>
#include <QtCore/qmath.h>
-#include <QtCore/qdatetime.h>
+#include <QtCore/qelapsedtimer.h>
#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qtextstream.h>
@@ -76,7 +76,7 @@ QVector<int> RenderStatistics::timesPerFrames;
void RenderStatistics::updateStats()
{
- static QTime time;
+ static QElapsedTimer time;
static int frames;
static int lastTime;
@@ -169,10 +169,13 @@ struct Options
bool multisample = false;
bool coreProfile = false;
bool verbose = false;
+ bool rhi = false;
+ bool rhiBackendSet = false;
QVector<Qt::ApplicationAttribute> applicationAttributes;
QString translationFile;
QmlApplicationType applicationType = DefaultQmlApplicationType;
QQuickWindow::TextRenderType textRenderType;
+ QString rhiBackend;
};
#if defined(QMLSCENE_BUNDLE)
@@ -271,6 +274,10 @@ static bool checkVersion(const QUrl &url)
QTextStream stream(&f);
bool codeFound= false;
while (!codeFound) {
+ if (stream.atEnd()) {
+ fprintf(stderr, "qmlscene: no code found in file '%s'.\n", qPrintable(fileName));
+ return false;
+ }
QString line = stream.readLine();
if (line.contains(QLatin1Char('{'))) {
codeFound = true;
@@ -345,24 +352,26 @@ static void usage()
puts(" --transparent .................... Make the window transparent");
puts(" --multisample .................... Enable multisampling (OpenGL anti-aliasing)");
puts(" --core-profile ................... Request a core profile OpenGL context");
+ puts(" --rhi [vulkan|metal|d3d11|gl] .... Use the Qt graphics abstraction (RHI) instead of OpenGL directly.\n"
+ " .... Backend has platform specific defaults. Specify to override.");
puts(" --no-version-detection ........... Do not try to detect the version of the .qml file");
puts(" --slow-animations ................ Run all animations in slow motion");
puts(" --resize-to-root ................. Resize the window to the size of the root item");
puts(" --quit ........................... Quit immediately after starting");
puts(" --disable-context-sharing ........ Disable the use of a shared GL context for QtQuick Windows\n"
- " .........(remove AA_ShareOpenGLContexts)");
- puts(" --desktop..........................Force use of desktop GL (AA_UseDesktopOpenGL)");
- puts(" --gles.............................Force use of GLES (AA_UseOpenGLES)");
- puts(" --software.........................Force use of software rendering (AA_UseOpenGLES)");
- puts(" --scaling..........................Enable High DPI scaling (AA_EnableHighDpiScaling)");
- puts(" --no-scaling.......................Disable High DPI scaling (AA_DisableHighDpiScaling)");
- puts(" --verbose..........................Print version and graphical diagnostics for the run-time");
+ " ........ (remove AA_ShareOpenGLContexts)");
+ puts(" --desktop......................... Force use of desktop GL (AA_UseDesktopOpenGL)");
+ puts(" --gles............................ Force use of GLES (AA_UseOpenGLES)");
+ puts(" --software........................ Force use of software rendering (AA_UseOpenGLES)");
+ puts(" --scaling......................... Enable High DPI scaling (AA_EnableHighDpiScaling)");
+ puts(" --no-scaling...................... Disable High DPI scaling (AA_DisableHighDpiScaling)");
+ puts(" --verbose......................... Print version and graphical diagnostics for the run-time");
#ifdef QT_WIDGETS_LIB
- puts(" --apptype [gui|widgets] ...........Select which application class to use. Default is widgets.");
+ puts(" --apptype [gui|widgets] .......... Select which application class to use. Default is widgets.");
#endif
- puts(" --textrendertype [qt|native].......Select the default render type for text-like elements.");
+ puts(" --textrendertype [qt|native]...... Select the default render type for text-like elements.");
puts(" -I <path> ........................ Add <path> to the list of import paths");
- puts(" -S <selector> .....................Add <selector> to the list of QQmlFileSelector selectors");
+ puts(" -S <selector> .................... Add <selector> to the list of QQmlFileSelector selectors");
puts(" -P <path> ........................ Add <path> to the list of plugin paths");
puts(" -translation <translationfile> ... Set the language to run in");
@@ -546,7 +555,13 @@ int main(int argc, char ** argv)
options.resizeViewToRootItem = true;
else if (lowerArgument == QLatin1String("--verbose"))
options.verbose = true;
- else if (lowerArgument == QLatin1String("-i") && i + 1 < size)
+ else if (lowerArgument == QLatin1String("--rhi")) {
+ options.rhi = true;
+ if (i + 1 < size && !arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
+ options.rhiBackendSet = true;
+ options.rhiBackend = arguments.at(++i);
+ }
+ } else if (lowerArgument == QLatin1String("-i") && i + 1 < size)
imports.append(arguments.at(++i));
else if (lowerArgument == QLatin1String("-s") && i + 1 < size)
customSelectors.append(arguments.at(++i));
@@ -588,6 +603,14 @@ int main(int argc, char ** argv)
QUnifiedTimer::instance()->setSlowModeEnabled(options.slowAnimations);
+ if (options.rhi) {
+ qputenv("QSG_RHI", "1");
+ if (options.rhiBackendSet)
+ qputenv("QSG_RHI_BACKEND", options.rhiBackend.toLatin1());
+ else
+ qunsetenv("QSG_RHI_BACKEND");
+ }
+
if (options.url.isEmpty())
#if defined(QMLSCENE_BUNDLE)
displayOptionsDialog(&options);
@@ -692,6 +715,8 @@ int main(int argc, char ** argv)
// QQuickView if one was created. That case is tracked by
// QPointer, so it is safe to delete the component here.
delete component;
+ } else {
+ exitCode = 1;
}
}
diff --git a/tools/qmltime/qmltime.cpp b/tools/qmltime/qmltime.cpp
index b897d304fc..7baedff611 100644
--- a/tools/qmltime/qmltime.cpp
+++ b/tools/qmltime/qmltime.cpp
@@ -29,7 +29,7 @@
#include <QQmlComponent>
#include <QDebug>
#include <QGuiApplication>
-#include <QTime>
+#include <QElapsedTimer>
#include <QQmlContext>
#include <QQuickView>
#include <QQuickItem>
@@ -119,7 +119,7 @@ void Timer::setWillParent(bool p)
void Timer::runTest(QQmlContext *context, uint iterations)
{
- QTime t;
+ QElapsedTimer t;
t.start();
for (uint ii = 0; ii < iterations; ++ii) {
QObject *o = m_component->create(context);
diff --git a/tools/qmlcachegen/resourcefilemapper.cpp b/tools/shared/resourcefilemapper.cpp
index 6a00b39f2e..b9cf463575 100644
--- a/tools/qmlcachegen/resourcefilemapper.cpp
+++ b/tools/shared/resourcefilemapper.cpp
@@ -50,17 +50,15 @@ bool ResourceFileMapper::isEmpty() const
QStringList ResourceFileMapper::resourcePaths(const QString &fileName)
{
const QString absPath = QDir::cleanPath(QDir::current().absoluteFilePath(fileName));
- QHashIterator<QString, QString> it(qrcPathToFileSystemPath);
QStringList resourcePaths;
- while (it.hasNext()) {
- it.next();
+ for (auto it = qrcPathToFileSystemPath.cbegin(), end = qrcPathToFileSystemPath.cend(); it != end; ++it) {
if (QFileInfo(it.value()) == QFileInfo(absPath))
resourcePaths.append(it.key());
}
return resourcePaths;
}
-QStringList ResourceFileMapper::qmlCompilerFiles() const
+QStringList ResourceFileMapper::qmlCompilerFiles(FileOutput fo) const
{
QStringList files;
for (auto it = qrcPathToFileSystemPath.constBegin(), end = qrcPathToFileSystemPath.constEnd();
@@ -69,7 +67,10 @@ QStringList ResourceFileMapper::qmlCompilerFiles() const
const QString suffix = QFileInfo(qrcPath).suffix();
if (suffix != QStringLiteral("qml") && suffix != QStringLiteral("js") && suffix != QStringLiteral("mjs"))
continue;
- files << qrcPath;
+ if (fo == FileOutput::AbsoluteFilePath)
+ files << it.value();
+ else
+ files << qrcPath;
}
return files;
}
diff --git a/tools/qmlcachegen/resourcefilemapper.h b/tools/shared/resourcefilemapper.h
index 2e0ab45171..ed3e486149 100644
--- a/tools/qmlcachegen/resourcefilemapper.h
+++ b/tools/shared/resourcefilemapper.h
@@ -34,12 +34,16 @@
struct ResourceFileMapper
{
+ enum class FileOutput {
+ RelativeFilePath,
+ AbsoluteFilePath
+ };
ResourceFileMapper(const QStringList &resourceFiles);
bool isEmpty() const;
QStringList resourcePaths(const QString &fileName);
- QStringList qmlCompilerFiles() const;
+ QStringList qmlCompilerFiles(FileOutput fo = FileOutput::RelativeFilePath) const;
private:
void populateFromQrcFile(QFile &file);
diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri
new file mode 100644
index 0000000000..c094b51d5f
--- /dev/null
+++ b/tools/shared/shared.pri
@@ -0,0 +1,3 @@
+INCLUDEPATH += $$PWD
+SOURCES += $$PWD/resourcefilemapper.cpp
+HEADERS += $$PWD/resourcefilemapper.h
diff --git a/tools/tools.pro b/tools/tools.pro
index 73cb6e2293..25ed760903 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -10,7 +10,7 @@ qtConfig(qml-devtools) {
qtConfig(commandlineparser):qtConfig(xmlstreamwriter): SUBDIRS += qmlcachegen
}
-qtConfig(thread):!android|android_app:!wasm {
+qtConfig(thread):!android|android_app:!wasm:!rtems {
SUBDIRS += \
qml